我需要优化的是以下过程:从yyyymmdd
形式的数值开始,将其转换为日期,添加3个月(不是90天,月份必须保持为相同),然后将其转换回yyyymmdd
格式的数字。以下代码实现了这一点:
library(lubridate)
a = 20180223
b = as.numeric(gsub("-", "",
x = as.character(ymd(a) %m+% months(3)),
fixed = TRUE))
b
20180523
然而,在我看来,将它应用于大型data.table
时运行速度太慢。下面是100万行的代码,在我的机器上运行13秒。有没有办法优化这个?我想,也许我不需要转换到约会,但不能绕过它。
library(data.table)
library(lubridate)
DT = CJ(rep(2016:2018, 1000), 1:12, 1:28)
DT[, StartDate := V1*10000 + V2*100 + V3]
system.time({
DT[, StartDate_3M := as.numeric(gsub("-", "",
x = as.character(ymd(StartDate) %m+% months(3)),
fixed = TRUE))]
})
user system elapsed
13.03 0.04 13.18
答案 0 :(得分:2)
这个答案是小组的努力,给出了很多有价值的评论。
library(data.table)
library(lubridate)
library(microbenchmark)
DT = CJ(rep(2016:2018, 100), 1:12, 1:28)
DT[, StartDate := V1*10000 + V2*100 + V3]
我将数据集缩小,以便在我的机器上进行比较以更快地完成。
预先计算期间:
diff_3m <- months(3)
这个建议的代码可以准确地为您提供所需内容,并指出20180131
之类的日期不会超过月末,因此您最终会使用20180430
来自%m+%
的{{1}}运算符。
lubridate
我的机器上的时间:
DT[, StartDate_3M_new := as.numeric(format(ymd(StartDate) %m+% diff_3m, "%Y%m%d"))]
当然,我不知道这是否是最快的&#34;但是我认为重新实现所有小日期技巧只需要更快的计算所花费的时间可能会超过运行此代码所需的时间。
编辑添加:这是@BogdanC的算术答案的清理版(即不会发出警告)。随着闰年免费!表现类似。
microbenchmark(
orig = DT[, StartDate_3M_orig := as.numeric(gsub("-", "",
x = as.character(ymd(StartDate) %m+% months(3)),
fixed = TRUE))],
new = DT[, StartDate_3M_new := as.numeric(format(ymd(StartDate) %m+% diff_3m, "%Y%m%d"))], times=10)
Unit: milliseconds
expr min lq mean median uq max neval
orig 713.2652 734.5133 798.1475 749.9207 883.2163 912.6070 10
new 458.8666 483.0388 523.6454 502.4709 518.7074 665.7304 10
答案 1 :(得分:0)
我继续前进并进行算术实现。我写的不是最干净的,但这是一个很好的起点。我用于添加一个月的逻辑与ymd() %m+% months(3)
相同,但有一个明显的例外是不处理闰年(也可以这样做,但我的业务逻辑并不需要它)。
下面的完整代码和基准 - 它比我原来的想法快96%,比团队努力快94%(我仍然非常重视)。
library(data.table)
library(lubridate)
library(microbenchmark)
DT = CJ(rep(2016:2018, 1000), 1:12, 1:28)
DT[, StartDate := V1*10000 + V2*100 + V3]
diff_3m <- months(3)
# arithmetic implementation
month_max_days <- c(31,28,31,30,31,30,31,31,30,31,30,31)
add_months <- function(dt, n_months, month_days) {
dt[, year := StartDate %/% 10000]
dt[, month := (StartDate - year * 10000) %/% 100]
dt[, day := StartDate %% 100]
dt[month + n_months <= 12, new_month := month + n_months]
dt[month + n_months > 12, new_month := (month + n_months) ]
dt[month + n_months > 12, new_month := new_month %% 12 ]
dt[month + n_months <= 12, StartDate_3M := year * 10000 + new_month * 100 + pmin(day,month_days[new_month])]
dt[month + n_months > 12, StartDate_3M := (year + (month + n_months) %/% 12) * 10000 + new_month * 100 + pmin(day,month_days[new_month])]
dt
}
microbenchmark(
orig = DT[, StartDate_3M_orig := as.numeric(gsub("-", "",
x = as.character(ymd(StartDate) %m+% months(3)),
fixed = TRUE))],
new = DT[, StartDate_3M_new := as.numeric(format(ymd(StartDate) %m+% diff_3m, "%Y%m%d"))],
new_v2 = add_months(DT,3,month_max_days),
times=10)
Unit: milliseconds
expr min lq mean median uq max neval
orig 11445.7826 12749.9895 13260.7266 13021.3065 13952.189 15247.221 10
new 7953.0839 8643.7795 10004.3372 9484.5933 11104.017 13002.025 10
new_v2 215.5608 309.2308 570.2348 408.0091 761.361 1196.798 10