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

时间:2013-08-22 14:09:38

标签: r dataframe

我正在尝试将数据框df乘以向量v,以便产品是数据框,i行由{df[i,]*v给出1}}。我可以这样做,例如,

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)

但仍然需要{_ 1}}等不可读的结构 我怎样才能在这里找到一个高效优雅的解决方法?

6 个答案:

答案 0 :(得分:27)

这也有效:

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

在该解决方案中,您正在利用data.framelist类型的事实,因此您可以迭代dfv的元素与mapply同时进行。

不幸的是,您可以从mapply输出的内容有限:简单listmatrix。如果您的数据量巨大,这可能会更有效:

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

因为它会将其转换为list,转换为data.frame效率更高。

答案 1 :(得分:11)

如果您正在寻找速度和记忆效率 - 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

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

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

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

答案 2 :(得分:8)

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

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

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

或者编写一个很好的函数,将非R风格的代码包装成R-Stylish。

答案 3 :(得分:3)

出了什么问题
t(apply(df, 1, function(x)x*v))

答案 4 :(得分:2)

library(purrr)

map2_dfc(df, v, `*`)

基准

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

eddi = function(dt) { for (i in seq_along(dt)) dt[, (i) := dt[[i]] * v[i]]; dt }
arun = function(df) { df * matrix(v, ncol=ncol(df), nrow=nrow(df), byrow=TRUE) }
nograpes = function(df) { data.frame(mapply(`*`,df,v,SIMPLIFY=FALSE)) }
ryan = function(df) {map2_dfc(df, v, `*`) }
library(microbenchmark)
microbenchmark(
  eddi(copy(dt))
  , arun(copy(dt))
  , nograpes(copy(dt))
  , ryan(copy(dt))
  , times = 100)


# Unit: milliseconds
# expr                     min        lq      mean    median        uq      max neval
# eddi(copy(dt))      8.367513  11.06719  24.26205  12.29132  19.35958 171.6212   100
# arun(copy(dt))     94.031272 123.79999 186.42155 148.87042 251.56241 364.2193   100
# nograpes(copy(dt))  7.910739  10.92815  27.68485  13.06058  21.39931 172.0798   100
# ryan(copy(dt))      8.154395  11.02683  29.40024  13.73845  21.77236 181.0375   100

答案 5 :(得分:1)

我认为最快的方式(没有测试data.table)是data.frame(t(t(df)*v))

我的测试:

testit <- function(nrow, ncol)
{
    df <- as.data.frame(matrix(rnorm(nrow*ncol),nrow=nrow,ncol=ncol))

    v <- runif(ncol)

    r1 <- data.frame(t(t(df)*v))
    r2 <- data.frame(mapply(`*`,df,v,SIMPLIFY=FALSE))
    r3 <- df * rep(v, each=nrow(df))

    stopifnot(identical(r1, r2) && identical(r1, r3))

    microbenchmark(data.frame(t(t(df)*v)), data.frame(mapply(`*`,df,v,SIMPLIFY=FALSE)), df * rep(v, each=nrow(df)))
}

结果

> 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