将数据帧的每一列乘以向量的最有效方法是什么?
e.g。数据框(df
)包含以下列(col1, col2, col3, col4
),向量(v
)具有以下元素(v1,v2,v3
)。
我希望输出为:col2*v1, col3*v2, col4*v3
我一直在尝试df[c(2:4)] * c(v1,v2,v3)
,但似乎向量的元素并没有使每列的每一行都成倍增加。
答案 0 :(得分:3)
您可以使用Map
。这是一个例子
> ( df <- data.frame(a = letters[1:3], x = 1:3, y = 4:6, z = 7:9) )
# a x y z
# 1 a 1 4 7
# 2 b 2 5 8
# 3 c 3 6 9
> v <- c(5, 10, 15)
> cbind(df[1], Map(`*`, df[-1], v))
# a x y z
# 1 a 5 40 105
# 2 b 10 50 120
# 3 c 15 60 135
在此示例中,
x
乘以v[1]
(5)y
乘以v[2]
(10)z
乘以v[3]
(15)cbind
用于将未使用的列a
附加到我们操作的列答案 1 :(得分:3)
您可以尝试(使用Richard Scriven的答案中的df
和v
):
df[-1] <- t(t(df[-1]) * v)
df
# a x y z
# 1 a 5 40 105
# 2 b 10 50 120
# 3 c 15 60 135
将矩阵乘以向量时,它会按列相乘。由于您希望将行乘以向量,因此我们使用df[-1]
转置t
,乘以v
,然后使用t
进行转置。
似乎这种方法在Map
方法的基准测试中略有优势,并且优于sweep
的显着优势:
library(microbenchmark)
rscriven <- function(df, v) cbind(df[1], Map(`*`, df[-1], v))
josilber <- function(df, v) cbind(df[1], t(t(df[-1]) * v))
dardisco <- function(df, v) cbind(df[1], sweep(df[-1], MARGIN=2, STATS=v, FUN="*"))
df2 <- cbind(data.frame(rep("a", 1000)), matrix(rnorm(100000), nrow=1000))
v2 <- rnorm(100)
all.equal(rscriven(df2, v2), josilber(df2, v2))
# [1] TRUE
all.equal(rscriven(df2, v2), dardisco(df2, v2))
# [1] TRUE
microbenchmark(rscriven(df2, v2), josilber(df2, v2), dardisco(df2, v2))
# Unit: milliseconds
# expr min lq median uq max neval
# rscriven(df2, v2) 5.276458 5.378436 5.451041 5.587644 9.470207 100
# josilber(df2, v2) 2.545144 2.753363 3.099589 3.704077 8.955193 100
# dardisco(df2, v2) 11.647147 12.761184 14.196678 16.581004 132.428972 100
感谢@thelatemail指出{100}大数据框的Map
方法速度更快:
df2 <- cbind(data.frame(rep("a", 10000)), matrix(rnorm(10000000), nrow=10000))
v2 <- rnorm(1000)
microbenchmark(rscriven(df2, v2), josilber(df2, v2), dardisco(df2, v2))
# Unit: milliseconds
# expr min lq median uq max neval
# rscriven(df2, v2) 75.74051 90.20161 97.08931 115.7789 259.0855 100
# josilber(df2, v2) 340.72774 388.17046 498.26836 514.5923 623.4020 100
# dardisco(df2, v2) 928.81128 1041.34497 1156.39293 1271.4758 1506.0348 100
您似乎需要进行基准测试,以确定哪种方法对您的应用来说最快。
答案 2 :(得分:2)
不是那么快,但更灵活:
sweep(df[-1], MARGIN=2, STATS=v, FUN="*")
答案 3 :(得分:1)
简单&#39;申请&#39;函数也可以在这里使用,按行读取:
df[-1]= (t(apply(df[-1],1, FUN=function(x)x*v)))
df
a x y z
1 a 5 40 105
2 b 10 50 120
3 c 15 60 135