如何在含dplyr::mutate()的函数中动态传递列名生成新列?
问题
我想编写一个函数,接收字符串(或裸变量名)作为列名,在dplyr::mutate()的等式两侧使用该列名。手动实现的示例如下:
cars %>% mutate( new_speed = speed + 5, revised_speed = case_when(new_speed < 12 ~ 0, new_speed == 12 ~ 1, new_speed > 12 ~ 1/new_speed), )
我需要把这个过程自动化,实现两个核心需求:
- 给指定列名添加
prefix_1前缀生成新列,值为原列数值加5 - 生成另一列,列名带
prefix_2前缀,值依赖于第一步生成的新列
现有函数框架需要补充正确语法:
insert_names <- function(df, oldname, prefix_1, prefix_2){ df %>% mutate( **prefix_1.oldname** = oldname + 5, **prefix_2.oldname** = case_when(**prefix_1.oldname** < 12 ~ 0, **prefix_1.oldname** == 12 ~ 1, **prefix_1.oldname** > 12 ~ 1/**prefix_1.oldname**), ) }
要求函数能通过以下调用复现手动实现的输出,同时支持传入未加引号的speed参数:
insert_names(cars, oldname = "speed", prefix_1 = "new", prefix_2 = "improved")
解决方案
要实现动态列名和变量引用,需要用到dplyr的tidy eval语法,核心是处理动态列名的:=赋值符,以及支持字符串/裸变量的符号转换工具。
方法1:同时支持字符串和裸变量输入
用ensym()将输入的列名转换为符号,兼容字符串和裸变量两种输入方式,再通过paste0()拼接新列名,结合.data代词引用动态生成的列:
library(dplyr) insert_names <- function(df, oldname, prefix_1, prefix_2){ # 将输入转换为符号,同时支持字符串和裸变量 old_sym <- ensym(oldname) # 生成新列名 new_col <- paste0(prefix_1, "_", as_name(old_sym)) improved_col <- paste0(prefix_2, "_", as_name(old_sym)) df %>% mutate( # 动态生成第一列:原列值+5 !!new_col := !!old_sym + 5, # 动态生成第二列,依赖第一列 !!improved_col := case_when( .data[[new_col]] < 12 ~ 0, .data[[new_col]] == 12 ~ 1, .data[[new_col]] > 12 ~ 1/.data[[new_col]] ) ) }
方法2:仅支持字符串输入(简化版)
如果只需要处理字符串类型的列名,直接用.data代词配合动态列名赋值即可:
insert_names_str <- function(df, oldname, prefix_1, prefix_2){ new_col <- paste0(prefix_1, "_", oldname) improved_col <- paste0(prefix_2, "_", oldname) df %>% mutate( !!new_col := .data[[oldname]] + 5, !!improved_col := case_when( .data[[new_col]] < 12 ~ 0, .data[[new_col]] == 12 ~ 1, .data[[new_col]] > 12 ~ 1/.data[[new_col]] ) ) }
测试验证
两种方法都能通过以下调用得到预期结果:
# 字符串参数调用 insert_names(cars, oldname = "speed", prefix_1 = "new", prefix_2 = "improved") # 裸变量调用 insert_names(cars, oldname = speed, prefix_1 = "new", prefix_2 = "improved")
输出会新增new_speed和improved_speed两列,逻辑与手动实现完全一致。
内容的提问来源于stack exchange,提问作者conflictcoder
相关产品推荐
相关产品推荐

