【问题标题】:Fast manipulation of Dates in R在 R 中快速操作日期
【发布时间】:2019-03-14 04:53:01
【问题描述】:

我有大约 34000 个日期向量,我必须更改日期和移动月份。我已经尝试过循环并使用mapply 函数,但它非常慢。 这是我所拥有的一个例子:

library(lubridate)
list_dates = replicate(34000,seq(as.Date("2019-03-14"),length.out = 208,by = "months"),simplify = F)
new_day = round(runif(34000,1,30))
new_day[sample(1:34000,10000)] = NA

new_dates = mapply(FUN = function(dates,day_change){
  day(dates) = ifelse(is.na(rep(day_change,length(dates))),day(dates),rep(day_change,length(dates)))
  dates = as.Date(ifelse(is.na(rep(day_change,length(dates))),dates,dates%m-%months(1)),origin = "1970-01-01")
  return(dates)
},dates = list_dates,day_change = as.list(new_day),SIMPLIFY = F)

变量new_dates 应该包含一个原始日期列表,这些日期相应地移动到变量new_day。 side中的函数是这样工作的:

  1. 如果 new_day 与 NA 不同,则会将日期更改为新日期
  2. 如果new_day 与NA 不同,它将把日期的月份移到后面。

我愿意接受任何可以提高速度的解决方案,无论使用何种包(如果它们在 CRAN 中)。

编辑

因此,基于 cmets,我将示例简化为包含 2 个日期的 2 个日期向量列表,并创建了一个新日期的手动向量:

list_dates = replicate(2,seq(as.Date("2019-03-14"),length.out = 2,by = "months"),simplify = F)

new_day = c(9,NA)

这是原始输入(变量 list_dates):

[[1]]
[1] "2019-03-14" "2019-04-14"

[[2]]
[1] "2019-03-14" "2019-04-14"

mapply 函数的预期输出是:

[[1]]
[1] "2019-02-09" "2019-03-09"

[[2]]
[1] "2019-03-14" "2019-04-14"

如您所见,第一个日期向量已更改为第 9 天,并且每个日期滞后一个月。第二个日期向量没有改变,因为 new_datesNA 对于那个值。

【问题讨论】:

  • 您能否将输入减少到 10 个日期,并显示您正在尝试做什么以及您的预期输出?
  • 除了 Ronak 的评论,请使用固定的随机种子来确保样本数据和预期输出的可重复性。我也不确定你所说的 “它将把日期的月份移到后面”是什么意思。你的意思是说 new_month = old_month - 1 对于list_dates 元素的所有日期?
  • 您是否想使用来自lubridate 的函数day 或索引您传递给mapply 的参数day 存在一些歧义。如果是后者,那么您应该使用方括号 [] 。在任何情况下,请避免将变量命名为与您打算使用的函数相同
  • 我将问题更新为尽可能少。 @Rohit 感谢关于 day 参数的评论,这是我从原始函数中遇到的一个错误,已经更改它。

标签: r lubridate


【解决方案1】:

这是lubridate 解决方案

library(lubridate)
mapply(
    function(x, y) { if (!is.na(y)) {
            day(x) <- y;
            month(x) <- month(x) - 1
        }
        return(x) },
    list_dates, new_day, SIMPLIFY = F)
#[[1]]
#[1] "2019-02-09" "2019-03-09"
#
#[[2]]
#[1] "2019-03-14" "2019-04-14"

或使用purrr

library(purrr)
library(lubridate)
map2(list_dates, new_day, function(x, y) {
    if (!is.na(y)) {
        day(x) <- y
        month(x) <- month(x) - 1
    }
    x })

【讨论】:

  • 谢谢,但我将您的函数和我的函数与microbenchmark 中长度为 208 的 2 个日期进行比较,并且 mi 原始函数略快。
  • @AlejandroAndrade 我明白了;我添加了一个purrr 选项,不确定在microbenchmark 比较中的比较。 PS:这里并没有真正的理由不赞成,坦率地说,这是对 SO 受访者提供免费建议的一般概念的某种误导性的回应;该解决方案重现了您的预期输出。
  • 新功能更快。关于否决票,您的原始答案基本上是我的相同功能,但是您使用了 if 而不是 ifelse 并且速度较慢,这不是问题的预期结果(我已经有一个功能可以做到并且速度更快)。我知道它的免费建议并感谢它,但我也因为一个合理的问题而被否决。结果仍然很慢,但我猜你没有尝试超过 100 个日期。
【解决方案2】:

除了 Maurits 的解决方案,如果想进一步提高计算速度,可以考虑使用多核与doParallel

library(data.table)
library(doParallel)

registerDoParallel(3)
df <- data.table(new_day,list_dates)

mlply(df,
      function(new_day,list_dates){

        list_dates <- list_dates[[1]]

        if(!is.na(new_day)){
          day(list_dates) <- new_day
          list_dates <-  list_dates %m-% months(1)
        }

        return(list_dates)
      }, .parallel = T, .paropts = list(.packages='lubridate')
)

【讨论】:

  • 我不可能并行化这个函数,因为我已经在运行一个更大的函数,它用foreach调用这个函数
猜你喜欢
  • 2018-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-13
  • 1970-01-01
  • 2012-10-05
  • 2023-03-07
  • 1970-01-01
相关资源
最近更新 更多