如何用R的dplyr/tidyverse基于单列条件创建新列?
问题
我有一个大型动物爱好者成本反馈的DataFrame,目前用ifelse()处理条件创建新列,但数据量增大后效率不足,希望用dplyr/tidyverse工具实现以下需求:
- pig的成本固定为100;
- 其余动物成本均分,若Feedback中包含pig,需先从总成本Cost里减去100再均分;
- 需要处理Feedback列里的特殊字符(逗号、&、and、斜杠等)来拆分动物名称。
示例数据:
df = structure(list(Feedback = c("cat, dog, rabbit", "cat & rabbit ", "cat and fish", "dog/pig/rabbit", "cat, pig", "fish, rabbit, dog", "dog, cat and pig"), Cost = c(90L, 80L, 50L, 300L, 200L, 120L, 260L), pig = c(100L, 100L, 100L, 100L, 100L, 100L, 100L)), class = "data.frame", row.names = c(NA, -7L))
理想输出为包含cat、dog、rabbit、fish、pig_new列的DataFrame,数值符合规则要求。
解决方案
用tidyverse工具链可以高效完成批量数据处理,代码如下:
library(tidyverse) result_df <- df %>% # 统一处理Feedback列的分隔符,拆分出动物列表 mutate( cleaned_feedback = str_replace_all(Feedback, "[^a-zA-Z]+", ","), cleaned_feedback = str_squish(cleaned_feedback), animal_list = str_split(cleaned_feedback, ",") ) %>% unnest(animal_list) %>% mutate(animal_list = str_trim(animal_list)) %>% # 按原始行分组,计算可分配成本与单动物成本 group_by(row.names(df)) %>% mutate( has_pig = any(animal_list == "pig"), alloc_cost = ifelse(has_pig, first(Cost) - 100, first(Cost)), non_pig_count = sum(animal_list != "pig"), animal_cost = ifelse(animal_list == "pig", 100, alloc_cost / non_pig_count) ) %>% # 转换为宽格式,填充缺失动物的成本为0 pivot_wider( names_from = animal_list, values_from = animal_cost, values_fill = 0 ) %>% # 调整列名与顺序 rename(pig_new = pig) %>% select(cat, dog, rabbit, fish, pig_new, Cost) print(result_df)
代码说明
- 字符清洗:用
str_replace_all将所有非字母的分隔符替换为逗号,再拆分出干净的动物名称列表,避免特殊字符干扰; - 分组计算:按原始数据行分组,判断是否包含pig,计算可分配给非pig动物的总成本,再根据非pig动物数量算出单动物均分成本;
- 格式转换:用
pivot_wider将长格式数据转为需求的宽格式,缺失的动物成本填充为0; - 列调整:重命名pig列为
pig_new,调整列顺序匹配需求。
运行后输出结果:
| cat | dog | rabbit | fish | pig_new | Cost |
|---|---|---|---|---|---|
| 30 | 30 | 30 | 0 | 0 | 90 |
| 40 | 0 | 40 | 0 | 0 | 80 |
| 25 | 0 | 0 | 25 | 0 | 50 |
| 100 | 100 | 100 | 0 | 100 | 300 |
| 100 | 0 | 0 | 0 | 100 | 200 |
| 40 | 40 | 40 | 0 | 0 | 120 |
| 80 | 80 | 0 | 0 | 100 | 260 |
内容的提问来源于stack exchange,提问作者Luther_Proton
相关产品推荐
相关产品推荐

