2017-04-22 9 views
0

大規模なデータセットとのいくつかのグループの組み合わせにおける平均とNAの数を計算したいと思います。これはおそらく、いくつかのテストデータで説明するのが最も簡単です。私はMacBook Proで最新バージョンのRを使用しています。また、data.tableパッケージもあります(データは1M行以上です)。 (注:これを投稿した後、私は誤って、以下の "m ="変数にmean()の代わりにsum()を使用していたことに気づきました。私はすべてを再実行したくないので編集しませんでした。 「がtはR、data.tableの複数の組み合わせを持つグループ別平均とnmissを計算する

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 

だから、私が持っているものの識別情報(年、ID、ID2)と私が要約したいデータがある)多くのことを重要だと思う:keep1 | 2、RATIO1 | 2。特にyr-idによって、私は平均とratio2をkeepとkeep2(したがってid2を圧縮する)を使って計算したいと思います。私はこれを、keep/keep2で計算比と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 

これは遅く、2、それは正しい数を取得していないで、私は一緒にすべての数式を配置することができます(私はratio*keep2ratio2*keepratio2*keep2に追加することもできます)が、1。 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を使用していること(キープのために、再びkeep2に一回)を2回繰り返し

答えて

1

あなたは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最適化は、上.SD [一覧(M =和「としてJ不変である(ラットIO、 na.rm = TRUE)、nmiss = SUM(is.na(比)))]」GForceが上にある、左のJ 変わらず

古いは、最適化がJまま、上であることを意味します。

各グループを作るとj(GForceのFALSE)を実行している... 、jの結果は

という名前のリストです。グループごとに同じ名前と を再度作成するのは非常に非効率です。

j = list(...)の場合、グループ化が完了した後で効率を上げるために、すべての名前が検出され、戻されます。 たとえば、j = transform()を使用すると、そのスピードアップを防ぐことができます( を=に変更することを検討してください)。このメッセージは将来警告にアップグレードされる可能性があります。

収集不連続グループは、溶液Bの場合は54000回のコールのために54000グループ
のeval(j)を取った22.487sため 22.521秒

を0.058sを取った:...

位置からグループサイズを見つける(RAMを節約するために避けることができる) ... 0秒lapply最適化がオン、jは 'list(sum(ratio、 na.rm = T)、合計(is.na(比)))」

GForceが上にある、左のJ変わらず

古い最適化を意味するがjまま、上にあります。

主な違いは、集計こと(GForceのFALSE)Jを実行している各グループを作ると ...収集不連続グループは54000グループにeval(J)のため 0.027sを取ったが、54000回のコールに対して0.079sを取っ 0.168秒

ですBの名前付きリストとして扱われますが、これは多くのグループがある場合には非常に遅いです(このデータの54kグループ!)。このタイプの同様のベンチマークについては、 this oneを参照してください。

2番目の部分(your3): 最初にkeep = 1で列をフィルタリングしませんでした。したがって、NAsの場合、keep !=nmissにも数えられます。したがって、NAの数は異なります。

+0

ブリリアント。私はまだここで詳細を学んでおり、冗長オプションについても知らなかった。 –

+0

パート2は何ですか:2番目の方法(test3)は機能しません。(私は思う)ratio = keepはNAを0にしても保持するからです。sum(is.na(ratios [keep] ))をサブセット比に変換し、is.na()関数を評価しますか?実際のデータには7つのバージョンの「保存」があり、これを7回繰り返して7つのデータセットを再マージする必要があるため、この2番目のアプローチを使用したいと思います。それはすべてのステップでそれを行うことがずっと良いでしょう。 –

+0

@JesseBlocherについては、「NA」が異なる理由についての編集を参照してください。私は、複数のキープとレシオのペアのためのループよりも、これを一度に行う方法を思いついたわけではありません。 – mt1022

関連する問題