purrr 的解决方案可能如下
df.result <- map2(.x = lapply(seq_len(nrow(responses)), function(i) responses[i,]),
.y = lapply(seq_len(nrow(order)), function(i) order[i,]),
.f = ~ .x[.y])
do.call("rbind", df.result)
在此代码中,.x 和 .y 是向量列表,即行列表(在此帖子之后 https://stackoverflow.com/a/6821395/11086911)。然后将map2 的输出聚合到具有do.call 和rbind 的矩阵中。
如果有人对这与其他解决方案的比较感到好奇,这里有一个比较。
library(microbenchmark)
library(purrr)
set.seed(42) # For reproducibility purposes
# Comparison with given data
order.matrix <- matrix(c("Anger", "Happy", "Sad", "Happy", "Sad","Anger", "Sad", "Anger", "Happy"),
ncol=3,
byrow=TRUE)
df.responses <- matrix(c(1, 2, 3, 3, 2, 0, 9, 2, 1),
ncol=3,
byrow=TRUE)
colnames(df.responses) <- c("Anger", "Happy", "Sad")
solForLoop <- function(order, responses) {
df.result <- responses
colnames(df.result) <- paste0("V", 1:ncol(responses))
for (i in 1:nrow(order)) {
df.result[i,] <- responses[i,order[i,]]
}
df.result
}
solmApply <- function(order, responses) {
t(mapply(FUN = function(x, y) x[y],
as.data.frame(t(responses)),
as.data.frame(t(order)),
USE.NAMES = F
))
}
solPurrr <- function(order, responses) {
df.result <- map2(.x = lapply(seq_len(nrow(responses)), function(i) responses[i,]),
.y = lapply(seq_len(nrow(order)), function(i) order[i,]),
.f = ~ .x[.y])
do.call("rbind", df.result)
}
microbenchmark::microbenchmark(
solForLoop(order.matrix, df.responses),
solmApply(order.matrix, df.responses),
solPurrr(order.matrix, df.responses),
times = 1000L,
check = "equivalent"
)
# Unit: microseconds
# expr min lq mean median uq max neval
# solForLoop(order.matrix, df.responses) 8.601 11.101 15.03331 15.9010 17.3020 62.002 1000
# solmApply(order.matrix, df.responses) 313.801 346.701 380.32261 357.7510 374.2010 14322.900 1000
# solPurrr(order.matrix, df.responses) 49.900 61.301 70.68950 70.7015 75.8015 190.700 1000
鉴于数据来自问卷调查,我假设order.matrix 行中的每个值只能出现一次。对于具有相同 3 列但 100 000 行的矩阵,我们发现
# Comparison for big data
order.matrix.big <- as.matrix(sample_n(as.data.frame(order.matrix), 100000, replace = TRUE))
df.responses.big <- as.matrix(sample_n(as.data.frame(df.responses), 100000, replace = TRUE))
microbenchmark::microbenchmark(
solForLoop(order.matrix.big, df.responses.big),
solmApply(order.matrix.big, df.responses.big),
solPurrr(order.matrix.big, df.responses.big),
times = 100L,
check = "equivalent"
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# solForLoop(order.matrix.big, df.responses.big) 110.2585 130.0916 163.3382 142.4249 167.7584 514.7262 100
# solmApply(order.matrix.big, df.responses.big) 4669.8815 4866.6152 5232.1814 5160.2967 5385.5000 6568.1718 100
# solPurrr(order.matrix.big, df.responses.big) 441.6195 502.0853 697.7207 669.4963 871.9122 1218.6721 100
因此,虽然map2 提供了一种有趣的方式来“循环”行,但在这种情况下,它不如简单的 for 循环那么快。