如何在dplyr::mutate_if中引用刚创建的变量生成新变量?
问题与解决方案
问题
使用mutate_if处理数据框列时,无法像普通mutate那样直接引用刚创建的新变量。例如普通mutate支持链式引用:
x %>% mutate(new = column_a * 2, new_2 = new * 2)
但mutate_if中只能重复初始计算逻辑:
mutate_if(!str_detect(names(.), 'date|PIB|Deflator|[$]'), .funs = list(Real = ~ . / Deflator, Real_YoY = ~ (((. / Deflator) / lag((. / Deflator), 12))-1) * 100))
期望实现的写法(直接引用刚生成的变量):
mutate_if(!str_detect(names(.), 'date|PIB|Deflator|[$]'), .funs = list(Real = ~ . / Deflator, Real_YoY = ~ ((Real / lag(Real, 12))-1) * 100))
可复现示例:
x <- data.frame(x = seq(1,10), x1 = seq(21,30), y = seq(10,19)) x %>% mutate_if(str_detect(colnames(.), 'x'), .funs = list(new = ~ (. * 2), new2 = ~ (. * 2) * 4)) # 期望此处可引用变量'new'
解决方案
dplyr 1.0.0及以后版本中,mutate_if这类作用域动词已被across()取代,而across配合mutate可以实现你需要的链式引用需求——因为mutate会按代码顺序依次计算变量,后续计算可直接引用前面生成的列。
1. 针对可复现示例的改写
library(dplyr) library(stringr) x <- data.frame(x = seq(1,10), x1 = seq(21,30), y = seq(10,19)) x %>% mutate( # 第一步:处理含"x"的列,生成带_new后缀的新列 across(matches("x"), ~ . * 2, .names = "{.col}_new"), # 第二步:基于刚生成的_new列,计算带_new_2后缀的列 across(matches("x_new"), ~ . * 4, .names = "{.col}_2") )
2. 针对原始经济数据场景的写法
your_data %>% mutate( # 生成带_Real后缀的列:目标列除以Deflator across(!matches("date|PIB|Deflator|[$]"), ~ . / Deflator, .names = "{.col}_Real"), # 基于_Real列计算同比增速:(当期/去年同期 -1)*100 across(matches("_Real$"), ~ ((. / lag(., 12)) - 1) * 100, .names = "{.col}_YoY") )
为什么mutate_if无法实现?
mutate_if的.funs列表中,每个函数都是独立作用于原始列的,函数之间不共享计算上下文,因此无法引用同一.funs列表内刚生成的变量。而across是在mutate的全局上下文执行,天然支持按顺序引用已生成的变量。
内容的提问来源于stack exchange,提问作者user123456
相关产品推荐
相关产品推荐

