将所有日期的字符串日期快速转换为 R 日期

问题描述 投票:0回答:6

这个问题已经被问过好几次了,但没有明确的答案:我想将“YYYY-mm-dd”形式的 R 字符串转换为

Date
as.Date
功能非常慢。 R 中的将字符*快速*转换为日期提供了一个使用 fasttime
 的解决方案,适用于 1970 年以后的日期。我的问题是我需要转换从 1900 年开始的日期,其中大约有 1 亿个。我必须经常这样做,所以速度很重要。还有其他解决办法吗?

r date posix
6个回答
10
投票
我可以通过使用

date

 包获得一点加速:

library(date) set.seed(21) x <- as.character(Sys.Date()-sample(40000, 1e6, TRUE)) system.time(dDate <- as.Date(x)) # user system elapsed # 6.54 0.01 6.56 system.time(ddate <- as.Date(as.date(x,"ymd"))) # user system elapsed # 3.42 0.22 3.64

您可能想查看它使用的 C 代码,看看是否可以修改它以更快地适应您的具体情况。


10
投票
我不久前遇到了类似的问题,并提出了以下解决方案:

    将字符串转换为
  1. 因子(如果还不是因子)
  2. 将因子的级别转换为日期
  3. 使用因子的索引向量将转换后的级别扩展为解决方案 扩展 Joshua Ulrich 的示例,我得到(我的笔记本电脑上的计时速度较慢)
library(date) set.seed(21) x <- as.character(Sys.Date()-sample(40000, 1e6, TRUE)) system.time(dDate <- as.Date(x)) # user system elapsed # 12.09 0.00 12.12 system.time(ddate <- as.Date(as.date(x,"ymd"))) # user system elapsed # 6.97 0.04 7.05 system.time({ xf <- as.factor(x) dDate <- as.Date(levels(xf))[as.integer(xf)] }) # user system elapsed # 1.16 0.00 1.15

在这里,一旦 x 足够大,并且步骤 3 的扩展性非常好(简单向量索引),步骤 2 就不依赖于 x 的长度。瓶颈应该是步骤 1,如果数据已经存储为因子,则可以避免该瓶颈。
    

“lubridate”包中的函数

7
投票
也非常快:

library(date)
library(lubridate)
set.seed(21)
x <- as.character(Sys.Date()-sample(40000, 1e6, TRUE))
system.time(date1 <- as.Date(x))
#  user  system elapsed 
# 12.86    0.00   12.94 
system.time(date2 <- as.Date(as.date(x,"ymd"))) # from package 'date'
#  user  system elapsed 
#  4.82    0.00    4.85 
system.time(date3 <- as.Date(parse_date_time(x,'%y-%m-%d'))) # from package 'lubridate'
#  user  system elapsed 
#  0.27    0.00    0.26 
all(date1 == date2)
#  TRUE
all(date1 == date3)
#  TRUE

考虑令人难以置信的快速 

6
投票
库,它适合 1970

anytime()

anydate()< issue. It uses the Boost date_time C++ library and provides functions
 进行转换。比较:
require(anytime)        #anydate()
require(lubridate)      #parse_date_time()
require(microbenchmark) #microbenchmark()

set.seed(21)
test.dd <- as.Date("2018-05-16") - sample(40000, 1e6, TRUE) #1 mln. random dates

microbenchmark(
    strptime(test.dd, "%Y-%m-%d"),                     #basic strptime
    parse_date_time(test.dd, orders = "ymd"),          #lubridate (POSIXct class)
    as.Date(parse_date_time(test.dd, orders = "ymd")), #lubridate + date class conversion
    anydate(test.dd),                                  #anytime library
    times = 10L, unit = "s"
)

结果/输出:

Unit: seconds expr min lq mean median uq max neval cld strptime(test.dd, "%Y-%m-%d") 10.177406012 10.472527403 1.064532e+01 10.621221596 10.819156870 11.288330598 10 c parse_date_time(test.dd, orders = "ymd") 4.541542019 4.603663894 4.844961e+00 4.869800287 5.055844972 5.128409226 10 b as.Date(parse_date_time(test.dd, orders = "ymd")) 4.461140695 4.568415584 4.867837e+00 4.739026273 5.080610126 5.532028490 10 b anydate(test.dd) 0.000000755 0.000004909 5.777500e-06 0.000005664 0.000006042 0.000012839 10 a

附注对于使用
时间序列
,请考虑

flipTime库。它拥有所有必需的工具,并且几乎与 anytime

 一样快用于转换目的:
require(devtools)
install_github("Displayr/flipTime")

进一步加速:您已经在使用 data.table。因此,使用您的日期创建一个查找表并将它们与您的数据合并。 

5
投票
library(lubridate) library(data.table) y <- seq(as.Date('1900-01-01'), Sys.Date(), by = 'day') id.date <- data.table(id = as.character(y), date = as.Date(y), key = 'id') set.seed(21) x <- as.character(Sys.Date()-sample(40000, 1e6, TRUE)) system.time(date3 <- as.Date(parse_date_time(x,'%y-%m-%d'))) # from package 'lubridate' # user system elapsed # 0.15 0.00 0.15 system.time(date4 <- id.date[setDT(list(id = x)), on='id', date]) # user system elapsed # 0.08 0.00 0.08 all(date3 == date4) # TRUE

这是一种解决方法,但我相信这就是 data.table 的用途。我不知道上面提到的时间/日期包内部是基于算法还是基于查找表(哈希表)。

对于较大的数据集,每当涉及字符操作(这往往很慢)时,我会考虑切换到查找参考表。

这比 Jonas Rauch 使用的因子方法稍快:

0
投票
> v=as.character(as.Date(sample(18000:19000,1e6,T))) > system.time({u=unique(v);as.Date(u)[match(v,u)]}) user system elapsed 0.041 0.000 0.042

这确实很快,但它仅适用于
%Y-%m-%d
格式,并且没有更改格式的选项:

> system.time({fasttime::fastDate(v)})
   user  system elapsed
  0.021   0.000   0.021

基准:
v=as.character(as.Date(sample(18000:19000,1e6,T)))

b=microbenchmark::microbenchmark(times=10,
  as.Date(v),
  fasttime::fastDate(v),
  {u=unique(v);as.Date(u)[match(v,u)]},
  {f=as.factor(v);as.Date(levels(f))[f]},
  anytime::anydate(v),
  strptime(v,"%Y-%m-%d"),
  as.Date(date::as.date(v,"ymd")),
  as.Date(lubridate::parse_date_time(v,orders="ymd")), {y=seq(as.Date(min(v)),as.Date(max(v)),1);data.table(id=as.character(y),date=as.Date(y),key="id")[setDT(list(id=v)),on="id",date]}
)

o=sort(tapply(b$time,b$expr,median))
writeLines(sprintf("%.2f %s",o/min(o),names(o)))

输出显示相对于最快选项的十次运行的中位时间:
1.00 fasttime::fastDate(v)
2.24 {     u = unique(v)     as.Date(u)[match(v, u)] }
2.62 {     f = as.factor(v)     as.Date(levels(f))[f] }
5.54 as.Date(lubridate::parse_date_time(v, orders = "ymd"))
13.19 {     y = seq(as.Date(min(v)), as.Date(max(v)), 1)     data.table(id = as.character(y), date = as.Date(y), key = "id")[setDT(list(id = v)),          on = "id", date] }
38.06 as.Date(date::as.date(v, "ymd"))
66.51 anytime::anydate(v)
152.23 strptime(v, "%Y-%m-%d")
160.41 as.Date(v)


© www.soinside.com 2019 - 2024. All rights reserved.