2016-12-09 5 views
1

私の質問は比較的単純で、Rでそれを行う方法をすでに見つけましたが、教育的目的のためにもっと巧妙な方法を探しています。私の解決策はループを使用し、私は常にループをできるだけ避けるようにしています。ループ方法についてインデックスを使って複数の列を集約する巧妙な方法

ind <- c(1,1,1,2,2,1,3,3,3,4) 

ザ・:

set.seed(1) 
tb <- matrix(round(runif(40,0,5)),4,10) 
tb 
    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] 
[1,] 1 1 3 3 4 5 1 4 2  4 
[2,] 2 4 0 2 5 1 2 2 1  1 
[3,] 3 5 1 4 2 3 0 2 4  4 
[4,] 5 3 1 2 4 1 2 3 3  2 

私はindice(またはインデックス)ベクトル以下、それらをrowSumming列の異なるグループを組み合わせたい:

私は、テーブル(またはマトリックス)を持っています:

res.ls <- NULL 
for(i in unique(ind)) { 
    res.ls[[i]] <- rowSums(subset(tb,select=ind==i)) 
} 
do.call("cbind",res.ls) 
    [,1] [,2] [,3] [,4] 
[1,] 10 7 7 4 
[2,] 7 7 5 1 
[3,] 12 6 6 4 
[4,] 10 6 8 2 

私は確かにそれを行うにはスマートな方法があります。何かヒント?

+2

メモリを2回転置できる場合は、 '?rowsum'を参照してください。 btw 'set.seed'は呼び出される必要があり、代入されない関数です(' set.seed(1) ') –

+0

転置してからgroup_byを集計してください!申し訳ありませんが、上記のコメントは表示されませんでした。同じ思想 –

+0

@alexis_laz、メモリに関するinterresting point。 set.seedの場合、フランス語では「脳のバブル」と言います...ごめんなさい... – Bastien

答えて

3

あなたの結果はあなたの入力に一致するようにそれはいないようですが、あなたのロジック以下、あなたがrowsumを使用することができますが、元の行列の転置バージョンについて:

t(rowsum(t(tb), ind)) 

#  1 2 3 4 
#[1,] 15 4 6 3 
#[2,] 17 8 5 3 
#[3,] 11 4 5 3 
#[4,] 12 2 6 4 

またはON apply()機能を使用しますrowsum()を持つすべての行が二回移調避けるために:

t(apply(tb, 1, rowsum, ind)) 
#  [,1] [,2] [,3] [,4] 
#[1,] 15 4 6 3 
#[2,] 17 8 5 3 
#[3,] 11 4 5 3 
#[4,] 12 2 6 4 

あなたがデータフレームを使用している場合、それは行列にデータフレームを変換しないように、これはより効率的かもしれません:

df <- data.frame(tb) 
do.call(cbind, lapply(split.default(df, ind), rowSums)) # use split.default to split data 
# frames as multiple data frames by columns and apply rowSums to each sub data frame 

#  1 2 3 4 
#[1,] 15 4 6 3 
#[2,] 17 8 5 3 
#[3,] 11 4 5 3 
#[4,] 12 2 6 4 
+0

結果が一致しない場合、あなたは正しい@Psidomです。コピー貼り付けと設定。問題です。私はそれを修正しました。あなたの命題に対してはうまくいくかもしれませんが、私はdata.frameを使用していたために移調するのは嫌です(正当な理由があってもなくても)。 – Bastien

+0

データフレームがある場合は、それをあなたの質問に反映させたいでしょうか?より良いデータフレームのソリューションが必要です。 – Psidom

+0

ホ...私はあなたの編集した適用オプションが本当に好きです!私はrowsum(vs rowSums)関数を知らなかった。 – Bastien

1

ここsplitrowSumsを使用して第二の方法があります:

sapply(split(tb, rep(ind, each=nrow(tb))), function(x) rowSums(matrix(x, nrow(tb)))) 
     1 2 3 4 
[1,] 10 7 7 4 
[2,] 7 7 5 1 
[3,] 12 6 6 4 
[4,] 10 6 8 2 

データ

set.seed(1) 
tb <- matrix(round(runif(40,0,5)),4,10) 
ind <- c(1,1,1,2,2,1,3,3,3,4) 
+0

私はあなたの答えのスピードをテストしましたが(下を参照)、うまくいっていますが、スピードは問題あります。 @Imo – Bastien

+0

うわー。それははっきりした違いです。スピードの比較をしていただきありがとうございます。私はこの方法で巨大なボトルネックがどこにあるのか少し考えなければならないでしょう。 – lmo

2

私は答えとしてこれを投稿すると仮定だわからないんだけど、私はEFFICをテストしたかったです私のループ、@ Psidom 3の回答、@ Imoの答え)のすべての5つの方法の有効性。だから、ループがその遅いとdata.frameではないようです

all(res1==res2) 
[1] TRUE 

all(res1==res3) 
[1] TRUE 

all(res1==res4) 
[1] TRUE 

all(res1==res5) 
[1] TRUE 

set.seed(1) 
tb <- matrix(round(runif(40000000,0,15)),40000,1000) 
ind <- round(runif(1000,1,300)) 

とすべてのオプション走った:私は私のデータが大きく作られてきたと

res.ls <- NULL 
system.time({ 
for(i in unique(ind)) { 
    res.ls[[i]] <- rowSums(subset(tb,select=ind==i)) 
} 
res1 <- do.call("cbind",res.ls) 
}) 
utilisateur  système  écoulé 
     0.60  0.04  0.64 

system.time(
res2 <- t(rowsum(t(tb), ind)) 
) 
utilisateur  système  écoulé 
     0.68  0.02  0.70 

system.time(
res3 <- t(apply(tb, 1, rowsum, ind)) 
) 
utilisateur  système  écoulé 
     20.01  0.21  20.24 

system.time(
res4 <- sapply(split(tb, rep(ind, each=nrow(tb))), function(x) rowSums(matrix(x, nrow(tb)))) 
) 
utilisateur  système  écoulé 
     58.68  0.42  59.13 


df1 <- data.frame(tb) 
system.time(
res5 <- do.call(cbind, lapply(split.default(df1, ind), rowSums))) 
utilisateur  système  écoulé 
     0.3   0.0   0.3 

をバージョンが最高です。面白い結果!

+1

良いベンチマーク。私は、行列を2回転置することを避けても、第2の方法がいかに効率的であるかに驚いています。おそらく最後のオプションのためのより良い方法は 'as.data.frame(lapply(split.default(df1、ind)、rowSums))'です。 – Psidom

+1

私は@Psidomの最後のコメントをテストするためにデータのサイズを2倍にしました。新しいdata.frameメソッドでは0.62、古いものでは0.67でした。いくつかの文脈では役に立つかもしれませんが、私のためにはありません! – Bastien

関連する問題