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字段,这里有优点和缺点,我正在尝试做什么.我的目标是获得不会破坏内存的格式,并在这些日期处理操作,聚合等方面给予最少的悲伤:
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)
编辑:我复制的代码不是最终的函数,所以它有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包中.
一些评论:
memory.limit(3000):看?memory.limit.关于你的代码.我使用的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您使用的功能.我的建议既节省了内存使用weekofyear和aggregate调用,也运行得更快.我使用我的提议从未获得超过2.6Gb的总内存.
希望这可以帮助.