使用dplyr的across与.names参数创建新列并实现列差值运算
R语言数据框对应列相减并生成指定列名
原始数据
首先我们有如下数据框df:
set.seed(123) # 设置随机种子保证结果可复现 df <- data.frame(A_TR1=sample(10:20, 8, replace = TRUE),A_TR2=seq(2, 16, by=2), A_TR3=seq(1, 16, by=2), B_TR1=seq(1, 16, by=2),B_TR2=seq(2, 16, by=2), B_TR3=seq(1, 16, by=2))
输出结果:
A_TR1 A_TR2 A_TR3 B_TR1 B_TR2 B_TR3 1 11 2 1 1 2 1 2 12 4 3 3 4 3 3 18 6 5 5 6 5 4 11 8 7 7 8 7 5 17 10 9 9 10 9 6 17 12 11 11 12 11 7 14 14 13 13 14 13 8 11 16 15 15 16 15
需求
需要将A_TR1减B_TR1、A_TR2减B_TR2……以此类推,生成名为x TR1、x TR2等的新列。
原代码错误分析
尝试的代码:
xdf <- df%>%mutate(across(starts_with("A_TR"), -across(starts_with("B_TR")), .names="x TR{.col}"))
报错:
attempt to select less than one element in integerOneIndex
错误原因是嵌套across的逻辑不对:第一个across会遍历每一个A_开头的列,但第二个across会返回所有B_开头的列,导致单个A列和多个B列做减法,维度不匹配,引发报错。同时.names里的{.col}会保留原列名(比如A_TR1),无法生成x TR1这种格式的列名。
解决方法
方法1:tidyverse风格(推荐)
用dplyr结合stringr,通过列名匹配实现对应列相减,并生成指定列名:
library(dplyr) library(stringr) xdf <- df %>% mutate( # 遍历所有A_TR开头的列 across(starts_with("A_TR"), # 当前A列减去对应的B列:把列名中的A_替换成B_,再获取该列的值 ~ .x - get(str_replace(cur_column(), "^A_", "B_")), # 生成新列名:去掉原列名的A_,加上前缀x .names = "x TR{str_remove(.col, 'A_')}" ) )
运行后xdf的新列结果:
x TR1 x TR2 x TR3 1 10 0 0 2 9 0 0 3 13 0 0 4 4 0 0 5 8 0 0 6 6 0 0 7 1 0 0 8 -4 0 0
方法2:Base R风格
如果习惯用基础R,也可以通过提取列名编号循环处理:
# 提取所有TR的编号(比如1、2、3) tr_nums <- unique(sub(".*TR(\\d+)", "\\1", names(df))) xdf <- df # 循环生成新列 for(num in tr_nums) { xdf[[paste0("x TR", num)]] <- xdf[[paste0("A_TR", num)]] - xdf[[paste0("B_TR", num)]] }
效果和方法1完全一致。
内容的提问来源于stack exchange,提问作者gingerbreadbeer
相关产品推荐
相关产品推荐

