簡體   English   中英

使用R,data.table中的多個組合按組計算均值和nmiss

[英]Compute mean and nmiss by group with multiple combinations in R, data.table

我想在一個大型數據集的多個組組合中計算NA的平均值和計數。 用一些測試數據可能最容易解釋。 我在Macbook Pro上使用的是最新版本的R,並且使用了data.table包(數據量大,行數大於1M)。 (注意:我在發布此內容后注意到,我不小心對下面的“ m =”變量使用了sum()而不是mean()。我沒有對其進行編輯,因為我不想重新運行所有內容,並且認為那么重要)

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,ratio1 | 2。 具體來說,通過yr-id,我想使用keep和keep2(因此壓縮id2)來計算平均比率和ratio2。 我考慮過通過保持/保持2的計算比率和ratio2子集或通過keep * ratio,keep2 * ratio,keep * ratio2和keep2 * ratio2的矩陣乘法來進行設置。

首先,我這樣做的方式會得到正確的答案,但是很慢:

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.沒有獲得正確數量的NA(請參閱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重復了兩次(一次用於keep,另一次用於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(ratio,na.rm = TRUE),nmiss = sum(is.na(ratio)))]'GForce打開,j不變

舊的均值優化功能已啟用,j保持不變。

制作每個組並運行j(GForce FALSE)... j的結果是

命名列表。 為每個組一遍又一遍地創建相同的名稱是非常低效的。

當j = list(...)時,為了提高效率,會在分組完成后檢測,刪除並放回任何名稱。 例如,使用j = transform()可以防止加速(考慮更改為:=)。 此消息將來可能會升級為警告。

收集不連續的組54000個組耗時0.058s
eval(j)花費22.487秒進行了54000次通話22.521秒

對於解決方案B:

...

從位置查找組大小(可以避免以節省RAM)... 0秒啟用了優化,j不變為'list(sum(ratio,na.rm = T),sum(is.na(ratio)) )'

GForce已開啟,j保持不變

舊的均值優化功能已啟用,j保持不變。 制作每個組並運行j(GForce FALSE)...收集不連續的組花費了54000個組0.027秒eval(j)花費了54000個調用0.079秒0.168秒

主要區別在於B中的匯總被視為命名列表,當有許多組(此數據為54k組!)時,這非常慢。 對於這種類型的類似基准看到這一個

對於第二部分(您的test3):我們沒有首先通過keep = 1過濾列。 因此,其中keep !=那些NA也計入nmiss 因此, NA的數量不同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM