工作日/周/月的最佳提取日期,内存紧凑性?

smc*_*mci 1 memory-management aggregate r date

执行摘要:*提高下面聚合fn调用中使用的基于日期的提取函数的内存效率; 不吹灭1Gb内存限制.*.

我有一个大的数据集TR存储在数据帧(3个COLS,1200万行;〜200MB)
的列CUSTOMER_ID(整数),VISIT_DATE和visit_spend(数字)数据集需要注册,所以这是因为它可以作为可再现的be:
数据集看起来像(完整文件在这里,需要注册):

customer_id,visit_date,visit_spend 
2,2010-04-01,5.97 
2,2010-04-06,12.71 
2,2010-04-07,34.52
#... 12146637 rows in total
Run Code Online (Sandbox Code Playgroud)

日期范围限制在2010-04-01 ... 2011-06-30(整数14700..15155)

在这里,我想问visit_date字段选择什么是最佳表示.我做了一些aggregate调用(底部的示例代码)来炸毁内存.我也使用日期实用程序fns,就像这里附带的东西(它们需要重新编码以获得紧凑性,但这些是我想做很多的典型操作).所以我需要一个代表避免这个的日期.

正如我所看到的,我可以使用三种可能的表示来访问visit_date字段,这里有优点和缺点,我正在尝试做什么.我的目标是获得不会破坏内存的格式,并在这些日期处理操作,聚合等方面给予最少的悲伤:

  • 整数因子
    缺点:
    1)不允许比较或排序操作,因此聚合是痛苦的.
    2)我需要硬编码所有与日期相关的功能(例如,2010年4月的14700..14729) - 可行但痛苦.
    3)需要手动处理图形.
  • 数字
    缺点:由于要求asDate()无处不在,因此会耗尽内存.
  • 日期
    优点:最适合打印(),图形和直方图; 在绘图之前不需要手动处理.
    缺点:如果我应用任何字符串操作(格式)或聚合,则会爆炸(内存不足).我认为这是chron :: Date,它是你设置class(tr$visit_date)<-'Date')或使用时得到的任何东西read.csv(colClasses=c(...,"Date",...)

这些是我希望运行很多的日期实用程序fns(但目前它们在聚合期间会爆炸):

# Utility fns related to date    
library(chron)    
# Get dayoftheweek as integer 0(Sun)..6(Sat)
dayofweek <- function(ddd) {
    with( month.day.year(ddd), day.of.week(month,day,year) )
    #do.call( "day.of.week", month.day.year(x) )
    # as.numeric(x-3)%%7
}    
weekofyear <- function(dt) {
    as.numeric(format(as.Date(dt), "%W"))
}
monthofyear <- function(dt) {
    as.numeric(format(as.Date(dt), "%m"))
}    
# Append a dayoftheweek-as-string field
append_dotwa_column <- function(x,from_date_fld) {
    x$dotwa <- format(x$from_date_fld,"%a")
}
Run Code Online (Sandbox Code Playgroud)

这里只是一个在内存不足的聚合()调用:

agg_dly_spend  <- aggregate(tr$visit_spend, 
        list('visit_date'=tr$visit_date), FUN="sum")
agg_wkly_spend <- aggregate(tr$visit_spend, 
        list('weekofyear'=weekofyear(tr$visit_date)), FUN="sum")
Run Code Online (Sandbox Code Playgroud)

(那些aggregate()调用应该占用多少内存?如果我错了,请纠正我,但混合类型使其难以使用bigmemory.所以我可能不得不去SQL,但这是一个很大的损失 - 我失去了R真的很好子集按日期:tr[tr$visit_date > "2010-09-01",])

(平台是R 2.13.1,Windows Vista 32b因此有2Gb的整体处理限制,这意味着任何数据帧不应超过~600-900Mb)

Jor*_*eys 6

编辑:我复制的代码不是最终的函数,所以它有bug.现在修复了错误.

我并不完全同意结束投票,但你的问题确实需要一些阅读.据我了解,问题是日期的表示.数字只是一个疯狂的想法,在这种情况下使用整数.就像不同格式及其相对空间的概述一样(使用这个问题的lsos函数:)

Dates <- rep(seq.Date(as.Date("2010-04-01"),
             as.Date("2011-04-01"),by='day'),each=100)

chardates <- as.character(Dates)
POSIXctDates <- as.POSIXct(Dates)
POSIXltDates <- as.POSIXlt(Dates)
integerDates <- as.integer(Dates)
chronDates <- as.chron(Dates)
numericDates <- as.numeric(Dates)

 > lsos()
                  Type    Size PrettySize  Rows Columns
POSIXltDates   POSIXlt 1464976     1.4 Mb 36600      NA
chronDates       dates  293400   286.5 Kb 36600      NA
POSIXctDates   POSIXct  292976   286.1 Kb 36600      NA
Dates             Date  292944   286.1 Kb 36600      NA
numericDates   numeric  292824     286 Kb 36600      NA
charDates    character  161064   157.3 Kb 36600      NA
integerDates   integer  146424     143 Kb 36600      NA
Run Code Online (Sandbox Code Playgroud)

内部Date可以与数字表示很好地竞争.character导致所有其他功能出现问题,所以忘掉那个.我只是使用Date,这样做并保持功能正常.注意以下内容的大小POSIXlt:提取月份,周,日等的所有功能都可以使用该格式.这format()对于函数weekdays()来说都是正确的months(),...... base或者在chron包中.

一些评论:

  • Vista在具有4Gb的机器上具有理论上的3Gb限制.你试过吗memory.limit(3000):看?memory.limit.
  • 如果你关心体面的内存管理,请尽快更新到Windows 7.Vista太糟糕了.重要时刻.如果可能,如果您使用该数据,请使用64位计算机.
  • 偶尔清理你的工作区.我可以在R 32bit上运行您的代码,在下面给出的数据帧(1千万行)上有2Gb内存限制.

关于你的代码.我使用的Date格式与数字格式大小相同.让我们尝试使用以下数据(您可以提供......),拥有1460万行.我运行的Windows7(64位)总共有4Gb内存.

n <- 40000
tr <- data.frame(
  customer_id = rep(1:366,n),
  visit_date = rep(seq.Date(as.Date("2010-04-01"),
                     as.Date("2011-04-01"),by='day'),each=n),
  visit_spend =runif(366*n,3,12)
 )
Run Code Online (Sandbox Code Playgroud)

首先是你的周功能.如上所述,该format函数使用底层POSIXlt格式,如图所示,它是内存密集型的.不过,只需直接访问它就可以减少大约一半的内存(请参阅参考资料?POSIXlt).它返回整数,大约占你返回的数字的一半内存:

dayofweek2 <- function(dt) {
  as.POSIXlt(dt)$wday
}

monthofyear2 <- function(dt){
  as.POSIXlt(dt)$mon +1L
}

weekofyear2 <- function(dt) {
  n <- length(dt)
  lt <- as.POSIXlt(dt)
  yearstart <- c(0,which(diff(lt$year)!=0))
  fday <- rep(lt[yearstart+1L]$wday,
              diff(c(yearstart,n)+1L)
          )
  as.integer((lt$yday+fday-1L)%/%7)
}

> lsos()
                   Type    Size PrettySize  Rows Columns
tr           data.frame  733128   715.9 Kb 36600       3
Dates              Date  293048   286.2 Kb 36600      NA
x1              numeric  292840     286 Kb 36600      NA # from your function
x2              integer  146440     143 Kb 36600      NA # from my function
Run Code Online (Sandbox Code Playgroud)

如果你需要更少,你将不得不自己做数学.但我建议你不要尝试这一点,绝对不是基于角色表示.字符串操作喜欢strsplit()并且substr()肯定会炸掉你的记忆.month.day.year()chron包的功能一样.远离chron大数据.事实上,无论POSIXlt对象需要多大的空间,使用POSIXlt仍然是内存提取的最佳选择.

到了aggregate.这适用于数据帧,因此aggregate调用会再次生成大量数据副本.更多地手动拨打电话,可以再次保存在副本上.关于功能的提议:

my.agg <- function(x,d,AGGFUN=NULL,FUN="sum"){
  FUN <- match.fun(FUN)
  if(!is.null(AGGFUN)) {
    AGGFUN <- match.fun(AGGFUN)
    d <- AGGFUN(d)
  }
  ud <- sort(unique(d))

  xout <- sapply(ud,function(i){
    id <- which(d==i)  # find which values respond to a certain value of d
    FUN(x[id])         # apply the function to only those values
  },USE.NAMES=FALSE)

  data.frame(d=ud,x=xout)    
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们应用这个并且我们观察内存使用情况:

gc(TRUE,reset=TRUE)
x1 <-  aggregate(tr$visit_spend, 
           list('weekofyear'=weekofyear(tr$visit_date)), FUN="sum")
rm(x1)
gc(TRUE,reset=TRUE)
Sys.sleep(5)
x2 <- my.agg(tr$visit_spend,tr$visit_date,weekofyear2,sum)    
gc(TRUE,reset=TRUE)
Run Code Online (Sandbox Code Playgroud)

,我得到以下结果:

在此输入图像描述

红色方块是您的总呼叫,黄色方块是我的提议.聚合调用的内存使用量的第一个突破是weekofyear您使用的功能.我的建议既节省了内存使用weekofyearaggregate调用,也运行得更快.我使用我的提议从未获得超过2.6Gb的总内存.

希望这可以帮助.