按R,DATA.TABLE中的多个组合计算均值和NMIS



我想用大型数据集计算几个组组合中Na的平均值和计数。使用一些测试数据解释可能最容易。我在MacBook Pro和Data.Table软件包上使用了最新版本的R(数据很大,> 1m行(。(注意:发布此信息后,我注意到我不小心使用了sum((而不是均值((,用于" m ="下面的变量。我没有编辑它,因为我不想重新运行所有内容,然后不要进行。认为这很重要(

set.seed(4)
YR = data.table(yr=1962:2015)
ID = data.table(id=10001:11000)
ID2 = data.table(id2 = 20001:20050)
DT <- YR[,as.list(ID), by = yr] # intentional cartesian join
DT <- DT[,as.list(ID2), by = .(yr, id)] # intentional cartesian join
rm("YR","ID","ID2")
# 2.7M obs, now add data
DT[,`:=` (ratio = rep(sample(10),each=27000)+rnorm(nrow(DT)))]
DT <- DT[round(ratio %% 5) == 0, ratio:=NA] # make some of the ratios NA
DT[,`:=` (keep = as.integer(rnorm(nrow(DT)) > 0.7)) ] # add in the indicator variable
# do it again
DT[,`:=` (ratio2 = rep(sample(10),each=27000)+rnorm(nrow(DT)))]
DT <- DT[round(ratio2 %% 4) == 0, ratio2:=NA] # make some of the ratios NA
DT[,`:=` (keep2 = as.integer(rnorm(nrow(DT)) > 0.7)) ] # add in the indicator variable

所以,我所拥有的是识别信息(yr,id,id2(和我要汇总的数据:keep1 | 2,比率1 | 2。特别是YR-ID,我想使用Keep and Keep2(因此压缩ID2(来计算平均比率和比率2。我想到通过keep/keep2进行子集的计算比率和比率2或通过矩阵的矩阵乘法来进行此操作。

首先,我正在这样做的方式得到正确的答案,但是很慢:

system.time(test1 <- DT[,.SD[keep == 1,.(m = sum(ratio,na.rm = TRUE), 
                                nmiss = sum(is.na(ratio)) )
                      ],by=.(yr,id)])
   user  system elapsed 
 23.083   0.191  23.319 

这在大约同一时间也同样有效。我认为首先将主要数据(而不是在.sd内部(划分可能更快:

system.time(test2 <- DT[keep == 1,.SD[,.(m = sum(ratio,na.rm = TRUE), 
                                nmiss = sum(is.na(ratio)) )
                       ],by=.(yr,id)])
   user  system elapsed 
 23.723   0.208  23.963

这两种方法中的任何一种问题是我需要为每个keep变量进行单独的计算。因此,我尝试了这种方式:

system.time(test3 <- DT[,.SD[,.( m = sum(ratio*keep,na.rm = TRUE),
                                 nmiss = sum(is.na(ratio*keep)) )
                       ],by=.(yr,id)])
   user  system elapsed 
 25.997   0.191  26.217 

这使我能够将所有公式放在一起(我可以添加ratio*keep2ratio2*keepratio2*keep2(,但是1.它较慢,2。它没有得到正确的NAS(请参阅nmiss列(:

> summary(test1)
       yr             id              m               nmiss      
 Min.   :1962   Min.   :10001   Min.   : -2.154   Min.   :0.000  
 1st Qu.:1975   1st Qu.:10251   1st Qu.: 30.925   1st Qu.:0.000  
 Median :1988   Median :10500   Median : 53.828   Median :1.000  
 Mean   :1988   Mean   :10500   Mean   : 59.653   Mean   :1.207  
 3rd Qu.:2002   3rd Qu.:10750   3rd Qu.: 85.550   3rd Qu.:2.000  
 Max.   :2015   Max.   :11000   Max.   :211.552   Max.   :9.000  
> summary(test2)
       yr             id              m               nmiss      
 Min.   :1962   Min.   :10001   Min.   : -2.154   Min.   :0.000  
 1st Qu.:1975   1st Qu.:10251   1st Qu.: 30.925   1st Qu.:0.000  
 Median :1988   Median :10500   Median : 53.828   Median :1.000  
 Mean   :1988   Mean   :10500   Mean   : 59.653   Mean   :1.207  
 3rd Qu.:2002   3rd Qu.:10750   3rd Qu.: 85.550   3rd Qu.:2.000  
 Max.   :2015   Max.   :11000   Max.   :211.552   Max.   :9.000  
> summary(test3)
       yr             id              m               nmiss      
 Min.   :1962   Min.   :10001   Min.   : -2.154   Min.   : 0.00  
 1st Qu.:1975   1st Qu.:10251   1st Qu.: 30.925   1st Qu.: 2.00  
 Median :1988   Median :10500   Median : 53.828   Median : 4.00  
 Mean   :1988   Mean   :10500   Mean   : 59.653   Mean   : 4.99  
 3rd Qu.:2002   3rd Qu.:10750   3rd Qu.: 85.550   3rd Qu.: 8.00  
 Max.   :2015   Max.   :11000   Max.   :211.552   Max.   :20.00  

YR-ID获得我的4种汇总信息组合的最快方法是什么?现在,我正在使用选项1或2重复两次(一次用于保留,再次用于keep2(

您可以直接在j中的表达式中进行摘要:

# solution A: summarize in `.SD`:
system.time({
    test2 <- DT[keep == 1,
                .SD[, .(m = sum(ratio, na.rm = TRUE),
                        nmiss = sum(is.na(ratio)))],
                by = .(yr, id), verbose = T]
})
#    user  system elapsed 
#  22.359   0.439  22.561
# solution B: summarize directly in j:
system.time({
    test2 <- DT[keep == 1, .(m = sum(ratio, na.rm = T),
                             nmiss = sum(is.na(ratio))),
                by = .(yr, id), verbose = T]
})
#    user  system elapsed 
#   0.118   0.077   0.195
添加

verbose = T以显示两种方法之间的差异:

用于解决方案A:

lapply优化已打开,j不变为'.sd [,list(m = sum(比率,比率,na.rm = true(,nmiss = sum(is.na(比((]'gforce开启,左j不变

旧的平均优化已经开始,左J不变。

使每个组并运行J(gforce false(...J的结果是

命名列表。创建相同名称和每组再次结束。

当j = list(...(时,检测到任何名称,分组完成后,删除并放回效率,以提高效率。例如,使用j = transform((阻止了该加速(请考虑更改为:=(。此消息可能会在以后升级到警告。

收集不连续的组为54000组的0.058
评估(j(花了22.487,用于54000个电话22.521秒

用于解决方案B:

...

从位置查找小组大小(可以避免以保存RAM(... 0 sec lapply优化已开始,j不变为'列表(总和(比率,na.rm = t(,sum(is.na(比率((('

gforce已打开,左J不变

旧的均值优化已经开始,左J不变。使每个小组和运行j(gforce false(...收集不连续的组54000组评估(J(的0.027次数为0.079,用于54000个电话0.168秒

主要区别在于,B中的摘要被视为命名列表,当有很多组时(该数据54K组!(非常慢。对于此类型的类似基准,请参阅此。

第二部分(您的test3(:我们没有先通过keep = 1过滤列。因此,在nmiss中也计数keep != NA s。因此,NA s的计数不同。

最新更新