【发布时间】:2015-08-28 11:54:50
【问题描述】:
我有一个包含大约 250k 行的 40 个数据帧的列表,我想将一个新变量附加到每个数据帧。这个新变量period是从另一个包含Date对象的变量中计算出来的,转换很简单,如果日期的年份部分低于2015,则设置为“新”否则“旧”。
我认为使用矢量化计算会非常快,但大约需要 41 秒才能完成! (使用 for 循环或 lapply 可以获得相同的性能)。
可重现的例子:
datas.d <- function(nDf, nRow) {
lapply(seq_len(nDf), function(x) {
data.frame(
id1 = sample(7e8:9e8, nRow),
id2 = sample(1e9, nRow),
id3 = sample(1e9, nRow),
date = sample(seq(as.Date("2012-01-01"), Sys.Date(), by = 1), nRow, rep = TRUE),
code1 = sample(10, nRow, rep = TRUE),
code2 = sample(10, nRow, rep = TRUE),
code3 = sample(10, nRow, rep = TRUE)
)
})
}
datasDate <- datas.d(40, 25e4)
forLoopDate <- function(datas) {
for (i in seq_along(datas)) {
datas[[i]]$period <- rep("old", nrow(datas[[i]]))
datas[[i]]$period[format(datas[[i]]$date, "%Y") == "2015"] <- "new"
}
return(datas)
}
> system.time(forLoopDate(datasDate))
utilisateur système écoulé
41.46 0.31 41.84
当我将字符串强制转换为 800k 行数据帧中的日期时,我已经经历了缓慢的性能,因此我怀疑日期操纵对性能不佳是有罪的。 R Profiler 证实了这一点:
Rprof(tmp <- tempfile())
datas <- forLoopDate(datasDate)
Rprof(NULL)
summaryRprof(tmp)
$by.self
self.time self.pct total.time total.pct
"format.POSIXlt" 39.34 94.16 39.34 94.16
"as.POSIXlt.Date" 1.80 4.31 1.80 4.31
"==" 0.36 0.86 0.36 0.86
"forLoopDate" 0.22 0.53 41.78 100.00
"format.Date" 0.06 0.14 41.20 98.61
所以我尝试了跳过日期格式的相同转换,即直接使用年份的字符串。性能提升是明确的:
我还使用 lubridate 包中的另一个格式化函数 year 对其进行了测试。格式化非常快,我猜是因为它是在 C 级别做的?
datas.s <- function(nDf, nRow) {
lapply(seq_len(nDf), function(x) {
data.frame(
id1 = sample(7e8:9e8, nRow),
id2 = sample(1e9, nRow),
id3 = sample(1e9, nRow),
date = sample(2012:2015, nRow, rep = TRUE),
code1 = sample(10, nRow, rep = TRUE),
code2 = sample(10, nRow, rep = TRUE),
code3 = sample(10, nRow, rep = TRUE)
)
})
}
datasString <- datas.s(40, 25e4)
forLoopString <- function(datas) {
for (i in seq_along(datas)) {
datas[[i]]$period <- rep("old", nrow(datas[[i]]))
datas[[i]]$period[datas[[i]]$date == "2015"] <- "new"
}
return(datas)
}
library(lubridate)
forLoopDate2 <- function(datas) {
for (i in seq_along(datas)) {
datas[[i]]$period <- rep("old", nrow(datas[[i]]))
datas[[i]]$period[year(datas[[i]]$date) == 2015] <- "new"
}
return(datas)
}
library(microbenchmark)
mbm <- microbenchmark(
date = datas <- forLoopDate(datasDate),
string = datas <- forLoopString(datasString),
lubridate = datas <- forLoopDate2(datasDate),
times = 10L)
> mbm
Unit: seconds
expr min lq mean median uq max neval
date 41.502728 41.561497 41.649533 41.652306 41.69218 41.875110 10
string 4.119266 4.131186 4.167809 4.166946 4.17993 4.239481 10
lubridate 2.088281 2.105413 2.133042 2.111710 2.15794 2.250739 10
这里有很多问题!
_为什么用 R 格式化/转换日期会那么慢?
_我可以使用 Base R 提高我的代码的性能吗?在处理日期/日期时间时,出于性能目的,有哪些好的做法?
谢谢!
【问题讨论】:
标签: r performance date datetime