这个问题的根本主题实际上是关于《D&D 5E》中的积分购买系统,但在研究它时,我注意到这c_across()大大减慢了速度。
该方法
die_value <- 8L:15L
point_value <- c(0L, 1L, 2L, 3L, 4L, 5L, 7L, 9L)
determine_points <- function(n) {
sapply(n, function(x) point_value[which(die_value == x)])
}
sum27 <- crossing(
d1 = die_value,
d2 = die_value,
d3 = die_value,
d4 = die_value,
d5 = die_value,
d6 = die_value
) %>%
mutate(across(d1:d6, ~determine_points(.x), .names = "{col}_points")) %>% # see next blocks for options
Run Code Online (Sandbox Code Playgroud)
作为第一次尝试按行对d[n]_points列进行求和total_points,我尝试采用明显的rowwise %>% sum(c_across)路线
rowwise %>% mutate(total_points = sum(c_across(d1_points:d6_points)))
Run Code Online (Sandbox Code Playgroud)
然而,运行这个过程花了很长时间。相比之下,更明确的版本确实很快。
mutate(total_points = d1_points + d2_points + d3_points + d4_points + d5_points + d6_points)
Run Code Online (Sandbox Code Playgroud)
平衡速度与便利性始终是一个挑战,但这里的差异是巨大的。
有没有更好的方法来总结数据帧的某些行,同时对需要添加的列数保持一点不可知性?
我们可以使用withrowSums代替+ 。使用,我们还可以删除如果存在的sumacrossrowwisec_acrossrowSumsNA
library(dplyr)
...
%>%
mutate(total_points = rowSums(across(d1_points:d6_points), na.rm = TRUE))
Run Code Online (Sandbox Code Playgroud)
或者在较新的版本 ( dplyr version >= 1.1.0) 中,使用pick选择列,因为我们将该函数应用于rowSums列的整个子集而不是每列本身。根据?pick
使用 pick(),您通常将函数应用于整个数据框。
使用 across(),您通常会对每一列应用一个函数。
%>%
mutate(total_points = rowSums(pick(ends_with("_points")), na.rm = TRUE))
Run Code Online (Sandbox Code Playgroud)
如果没有 NA,还可以选择reduce使用+
library(purrr)
...
%>%
mutate(total_points = reduce(across(1_points:d6_points), `+`))
Run Code Online (Sandbox Code Playgroud)
> dim(sum27)
[1] 262144 12
> system.time(sum27 %>%
+ mutate(total_points = rowSums(across(d1_points:d6_points),
na.rm = TRUE)))
user system elapsed
0.021 0.006 0.027
# stopped the timing
> system.time(sum27 %>% rowwise %>%
+ mutate(total_points = sum(c_across(d1_points:d6_points)))
+ )
#Timing stopped at: 122.3 0.892 124.6
> system.time(sum27 %>%
+ mutate(total_points = reduce(across(d1_points:d6_points), `+`)))
user system elapsed
0.030 0.010 0.039
Run Code Online (Sandbox Code Playgroud)