提问者:小点点

如何将数据帧中的每列乘以每列不同的值[重复]


考虑以下数据框架

   x y z
 1 0 0 0
 2 1 0 0
 3 0 1 0
 4 1 1 0
 5 0 0 1
 6 1 0 1
 7 0 1 1
 8 1 1 1
 -------
 x 4 2 1  <--- vector to multiply by 
 

我想将每列乘以一个单独的值,例如c(4,2,1)。给出:

   x y z
 1 0 0 0
 2 4 0 0
 3 0 2 0
 4 4 2 0
 5 0 0 1
 6 4 0 1
 7 0 2 1
 8 4 2 1

代码:

pw2 <- c(4, 2, 1)
s01  <- seq_len(2) - 1
df  <- expand.grid(x=s01, y=s01, z=s01)
df

for (d in seq_len(3)) df[,d] <- df[,d] * pw2[d]
df

问题:找到一个没有for循环的矢量化解决方案(在base R中)。


共3个答案

匿名用户

使用扫描在数据框的边距上应用函数:

sweep(df, 2, pw2, `*`)

或使用col

df * pw2[col(df)]

输出

  x y z
1 0 0 0
2 4 0 0
3 0 2 0
4 4 2 0
5 0 0 1
6 4 0 1
7 0 2 1
8 4 2 1

对于大数据帧,检查e::TRA拼贴,这比任何其他答案快10倍(参见基准测试):

collapse::TRA(df, pw2, "*")

基准:

bench::mark(sweep = sweep(df, 2, pw2, `*`),
            col = df * pw2[col(df)],
            '%*%' = setNames(
              as.data.frame(as.matrix(df) %*% diag(pw2)), 
              names(df)
            ), 
            TRA = collapse::TRA(df, pw2, "*"), 
            mapply = data.frame(mapply(FUN = `*`, df, pw2)),
            apply = t(apply(df, 1, \(x) x*pw2)), 
            t = t(t(df)*pw2), check = FALSE,
            )

# A tibble: 7 × 13
  expression      min  median itr/s…¹ mem_al…² gc/se…³ n_itr  n_gc total…⁴
  <bch:expr> <bch:tm> <bch:t>   <dbl> <bch:by>   <dbl> <int> <dbl> <bch:t>
1 sweep       346.7µs 382.1µs   2427.   1.23KB   10.6   1141     5 470.2ms
2 col         303.1µs 330.4µs   2760.     784B    8.45  1307     4 473.5ms
3 %*%          72.8µs  77.9µs  11861.     480B   10.6   5599     5 472.1ms
4 TRA             5µs   5.5µs 167050.       0B   16.7   9999     1  59.9ms
5 mapply      117.6µs 127.9µs   7309.     480B   10.6   3442     5 470.9ms
6 apply       107.8µs 117.9µs   7887.   6.49KB   12.9   3658     6 463.8ms
7 t            55.3µs  59.7µs  15238.     720B    8.13  5620     3 368.8ms

匿名用户

dfpw2转换为矩阵,使用%*%矩阵乘法运算符,然后转换回数据帧。这将剥离列名,因此包装在setNames()中以保留它们。

setNames(
  as.data.frame(as.matrix(df) %*% diag(pw2)), 
  names(df)
)
  x y z
1 0 0 0
2 4 0 0
3 0 2 0
4 4 2 0
5 0 0 1
6 4 0 1
7 0 2 1
8 4 2 1

匿名用户

另一个使用应用和转置的选项,如下所示:

pw2 <- c(4, 2, 1)
t(apply(df, 1, \(x) x*pw2))
#>   x y z
#> 1 0 0 0
#> 2 4 0 0
#> 3 0 2 0
#> 4 4 2 0
#> 5 0 0 1
#> 6 4 0 1
#> 7 0 2 1
#> 8 4 2 1

使用reprex v2.0.2创建于2023-04-10