通过data.table将日期更改为月份的结束日期

时间:2017-05-23 02:26:08

标签: r date memory-management data.table

我有4190145次观察。我想把我的约会时间改为月末。我在下面解释。

以下是我的数据部分:

Time1
2015/01/15
2015/02/24
2015/07/18
2015/11/10
2016/02/20
2016/04/26
2016/08/17

我想创建新列Time2

Time1           Time2
2015/01/15      2015/01/31
2015/02/24      2015/02/28
2015/07/18      2015/07/31
2015/11/10      2015/11/30
2016/02/20      2016/02/29
2016/04/26      2016/04/30
2016/08/17      2016/08/31

代码是:

data[, Time2 := Time1]
day(data$Time2) <- days_in_month(data$Time1)

然而,我收到了错误。

Error: cannot allocate vector of size N Mb

因此,我在Stack Overflow上搜索我的问题并找到this

我使用gc()但仍无效。所以我看到了sessionInfo()

sessionInfo()
R version 3.3.3 (2017-03-06)
Platform: i386-w64-mingw32/i386 (32-bit)
Running under: Windows 7 (build 7601) Service Pack 1

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] lubridate_1.6.0   data.table_1.10.4

loaded via a namespace (and not attached):
 [1] lazyeval_0.2.0 R6_2.2.0       assertthat_0.1 magrittr_1.5   DBI_0.5-1      tools_3.3.3    dplyr_0.5.0    tibble_1.2    
 [9] Rcpp_0.12.9    stringi_1.1.2  stringr_1.1.0 

我认为这是因为我的电脑只是32位。并且memory.limit()可以将大小设置为4000,因为我的RAM是4 GB。

此外,我发现如果我使用data.table执行,我可以运行。

所以,我的问题是如何将我的代码从day(data$Time2) <- days_in_month(data$Time1)更改为data.table格式。

也许喜欢data[, day(data$Time2) := days_in_month(data$Time1)]

我知道这是不正确的,因为我得到了

  

错误:无法在同一查询中两次分配到同一列   (检测到重复)。

有什么建议吗?

2 个答案:

答案 0 :(得分:2)

也许这有用吗?

set.seed(120340)
NN = 5e6
DT = data.table(Time1 = 
                  sprintf('%04d/%02d/%02d',
                          sample(2000:2017, NN, TRUE),
                          sample(12, NN, TRUE),
                          sample(28, NN, TRUE)))

# potential memory bottleneck
DT[ , c('y', 'm', 'd') := tstrsplit(Time1, '/')]

days_month = data.table(
  month = sprintf('%02d', 1:12),
  days = c(31L, 28L, 31L, 30L, 31L, 30L, 
           31L, 31L, 30L, 31L, 30L, 31L)
)

DT[days_month, d_end := i.days, on = c(m = 'month')]
DT[m == 2L & as.integer(y) %% 4L == 0L, d_end := 29L]
DT[ , Time2 := do.call(paste, c(.SD, list(sep = '/'))), 
    .SDcols = c('y', 'm', 'd_end')]

如果失败了,我猜这会减少对内存的影响:

DT[ , y := gsub('/.*', '', Time1)]
DT[ , c('m', 'd') := tstrsplit(Time1, '/')[2L:3L], by = y]

如果失败了,我建议购买更多RAM,或者在部署到更严重的机器之前使用一部分数据。另外,正如Frank指出的那样,你真的希望将它们存储为IDates以获得最大的内存效率。

答案 1 :(得分:0)

为了完整起见,我将使用<img class="anim-object bluebag iteration-1 speed-7 " src="http://via.placeholder.com/350x150" />data.table执行此操作:

lubridate

使用# create sample data library(data.table) set.seed(120340) NN <- 1e6 DT <- data.table(Time1 = sprintf('%04d/%02d/%02d', sample(2000:2017, NN, TRUE), sample(12, NN, TRUE), sample(28, NN, TRUE)))

ceiling_date()
library(lubridate)
DT[, Time2 := ceiling_date(ymd(Time1), "month") - 1]
DT
              Time1      Time2
      1: 2005/04/14 2005-04-30
      2: 2007/01/11 2007-01-31
      3: 2014/09/08 2014-09-30
      4: 2017/05/13 2017-05-31
      5: 2008/05/23 2008-05-31
     ---                      
 999996: 2003/06/08 2003-06-30
 999997: 2004/04/12 2004-04-30
 999998: 2009/06/10 2009-06-30
 999999: 2013/02/04 2013-02-28
1000000: 2014/03/05 2014-03-31
tables()

使用 NAME NROW NCOL MB COLS KEY [1,] DT 1,000,000 2 16 Time1,Time2 Total: 16MB 的赋值运算符data.table通过引用更新:=,即不复制整个数据对象。

DT 获取一个日期时间对象,并将其四舍五入到指定时间单位的最近边界,即到下个月的第一天。因此,我们必须减去1天才能得到实际月份的最后一天。

使用ceiling_date()

减少内存占用

as.IDate()有自己的日期和时间类,带有整数存储空间,可用于快速排序和分组。在某些系统上,整数存储data.table需要的内存少于may

double
DT[, Time1 := as.IDate(Time1, "%Y/%m/%d")]
DT[, Time2 := as.IDate(ceiling_date(ymd(Time1), "month") - 1)]
tables()

这里只需要一半的内存。