这已经被问了好几次没有明确的答案:我想将“YYYY-mm-dd”形式的 R 字符串转换为Date
. 该as.Date
功能非常缓慢。 在 R 中将字符转换为日期 *quickly*提供了一种解决方案,该解决方案fasttime
适用于 1970 年以后的日期。我的问题是我需要转换从 1900 年开始的日期,其中大约有 1 亿个。我必须经常这样做,所以速度很重要。还有其他解决方案吗?
问问题
6649 次
5 回答
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 代码,看看是否可以根据您的具体情况对其进行修改以使其更快。
于 2013-01-08T15:49:58.653 回答
10
不久前我遇到了类似的问题,并提出了以下解决方案:
- 将字符串转换为因子(如果还不是因子)
- 将因子的水平转换为日期
- 使用因子的索引向量将转换后的水平展开为解
扩展 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 足够大,第 2 步不依赖于 x 的长度,并且第 3 步的伸缩性非常好(简单向量索引)。瓶颈应该是第 1 步,如果数据已经作为一个因素存储,则可以避免。
于 2013-03-08T09:40:38.187 回答
6
'lubridate' 包中的功能parse_date_time
也非常快:
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
于 2016-02-17T01:21:48.150 回答
5
考虑令人难以置信的快速anytime
库,它适用于 1970< 问题。它使用 Boost date_time C++ 库并提供函数anytime()
和anydate()
用于转换。比较:
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
ps 对于使用时间序列,请考虑flipTime
库。anytime
它具有所有必需的工具,并且几乎与转换目的一样快:
require(devtools)
install_github("Displayr/flipTime")
于 2018-05-16T11:45:49.217 回答
3
进一步加速:您已经使用 data.table。因此,使用您的日期创建一个查找表并将它们与您的数据合并。
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 的用途。我不知道上面提到的时间/日期包内部是基于算法还是基于查找表(哈希表)。
对于较大的数据集,只要涉及字符操作,这往往很慢,我会考虑切换到查找参考表。
于 2017-05-14T09:34:56.303 回答