与此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)]
答案 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