2017-01-13 1 views
1

私は顧客IDの列、購入日の列、および列を持つdata.tableを持っていますその購入額を私がしたいことは、顧客の間で毎日の購入価値の平均を計算し、欠損値を次の利用可能な値で埋めることです。data.table列の(locf/nocb)値を効率的に記入し、別の列で集計します

わかりやすくするために、最小限の例では重複する日はありません。

library(data.table) 
dat <- data.table(custid=rep(seq(10),5), day=sample(50), val=rnorm(50,0,1))[order(custid,day)] 

これを解決する方法はわかっていますが、効率的に行う方法はわかりません。一つの解決策は、欠損値がzooからna.locf()を使用して、後方次の観察を運ぶために、その後NAとなり、そのようdata.tableを拡大することです:

library(zoo) 
res <- dat[as.data.table(expand.grid(custid=seq(10), day=seq(50))), on=c('custid','day'), allow.cartesian=TRUE, nomatch=NA][order(custid,day)] 
res[, val:=na.locf(val, fromLast=TRUE, na.rm=FALSE), by='custid'] 
res <- res[,list(meanVal=mean(val, na.rm=TRUE)), by='day'] 

多くの日がある場合ただし、これは非常に大きなテーブルを作成し、多くの顧客が、ほとんどの顧客はたった数日で購入しました。だから私はそれを望んでいない。

別の解決策は、一日あたりの日、フィルタと集計をループにし、再度data.tableに行をバインドします

res2 <- list() 
for (dy in seq(max(dat$day))) { 
    res2 <- c(res2, 
       list(dat[day>=dy, .SD[1], by='custid'][,list(day=dy, meanVal=mean(val, na.rm=T))])) 
} 
res2 <- rbindlist(res2) 

しかし、これは遅いです。

誰も、遅いループや大規模な中間テーブルの作成を必要としないdata.tableソリューションを考え出すことができますか?私の限られたテストで

+0

'aggregate(dat、list(dat $ day)、mean)[、c( 'day'、 'val')]'あなたは何をしたいですか? – Ryan

+0

平均化する前にNAsを次の値に置き換えたい場合は、直接dat [is.na(dat $ val)、] $ val < - dat [which(na) )+ 1、] $ val' – Ryan

答えて

2

これはあなたのオプション(ところで代わりdata.table(expand.gridCJを使用)のいずれかよりも高速で、多くのメモリを使用していません:

dat[dat, on = .(day >= day), mean(val[!duplicated(custid)]), by = .EACHI] 

これは、データはOPのように一日でソートされていると仮定し。

+0

これは私が探していたものです - 純粋なdata.tableソリューションです!ありがとう! – mpjdem

関連する問題