如何用更简洁方式或dplyr实现数据框按n列分组的列除法运算?
问题描述
现有如下R语言数据框:
df <- data.frame(a = c(1,2,3,4), b = c(5,6,7,8), c = c(22,33,44,55), d = c(7,8,9,10), e = c(2,3,4,5), f = c(99,88,66,44))
输出结果:
a b c d e f 1 1 5 22 7 2 99 2 2 6 33 8 3 88 3 3 7 44 9 4 66 4 4 8 55 10 5 44
需求:将每n列(示例中n=2)除以最初的n列,当前通过手动逐列赋值实现:
df$c <- df$c / df$a df$d <- df$d / df$b df$e <- df$e / df$a df$f <- df$f / df$b df$a <- df$a / df$a df$b <- df$b / df$b
最终得到:
a b c d e f 1 1 1 22.00000 1.400000 2.000000 19.800000 2 1 1 16.50000 1.333333 1.500000 14.666667 3 1 1 14.66667 1.285714 1.333333 9.428571 4 1 1 13.75000 1.250000 1.250000 5.500000
询问是否存在更简洁的实现方式,或如何用dplyr包完成该操作?
解决方案
一、基础R简洁实现
通过循环+索引匹配的方式,避免手动逐列赋值:
n <- 2 # 提取前n列作为除数模板 div_template <- df[, 1:n] # 遍历所有列,对应除以模板中的循环列 df[] <- lapply(seq_len(ncol(df)), function(col_idx) { df[, col_idx] / div_template[, (col_idx - 1) %% n + 1] })
核心逻辑:
- 先获取前n列作为重复使用的除数模板
- 对每一列,用
(col_idx - 1) %% n + 1计算它在模板中的对应位置(实现循环匹配) - 逐列完成除法后重新赋值给原数据框
二、dplyr包实现
利用across()函数结合列索引匹配,代码更简洁:
library(dplyr) n <- 2 df <- df %>% mutate(across(everything(), ~ .x / pick((cur_column() %>% as.integer() - 1) %% n + 1)))
如果觉得列名转整数的写法不够直观,也可以用列索引直接处理:
df <- df %>% mutate(across(1:ncol(.), ~ .x / !!sym(names(.)[(cur_column() %>% as.integer() - 1) %% n + 1])))
逻辑说明:
across(everything())指定对所有列执行操作cur_column()获取当前处理的列名,转为整数后计算对应的除数列位置pick()或!!sym()提取对应的除数列,完成除法运算
上述两种方法的输出结果和手动赋值完全一致,且能灵活适配不同的n值。
内容的提问来源于stack exchange,提问作者tmxkrdhqj
相关产品推荐
相关产品推荐

