如何将带有字符串的混合时间格式转换为军事时间?
How to convert mixed time formats with strings to military time?
我有一个变量需要转换成军用时间。这个变量很乱,因为它在格式上缺乏一致性。
下面是变量中可能存在的内容的示例。
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found", "0.125", "Found 1525")
到目前为止,我已经取得了一些成功,使所有内容的格式与 RegEx 更加一致:
x <- str_extract(x, "[0-9]+.+[0-9]|[0-9][0-9][:][0-9][0-9]")
x <- str_remove(x, "[:]")
x <- str_remove(x, "[:][0-9][0-9]$")
如您所见,我得到:“0.9305555555555547”、“1520”、“1000”、“0.125”、“1525”
问题是小数点需要乘以 2400 才能回到军事时间,但我也不想乘以整数(因为那些已经在军事时间)。
x
本质上是数据帧中的一个变量。
我正在考虑使用 if/else 逻辑,但我不知道如何实现它。
为了澄清,我想要:
输入:“0.9305555555555547”、“15:20 找到”、“10:00:00 AM 找到”、“0.125”、“找到 15:25”
输出:“2233”、“1520”、“1000”、“0300”、“1525”
在使用正则表达式完成 pre-processing 之后,您可以在此处使用 str_detect()
实现 if/else 逻辑
x <- ifelse(str_detect(x, "\."),
as.integer(as.numeric(x) * 2400),
as.integer(x)) %>%
sprintf("%04d", .)
这将return你想要的输出字符
然后你可以像这样将它解析为 POSIXct
x <- as.POSIXct(x,
format = "%H%M",
origin = "1970-01-01",
tz = "UTC")
我从字面上按照你的逻辑得到了完全相同的结果。
- 数值转换
- 乘以 2400 并返回字符
- for 循环检测字符中的点并在之后删除
- for 循环将 0 放在少于 4 个字符的数字前面
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found", "0.125", "Found 1525")
x <- str_extract(x, "[0-9]+.+[0-9]|[0-9][0-9][:][0-9][0-9]")
x <- str_remove(x, "[:]")
x <- str_remove(x, "[:][0-9][0-9]$")
x <- as.numeric(x)
x <- as.character(ifelse(x<1,x*2400,x))
for(i in 1:length(x)){
ii <- stri_locate_first_regex(x[i],"\.")[1]
if(!is.na(ii)){
x[i] <- str_sub(x[i],1,ii-1)
}
}
for(i in 1:length(x)){
while (nchar(x[i])<4) {
x[i] <- paste0("0",x[i])
}
}
x
[1] "2233" "1520" "1000" "0300" "1525"
>
编辑: 我意识到了这一点:
the decimals need to be multiplied by 2400 to get back to military time
不太对。 “2400”不是十进制数(技术上它是六十进制,以 60 为底),因此十进制乘法不会给出正确的结果。我已经相应地更改了我的代码。
我不会在所有内容上都使用相同的正则表达式,而是首先确定 x
、 的每个元素的格式,然后 相应地处理该元素。
我喜欢 hms
库用于处理时间并在下面使用它,但您也可以使用基础 POSIXct
或 POSIXlt
class.
library(tidyverse)
library(hms)
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found", "0.125", "Found 1525")
# ----
# define functions for parsing each possible format in `x`
parse_decimal <- function(x) {
hrs <- as.numeric(str_extract(x, "^0\.\d+")) * 24
min <- (hrs %% 1) * 60
hrs <- floor(hrs)
hms(hours = hrs, minutes = min)
}
parse_timestring <- function(x) {
out <- str_remove_all(x, "\D")
hr_digits <- if_else(str_length(out) == 3, 1, 2)
hrs <- as.numeric(str_sub(out, end = hr_digits))
hrs <- if_else(str_detect(str_to_upper(x), "P\.?M"), hrs + 12, hrs)
min <- as.numeric(str_sub(out, start = hr_digits + 1))
hms(hours = hrs, minutes = min)
}
# ----
# test each element of x, and pass to appropriate parsing Fx
time <- case_when(
str_detect(x, "^[01]$|^0\.\d") ~ parse_decimal(x),
str_detect(x, "\d{1,2}:?\d{2}") ~ parse_timestring(x),
TRUE ~ NA_real_
)
time
# 22:20:00.000000
# 15:20:00.000000
# 10:00:00.000000
# 03:00:00.000000
# 15:25:00.000000
我们应该扩展 AM/PM 指标的正则表达式,这样力量就不会错过彼此。接下来,在子集中我们处理十进制时间、英制时间、24 小时制时间和 return
结果。
milt <- function(x) {
u <- trimws(gsub('\D*(\d*\W?[AP]?M?)\D*', '\1', x))
u[grep('\.', u)] <- sprintf('%04d', round(as.double(u[grep('\.', u)])*2400))
u[grep('[AP]M', u)] <- strftime(strptime(u[grep('[AP]M', u)], '%I:%M:%S %p'), '%H%M')
u[grep(':', u)] <- gsub(':', '', u[grep(':', u)] )
return(u)
}
milt(x)
# [1] "2233" "1520" "1000" "2200" "0300" "1525" "0000" "1020"
数据:
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found",
"10:00:00 PM Found", "0.125", "Found 1525", "0000", "10:20")
我有一个变量需要转换成军用时间。这个变量很乱,因为它在格式上缺乏一致性。
下面是变量中可能存在的内容的示例。
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found", "0.125", "Found 1525")
到目前为止,我已经取得了一些成功,使所有内容的格式与 RegEx 更加一致:
x <- str_extract(x, "[0-9]+.+[0-9]|[0-9][0-9][:][0-9][0-9]")
x <- str_remove(x, "[:]")
x <- str_remove(x, "[:][0-9][0-9]$")
如您所见,我得到:“0.9305555555555547”、“1520”、“1000”、“0.125”、“1525”
问题是小数点需要乘以 2400 才能回到军事时间,但我也不想乘以整数(因为那些已经在军事时间)。
x
本质上是数据帧中的一个变量。
我正在考虑使用 if/else 逻辑,但我不知道如何实现它。
为了澄清,我想要:
输入:“0.9305555555555547”、“15:20 找到”、“10:00:00 AM 找到”、“0.125”、“找到 15:25”
输出:“2233”、“1520”、“1000”、“0300”、“1525”
在使用正则表达式完成 pre-processing 之后,您可以在此处使用 str_detect()
x <- ifelse(str_detect(x, "\."),
as.integer(as.numeric(x) * 2400),
as.integer(x)) %>%
sprintf("%04d", .)
这将return你想要的输出字符
然后你可以像这样将它解析为 POSIXct
x <- as.POSIXct(x,
format = "%H%M",
origin = "1970-01-01",
tz = "UTC")
我从字面上按照你的逻辑得到了完全相同的结果。
- 数值转换
- 乘以 2400 并返回字符
- for 循环检测字符中的点并在之后删除
- for 循环将 0 放在少于 4 个字符的数字前面
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found", "0.125", "Found 1525")
x <- str_extract(x, "[0-9]+.+[0-9]|[0-9][0-9][:][0-9][0-9]")
x <- str_remove(x, "[:]")
x <- str_remove(x, "[:][0-9][0-9]$")
x <- as.numeric(x)
x <- as.character(ifelse(x<1,x*2400,x))
for(i in 1:length(x)){
ii <- stri_locate_first_regex(x[i],"\.")[1]
if(!is.na(ii)){
x[i] <- str_sub(x[i],1,ii-1)
}
}
for(i in 1:length(x)){
while (nchar(x[i])<4) {
x[i] <- paste0("0",x[i])
}
}
x
[1] "2233" "1520" "1000" "0300" "1525"
>
编辑: 我意识到了这一点:
the decimals need to be multiplied by 2400 to get back to military time
不太对。 “2400”不是十进制数(技术上它是六十进制,以 60 为底),因此十进制乘法不会给出正确的结果。我已经相应地更改了我的代码。
我不会在所有内容上都使用相同的正则表达式,而是首先确定 x
、 的每个元素的格式,然后 相应地处理该元素。
我喜欢 hms
库用于处理时间并在下面使用它,但您也可以使用基础 POSIXct
或 POSIXlt
class.
library(tidyverse)
library(hms)
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found", "0.125", "Found 1525")
# ----
# define functions for parsing each possible format in `x`
parse_decimal <- function(x) {
hrs <- as.numeric(str_extract(x, "^0\.\d+")) * 24
min <- (hrs %% 1) * 60
hrs <- floor(hrs)
hms(hours = hrs, minutes = min)
}
parse_timestring <- function(x) {
out <- str_remove_all(x, "\D")
hr_digits <- if_else(str_length(out) == 3, 1, 2)
hrs <- as.numeric(str_sub(out, end = hr_digits))
hrs <- if_else(str_detect(str_to_upper(x), "P\.?M"), hrs + 12, hrs)
min <- as.numeric(str_sub(out, start = hr_digits + 1))
hms(hours = hrs, minutes = min)
}
# ----
# test each element of x, and pass to appropriate parsing Fx
time <- case_when(
str_detect(x, "^[01]$|^0\.\d") ~ parse_decimal(x),
str_detect(x, "\d{1,2}:?\d{2}") ~ parse_timestring(x),
TRUE ~ NA_real_
)
time
# 22:20:00.000000
# 15:20:00.000000
# 10:00:00.000000
# 03:00:00.000000
# 15:25:00.000000
我们应该扩展 AM/PM 指标的正则表达式,这样力量就不会错过彼此。接下来,在子集中我们处理十进制时间、英制时间、24 小时制时间和 return
结果。
milt <- function(x) {
u <- trimws(gsub('\D*(\d*\W?[AP]?M?)\D*', '\1', x))
u[grep('\.', u)] <- sprintf('%04d', round(as.double(u[grep('\.', u)])*2400))
u[grep('[AP]M', u)] <- strftime(strptime(u[grep('[AP]M', u)], '%I:%M:%S %p'), '%H%M')
u[grep(':', u)] <- gsub(':', '', u[grep(':', u)] )
return(u)
}
milt(x)
# [1] "2233" "1520" "1000" "2200" "0300" "1525" "0000" "1020"
数据:
x <- c("0.9305555555555547", "15:20 Found", "10:00:00 AM Found",
"10:00:00 PM Found", "0.125", "Found 1525", "0000", "10:20")