r - 如何在大数据帧中快速转换不同的时间格式?
问题描述
我想计算不同时间维度的长度,但我在处理数据框列中两种略有不同的时间格式时遇到了问题。
原始数据框列有大约一百万行,两种格式(如示例代码所示)混合在一起。
示例代码:
time <- c("2018-07-29T15:02:05Z", "2018-07-29T14:46:57Z",
"2018-10-04T12:13:41.333Z", "2018-10-04T12:13:45.479Z")
length <- c(15.8, 132.1, 12.5, 33.2)
df <- data.frame(time, length)
df$time <- format(as.POSIXlt(strptime(df$time,"%Y-%m-%dT%H:%M:%SZ", tz="")))
df
格式"2018-10-04T12:13:41.333Z"
和"2018-10-04T12:13:45.479Z"
结果NA
。
是否有一种解决方案也适用于两种格式混合的大数据框架?
解决方案
我们可以使用%OS
而不%S
是以秒为单位来计算小数。
help("strptime")
特定于 R 是
%OSn
,对于输出,它给出的秒数被截断为 0 <= n <= 6 位小数(如果 %OS 后面没有数字,它使用 getOption("digits.secs") 的设置,或者如果即未设置,n = 0)。
as.POSIXct(time, format="%Y-%m-%dT%H:%M:%OSZ")
# [1] "2018-07-29 15:02:05 CEST" "2018-07-29 14:46:57 CEST"
# [3] "2018-10-04 12:13:41 CEST" "2018-10-04 12:13:45 CEST"
这个基本的 R 代码比包解决方案快得多,你自己试试吧。
更新 1
time2 <- c("2018-09-01T12:42:37.000+02:00", "2018-10-01T11:42:37.000+03:00")
这个比较棘手。?strptime
说我们应该使用%z
UTC 的偏移量,但不知何故它不适用于as.POSIXct
. 相反,我们可以这样做,
as.POSIXct(substr(time2, 1, 23), format="%Y-%m-%dT%H:%M:%OS") +
{os <- as.numeric(el(strsplit(substring(time2, 24), "\\:")))
(os[1]*60 + os[2])*60}
# [1] "2018-09-01 14:42:37 CEST" "2018-10-01 13:42:37 CEST"
它从字符串中删除不可读的部分,将其转换为秒并将其添加到"POSIXct"
对象中。
如果只有几个小时time2
,我们也可以说:
as.POSIXct(substr(time2, 1, 23), format="%Y-%m-%dT%H:%M:%OS") +
as.numeric(substr(time2, 24, 26))*3600
# [1] "2018-09-01 14:42:37 CEST" "2018-10-01 13:42:37 CEST"
代码现在稍长一点不应掩盖这样一个事实,即它的运行速度几乎与答案顶部的代码一样快。
更新 2
您可以将当前的三个变体包装成一个具有if (nchar(x) == 29) ... else
结构的函数,例如这个:
fixDateTime <- function(x) {
s <- split(x, nchar(x))
if ("20" %in% names(s))
s$`20` <- as.POSIXct(s$`20` , format="%Y-%m-%dT%H:%M:%SZ")
else if ("24" %in% names(s))
s$`24` <- as.POSIXct(s$`24`, format="%Y-%m-%dT%H:%M:%OSZ")
else if ("29" %in% names(s))
s$`29` <- as.POSIXct(substr(s$`29`, 1, 23), format="%Y-%m-%dT%H:%M:%OS") +
{os <- as.numeric(el(strsplit(substring(s[[3]], 24), "\\:")))
(os[1]*60 + os[2])*60}
return(unsplit(s, nchar(x)))
}
res <- fixDateTime(time3)
res
# [1] "2018-07-29 15:02:05 CEST" "2018-10-04 00:00:00 CEST" "2018-10-01 00:00:00 CEST"
str(res)
# POSIXct[1:3], format: "2018-07-29 15:02:05" "2018-10-04 00:00:00" "2018-10-01 00:00:00"
与包相比,只能fixDateTime
处理所有三种定义的日期时间类型。根据结论性基准,该功能仍然非常快。
注意:如果不同的日期格式相同nchar
,则该函数在逻辑上会失败,并且应该在案例中自定义(例如通过其他split
条件)!未测试:将秒数添加到POSIXct
.
基准
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# fixDateTime 35.46387 35.94761 40.07578 36.05923 39.54706 68.46211 10 c
# as.POSIXct 20.32820 20.45985 21.00461 20.62237 21.16019 23.56434 10 b # to compare
# lubridate 11.59311 11.68956 12.88880 12.01077 13.76151 16.54479 10 a # produces NAs!
# anytime 198.57292 201.06483 203.95131 202.91368 203.62130 212.83272 10 d # produces NAs!
数据
time <- c("2018-07-29T15:02:05Z", "2018-07-29T14:46:57Z", "2018-10-04T12:13:41.333Z",
"2018-10-04T12:13:45.479Z")
time2 <- c("2018-07-29T15:02:05Z", "2018-07-29T15:02:05Z", "2018-07-29T15:02:05Z")
time3 <- c("2018-07-29T15:02:05Z", "2018-10-04T12:13:41.333Z",
"2018-10-01T11:42:37.000+03:00")
基准代码
n <- 1e3
t1 <- sample(time2, n, replace=TRUE)
t2 <- sample(time3, n, replace=TRUE)
library(lubridate)
library(anytime)
microbenchmark::microbenchmark(fixDateTime=fixDateTime(t2),
as.POSIXct=as.POSIXct(t1, format="%Y-%m-%dT%H:%M:%OSZ"),
lubridate=parse_date_time(t2, "ymd_HMS"),
anytime=anytime(t2),
times=10L)
推荐阅读
- android - 我已经创建了通知通道,但在 Android 8.1.0 上崩溃的可能性仍然很低 startForeground 的错误通知
- android - 当我阅读条形码时,Listview 项目数据重复
- google-cloud-platform - 在获得谷歌云计算引擎的管理员访问权限后,如何创建项目和 VM 实例?
- javascript - 我想制作一个函数,在 php 中按函数打印插入查询。问题 :
- mysql - 1 小时内的 MySQL 查询选择
- stackdriver - Stackdriver 保留政策为 30 天。Stackdriver 信息中心如何设法显示过去 6 周的数据?
- android - 我可以使用整个原生项目与原生 UI 进行原生反应吗?
- cmake - cmake3外部项目添加步骤循环
- reactjs - 如何正确定义引用(React.RefObject
) 用于样式化组件(用于 TypeScript)? - android - Android 从相机捕获图像返回空数据