2013-08-22 11 views
16

データフレームdfにベクトルvを乗算しようとしています。その結果、製品はデータフレームであり、i番目の行はdf[i,]*vです。私が(!と非常にシンプルなもの)私はもっとR-スタイルアプローチが存在しなければならないと確信していますデータフレームにベクトルを乗算する正しい方法は何ですか?

df <- data.frame(A=1:5, B=2:6); v <- c(0,2) 
as.data.frame(t(t(df) * v)) 
    A B 
1 0 4 
2 0 6 
3 0 8 
4 0 10 
5 0 12 

により、例えば、これを行うことができますが、何も私の心に来ることはありません。私も

apply(df, MARGIN=1, function(x) x*v) 

のようなものを試してみましたが、それでも、as.data.frame(t(.))のような非可読構造が必要とされています。
ここで効率的でエレガントな回避策を見つけるにはどうすればよいですか?

+3

なぜそれがdata.frameする必要がありますか。すべての数値要素がある場合は、一般に行列を使用する方が理にかなっています。 –

答えて

21

これはあまりにも動作します。そのソリューションで

data.frame(mapply(`*`,df,v)) 

を、あなたはdata.framelistのタイプであるという事実を利用しているので、あなたは同時にdfvの要素の両方を反復処理することができますmapplyである。

残念ながら、mapplyから出力できるものは、簡略listまたはmatrixと限られています。あなたのデータが巨大である場合、これはおそらく、より効率的である:

data.frame(mapply(`*`,df,v,SIMPLIFY=FALSE)) 

それはdata.frameに変換する方が効率的である、listに変換しますので。

+0

これはコードの素晴らしい行であり、最も効率的であるようです。私の解決策と比較して、コードでは自己説明的ではありませんが、非常にきれいです。 +1のさらなる最適化! – tonytonov

+0

@Arun私はあなたが正しいと思った、eddiの答えは、それがずっと遅いことを示すようだ。おそらく、行列の生成に時間がかかりますか? – nograpes

7

ベクトルを行列と組み合わせることができる言語は、行列が行優先か列主優先かを決定する必要があります。理由は:

> df * v 
    A B 
1 0 4 
2 4 0 
3 0 8 
4 8 0 
5 0 12 

は、Rが最初に列を操作するためです。ダブルトランスポーズトリックを実行すると、これが無効になります。申し訳ありませんが、あなたが知っていることだけを説明していますが、明示的に同じサイズの行列にvを展開する以外は、別の方法ではわかりません。

また、非常にRスタイルでないコードをRスタイリッシュなものにラップする素敵な関数を書いてください。

+0

Rの柔軟性は私たちがそれを愛していることです、それは本当です。コメントをいただきありがとうございます。コードの可読性を維持するために、これを関数にラップすることが解決策だと思います。 – tonytonov

3

いただきまし間違っ

t(apply(df, 1, function(x)x*v)) 

と?

+0

これはうまくいくようです。 – Mayou

+0

これはdata.frameの代わりに行列を返します。したがって、 'data.frame(t(apply(df、1、function(x)x * v)))')となります。 @nograpesの答え 'data.frame(mapply(' * '、df、v))'より簡潔です。 – Rob

+0

* mapply *のバージョンは、より速く、クールだと思われます。 – Fernando

9

あなたはスピードとメモリ効率を探している場合 - 救助にdata.table

library(data.table) 
dt = data.table(df) 

for (i in seq_along(dt)) 
    dt[, i := dt[[i]] * v[i], with = F] 


eddi = function(dt) { for (i in seq_along(dt)) dt[, i := dt[[i]] * v[i], with = F] } 
arun = function(df) { df * matrix(v, ncol=ncol(df), nrow=nrow(df), byrow=TRUE) } 
nograpes = function(df) { data.frame(mapply(`*`,df,v,SIMPLIFY=FALSE)) } 

N = 1e6 
dt = data.table(A = rnorm(N), B = rnorm(N)) 
v = c(0,2) 

microbenchmark(eddi(copy(dt)), arun(copy(dt)), nograpes(copy(dt)), times = 10) 
#Unit: milliseconds 
#    expr  min   lq  median   uq  max neval 
#  eddi(copy(dt)) 17.46796 19.23358 23.53997 26.03665 30.
#  arun(copy(dt)) 1014.36108 1375.66253 1461.46489 1527.66639 1721.96316 10 
# nograpes(copy(dt)) 92.14517 109.30627 158.42780 186.32240 188.01758 10 

アルンはコメントで指摘したように、一もこの中に操作を行うためにdata.tableパッケージからset機能を使用することができますdata.frameのインプレース修正:s「は、同様のコースの

for (i in seq_along(df)) 
    set(df, j = i, value = df[[i]] * v[i]) 

これもdata.tableのために働く」の数と列数が多い場合に大幅に高速である可能性があります。

+1

+1いいね!ドキュメントには 'for-loop'で' set'を使う方が速くなると書かれています。なぜなら '[.data.table'のためにオーバーヘッドがないからです。しかし、ここでは、私はそれが速いとは思わない..任意のアイデア?また、 'set'は' data.frame'でも使用できます。 'data.table'に変換する必要はありません。 – Arun

+0

良い点は約ですが、柱の数は少ないと思いますので、ループの対は違いはありません(列の数が問題になるほど大きければ'data.table'はそれ以上良いデータ構造ではありません)。私の世界では 'data.table'への変換はありません。すべてが' data.table'です。) – eddi

+0

はい。私が意味していたことは、「設定されています」が遅かったということです。なぜそれが遅いのか説明できません。 – Arun

1

私は最も速い方法(data.tableのテストなし)はdata.frame(t(t(df)*v))だと思います。

私のテスト:

結果

> set.seed(1) 
> 
> testit(100,100) 
Unit: milliseconds 
              expr  min  lq median  uq  max neval 
         data.frame(t(t(df) * v)) 2.297075 2.359541 2.455778 3.804836 33.05806 100 
data.frame(mapply(`*`, df, v, SIMPLIFY = FALSE)) 9.977436 10.401576 10.658964 11.762009 15.09721 100 
        df * rep(v, each = nrow(df)) 14.309822 14.956705 16.092469 16.516609 45.13450 100 
> testit(1000,10) 
Unit: microseconds 
              expr  min  lq median  uq  max neval 
         data.frame(t(t(df) * v)) 754.844 805.062 844.431 1850.363 27955.79 100 
data.frame(mapply(`*`, df, v, SIMPLIFY = FALSE)) 1457.895 1497.088 1567.604 2550.090 4732.03 100 
        df * rep(v, each = nrow(df)) 5383.288 5527.817 5875.143 6628.586 32392.81 100 
> testit(10,1000) 
Unit: milliseconds 
              expr  min  lq median  uq  max neval 
         data.frame(t(t(df) * v)) 17.07548 18.29418 19.91498 20.67944 57.62913 100 
data.frame(mapply(`*`, df, v, SIMPLIFY = FALSE)) 99.90103 104.36028 108.28147 114.82012 150.05907 100 
        df * rep(v, each = nrow(df)) 112.21719 118.74359 122.51308 128.82863 164.57431 100 
+0

あなたは小さなデータを見ています(ループを行わない限り、それらの違いは関係ありません)。 'testit(100000,10)' - スーパーのようなものではなく、データのような形をしているのが普通です。 – eddi

+0

@面白いです。しかし、2回転位することは、1e6の場合と同じ順番です。行実際には、私の実行では約5%高速です。 –

関連する問題