【问题标题】:convert string date to R Date FAST for all dates将所有日期的字符串日期转换为 R 日期 FAST
【发布时间】:2013-01-08 15:25:15
【问题描述】:

这个问题已经被问了好几次了,没有明确的答案:我想将“YYYY-mm-dd”形式的R字符串转换为Dateas.Date 函数非常慢。 convert character to date *quickly* in R 提供了一个使用 fasttime 的解决方案,适用于 1970 年以后的日期。我的问题是我需要转换从 1900 年开始的日期,其中大约有 1 亿个。我必须经常这样做,所以速度很重要。还有其他解决方案吗?

【问题讨论】:

  • quickly 意味着 1 亿个算子应该花不到一分钟的时间。我确实需要它们采用Date 格式。
  • @Woot4Moo:我不相信这是重复的,因为我知道为什么这很慢。我正在寻找可能存在的 R 包中的解决方案。
  • 我确认使用日期格式真的很慢,而且我使用数据已经 50 年了。在我的情况下,可以切换到字符串并在我的代码中对年、月、日的查询进行 strsplit。
  • 是的,但是如果分析需要两个小时,那么加快 10 分钟的转换并没有多大意义。

标签: r date posix


【解决方案1】:

我可以通过使用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 代码,看看是否可以根据您的具体情况对其进行修改以更快。

【讨论】:

    【解决方案2】:

    前段时间我也遇到过类似的问题,想出了如下解决方案:

    1. 将字符串转换为因子(如果还不是因子)
    2. 将因子的级别转换为日期
    3. 使用因子的索引向量将转换后的水平展开

    扩展 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 步,如果数据已经作为一个因素存储,则可以避免。

    【讨论】:

    • 第 3 步的输出“dDate”不是一个因素。类(dDate)=日期。所以,我不明白你为什么要对字符串进行因子转换,然后执行 as.Date(levels(xf))。难道只是为了加快转化速度?
    【解决方案3】:

    '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
    

    【讨论】:

      【解决方案4】:

      考虑非常快的anytime 库,它适用于 1970anytime() 和 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 
      

      附言对于使用时间序列,请考虑flipTime 库。它具有所有必需的工具,并且几乎与 anytime 一样快,用于转换目的:

      require(devtools)
      install_github("Displayr/flipTime")
      

      【讨论】:

        【解决方案5】:

        进一步加速:您已经使用 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 的用途。我不知道上面提到的时间/日期包内部是基于算法还是基于查找表(哈希表)。

        对于较大的数据集,每当涉及字符操作时,这往往很慢,我会考虑切换到查找参考表。

        【讨论】:

          猜你喜欢
          • 2011-11-28
          • 2020-09-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-26
          相关资源
          最近更新 更多