2013-08-22 322 views
16

我試圖將數據幀df乘以一個向量v,以便產品是一個數據幀,其中i行由df[i,]*v給出。我能做到這一點,例如,通過用矢量乘數據幀的正確方法是什麼?

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 

我相信必須有一個更R型方法(和一個非常簡單的!),但沒有一樣是在我的腦海。我甚至想是這樣

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

但儘管如此,非可讀的結構類似as.data.frame(t(.))是必需的。
如何在此找到高效優雅的解決方法?

+3

爲什麼它需要一個data.frame?如果你有所有的數字元素,通常使用矩陣更有意義。 –

回答

21

這工作太:

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

在這種解決方案,您正在利用的事實,data.frame是一種類型的list,這樣你就可以遍歷兩個dfv在同一時間的元素與mapply

不幸的是,您在mapply輸出的內容有限:簡單的listmatrix。如果您的數據是巨大的,這將可能是更有效的:

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

,因爲這將其轉換爲list,這是更有效地轉換爲data.frame

+0

這是一段很棒的代碼,它看起來也是最有效的。與我的解決方案相比,代碼中不太自我解釋,但非常整潔。 +1進一步優化! – tonytonov

+0

@阿倫我認爲你是對的,艾迪的答案似乎表明它慢得多。矩陣生成可能比您想象的要長? – 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)))'which比@nograpes'answer'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 

由於阿倫在評論中指出的,還可以使用set功能從data.table包要做到這一點在data.frame -place修改的還有:

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

這當然也適用於data.table的,可能是顯著更快,如果列數很大。

+1

+1不錯!該文檔指出,使用'set'和'for-loop'會更快,因爲沒有'[.data.table'的開銷。但是,在這裏,我不認爲它更快..任何想法?另外,'set'可以和'data.frame'一起使用。您不必轉換爲'data.table'(並通過引用進行分配)! – Arun

+0

有關集合的好處,但是因爲我認爲列數很少,所以我不認爲循環vs集合會有所作爲(如果列的數量足夠大,我認爲'data.table'在這一點上不再是一個好的數據結構);在我的世界中也沒有轉換到'data.table',因爲一切都以'data.table'開始;) – eddi

+0

是的。我的意思是(關於*任何想法*)是,'set'是*較慢* ...我不能解釋爲什麼它更慢... – 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

@eddi,有趣。但是兩次移位仍然與1e6的mapply順序相同。行其實它在我的跑步中快了大約5%。 –

相關問題