为什么 R(在我的示例中)处理日期/日期时间非常慢



我有一个包含 40 个数据帧的列表,大约有 250k 行,我想为每个数据帧附加一个新变量。这个新变量period是从另一个包含Date对象的变量计算出来的,转换非常简单,如果年份部分的日期低于2015年周期设置为"new",否则设置为"old"。

我以为使用矢量化计算会非常快,但大约需要 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 行数据帧中将字符串强制放入 Date 时,我已经经历了性能缓慢的情况,所以我怀疑日期操纵对糟糕的性能感到内疚。R 分析器证实了这一点:

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

所以我尝试了跳过日期格式的相同转换,即直接使用年份字符串。性能提升是明确的:

我还使用另一个格式化功能对其进行了测试,该功能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

这里有很多问题!

_Why格式化/转换日期是那么慢吗?

_Can 我使用 Base R 提高代码的性能?出于性能目的,在处理日期/日期时间时有哪些良好做法?

谢谢!

一个可以返回许多不同格式的format函数,预计会很慢。如果您对lubridate的year功能感到满意,则可以使用其(非常简单的)代码:

as.POSIXlt(x, tz = tz(x))$year + 1900

通常,当性能很重要时,应避免任何类型/类和字符之间的转换。这通常会很慢。最好进行数值计算(例如,您可以使用作为 Date 变量基础的整数,但这会导致闰年问题,因此最好使用 POSIXlt,它会为您处理这个问题)。

相关内容

  • 没有找到相关文章

最新更新