将命名向量乘以数据帧的正确方法是什么?

时间:2016-09-26 21:14:14

标签: r dataframe

与此SO question类似,将命名向量乘以数据帧的正确方法是什么,以便每行乘以向量的对应元素?

df <- data.frame(A=1:5, B=2:6)
v <- c(2, 0)
names(v) <- c("B", "A")

我想要以下输出:

   A  B
1  0  4
2  0  6
3  0  8
4  0 10
5  0 12

其他问题的建议解决方案都没有将列名与向量名匹配。例如,

dt <- data.table(df)
for (i in seq_along(dt))
    dt[, i := dt[[i]] * v[i], with = F]

dt
    A B
1:  2 0
2:  4 0
3:  6 0
4:  8 0
5: 10 0

我可以通过重新排序v来实现,但我想知道是否有更好的方法来做到这一点:

v <- v[colnames(df)]

4 个答案:

答案 0 :(得分:2)

我们可以使用lapply,然后cbind

来遍历名称
res <- do.call(cbind, 
               lapply(names(df), function(i){
                 df[i] * v[i]
               }))


class(res)
# [1] "data.frame"
res
#   A  B
# 1 0  4
# 2 0  6
# 3 0  8
# 4 0 10
# 5 0 12

答案 1 :(得分:2)

这个怎么样:

r <- mapply('*', df, v[names(df)])
# or equivalently: mapply(function(x,y) x*y, df, v[names(df)])

#     A  B
#[1,] 0  4
#[2,] 0  6
#[3,] 0  8
#[4,] 0 10
#[5,] 0 12

v[names(df)]将按照与df中相同的顺序给出向量元素,因此可以说是列名相应的。

如果您想将r作为数据框,请执行as.data.frame(r)

这来自?mapply

  

mapply是一个多元版本的sapply。 mapply将FUN应用于每个...参数的第一个元素,第二个元素,第三个元素,等等。如有必要,可以回收论据。

我们的设置中

FUN*

答案 2 :(得分:1)

您可以执行以下操作(转置data.frame,乘以有序向量,然后再转置):

  as.data.frame(t(t(df)*v[colnames(df)]))

以下是更大数据框架的一些基准:(f1是@ zx8754的函数,f2是@m0h3n的函数)

df <- data.frame(A=1:5000, B=2:5001)
v <- c(2, 0)
names(v) <- c("B", "A")

library(microbenchmark)

f1 <- function(){
  do.call(cbind, 
          lapply(names(df), function(i){
            df[i] * v[i]
          }))
}

f2 <- function(){
  as.data.frame(mapply('*', df, v[names(df)]))
}

f3 <- function(){
  as.data.frame(t(t(df)*v[colnames(df)]))
}

microbenchmark(f1(), f2(), f3())

Unit: microseconds
 expr      min        lq      mean    median        uq      max neval cld
 f1()  594.394  663.9595  711.3634  690.8815  748.8425 1022.605   100  b 
 f2() 2428.762 2618.7460 2701.1528 2669.4355 2730.8070 3904.354   100   c
 f3()  251.776  361.7550  401.8032  381.8825  418.6225  793.604   100 a 

答案 3 :(得分:0)

如果数据框中的变量多于向量中的元素,则可能需要使用@jav答案的扩展版本:

library(magrittr) 
df %>% 
  select(one_of(vars)) %$% 
  as.data.frame(t(t(.)*multiplier[vars])) %>% 
  bind_cols(df %>% select(-one_of(vars))) 

或者,你可以使用map2_df包中的purrr功能来完成腿部工作(我无耻地借用@akrun's answer给我(事实证明){{3}这里)。

library(purrr)
df %>% 
  select(one_of(vars)) %>% 
  map2_df(multiplier[vars], ~ .x * .y)  %>%
  bind_cols(df %>% select(-one_of(vars))) 

如果您热衷于保留变量的原始顺序,只需将%>% select(one_of(names(df)))添加到其中任何一个。

在性能方面,这两者看起来几乎相同:

f4 <- function(){
  df %>% 
    select(one_of(vars)) %$% 
    as.data.frame(t(t(.)*multiplier[vars])) %>% 
    bind_cols(df %>% select(-one_of(vars))) 
  }

f5 <- function(){
  df %>% 
    select(one_of(vars)) %>% 
    map2_df(multiplier[vars], ~ .x * .y)  %>%
    bind_cols(df %>% select(-one_of(vars))) 
}

microbenchmark(f4(), f5())

Unit: milliseconds
 expr      min       lq     mean   median       uq      max neval
 f4() 1.142170 1.178752 1.320680 1.197293 1.227915 2.858073   100
 f5() 1.155081 1.180077 1.248928 1.206396 1.227915 2.647517   100