通过向量乘以数据帧的正确方法是什么?

ton*_*nov 21 r dataframe

我正在尝试将数据帧乘以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
Run Code Online (Sandbox Code Playgroud)

我确信必须有更多R风格的方法(而且非常简单!),但没有任何想法.我甚至尝试过类似的东西

apply(df, MARGIN=1, function(x) x*v)
Run Code Online (Sandbox Code Playgroud)

但仍然as.data.frame(t(.))需要不可读的结构.
如何在这里找到高效优雅的解决方法?

nog*_*pes 27

这也有效:

data.frame(mapply(`*`,df,v))
Run Code Online (Sandbox Code Playgroud)

在该解决方案,您正在利用的事实,data.frame是一个类型的list,所以你可以遍历的两个元素df,并v在同一时间mapply.

不幸的是,你可以输出的内容有限mapply:简单list或者a matrix.如果您的数据很大,这可能会更有效:

data.frame(mapply(`*`,df,v,SIMPLIFY=FALSE))
Run Code Online (Sandbox Code Playgroud)

因为它会将其转换为a list,转换为a更有效data.frame.


edd*_*ddi 12

如果你正在寻找速度和记忆效率 - data.table救援:

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

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


eddi = function(dt) { for (i in seq_along(dt)) dt[, (i) := dt[[i]] * v[i]] }
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      mean    median        uq       max neval
#     eddi(copy(dt))  23.01106  24.31192  26.47132  24.50675  28.87794  34.28403    10
#     arun(copy(dt)) 337.79885 363.72081 450.93933 433.21176 516.56839 644.70103    10
# nograpes(copy(dt))  19.44873  24.30791  36.53445  26.00760  38.09078  95.41124    10
Run Code Online (Sandbox Code Playgroud)

正如Arun在评论中指出的那样,也可以使用包中的set函数data.table对其进行就地修改data.frame:

for (i in seq_along(df))
  set(df, j = i, value = df[[i]] * v[i])
Run Code Online (Sandbox Code Playgroud)

这当然也适用于data.table,如果列数很大,可能会明显更快.


Spa*_*man 8

允许您将向量与矩阵组合在一起的语言必须在某些时候做出决定矩阵是行主要还是列主要有序.原因:

> df * v
  A  B
1 0  4
2 4  0
3 0  8
4 8  0
5 0 12
Run Code Online (Sandbox Code Playgroud)

是因为R首先按列操作.做双重转置技巧颠覆了这一点.对不起,如果这只是解释你所知道的,但我不知道另一种方法,除了明确扩展v到相同大小的矩阵.

或者编写一个很好的函数,将非R风格的代码包装成R-stylish的东西.