使用tidyverse的across函数生成新变量失败,求解决方案
用tidyverse批量生成金额变量的解决方案
问题背景
原始数据框如下:
library(tidyverse) raw_df <- data.frame(region=c('R_1','R_2','R_3'), qty_A=c(1,2,3), qty_B=c(4,1,3), qty_C=c(7,1,6), qty_D=c(7,1,6), price_A=c(100,110,300), price_B=c(300,100,230), price_C=c(170,116,66), price_D=c(73,11,15))
手动逐个生成amount_A/B/C/D的代码可行但效率低:
raw_df %>% mutate(amount_A = qty_A * price_A, amount_B = qty_B * price_B, amount_C = qty_C * price_C, amount_D = qty_D * price_D)
尝试用across批量实现时失败,需求是:用across批量生成金额变量,当某后缀无匹配的qty和price列时,对应amount变量为NULL。
错误原因
你之前的代码逻辑有误:
across(where(is.numeric))是对所有数值列逐个操作,不符合按后缀配对qty_*和price_*的需求- 直接用
str_c('qty_',c('A','B','C')) * str_c('price_',c('A','B','C'))是对字符串做乘法,R无法识别这种操作,必然报错
正确实现代码
方法1:指定后缀批量生成
直接针对目标后缀循环,检查列是否存在后计算乘积:
raw_df %>% mutate( across(c("A", "B", "C", "D"), ~ { qty_col <- str_c("qty_", .x) price_col <- str_c("price_", .x) if (all(c(qty_col, price_col) %in% names(raw_df))) { !!sym(qty_col) * !!sym(price_col) } else { NULL } }, .names = "amount_{.col}" ) )
方法2:自动提取后缀(更灵活)
如果后续可能新增后缀,可自动提取所有存在的qty_*或price_*的后缀,无需手动指定:
# 提取所有存在的后缀 suffixes <- raw_df %>% names() %>% str_extract("(?<=qty_|price_)\\w+") %>% na.omit() %>% unique() # 批量生成金额变量 raw_df %>% mutate( across(suffixes, ~ { qty_col <- str_c("qty_", .x) price_col <- str_c("price_", .x) if (all(c(qty_col, price_col) %in% names(raw_df))) { !!sym(qty_col) * !!sym(price_col) } else { NULL } }, .names = "amount_{.col}" ) )
效果验证
运行后会生成amount_A到amount_D四个变量,每个变量对应qty_*与price_*的乘积。如果某后缀(比如新增E)只有qty_E没有price_E,则amount_E会被设为NULL,不会出现在结果数据框中。
内容的提问来源于stack exchange,提问作者anderwyang
相关产品推荐
相关产品推荐

