2013-08-22 114 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%。 –

相关问题