如何用dplyr更优雅地按组条件修改item_1的time_1值?
优化dplyr分组替换逻辑的优雅实现
原始数据与需求
首先是创建数据框的代码:
df1 <- data.frame(id = rep( c('id_1', 'id_2') , c(3,3)), item = c('item_1', 'item_2', 'item_3') , time_1 = c( 1:3 , 2:3 , 1) , time_2 = c( 1:3 , 1, 3 , 1))
数据展示(time_2是time_1的期望替换结果):
id item time_1 time_2 1 id_1 item_1 1 1 2 id_1 item_2 2 2 3 id_1 item_3 3 3 4 id_2 item_1 2 1 5 id_2 item_2 3 3 6 id_2 item_3 1 1
需求:按id分组,检查每组中item_1的time_1是否为组内最小值,若不是则替换为组内最小值(比如id_2组的item_1原time_1为2,需替换为1)。
原实现方式:
df1 %>% group_by( id ) %>% mutate( min_ = min( time_1 ) ) %>% mutate( time_1 = ifelse( ( item == 'item_1' ) & ( time_1 != min_ ) , min_ , time_1 ) )
优化后的dplyr实现
方案1:用case_when简化逻辑(推荐)
df1 %>% group_by(id) %>% mutate( time_1 = case_when( item == "item_1" ~ pmin(time_1, min(time_1)), TRUE ~ time_1 ) ) %>% ungroup()
这里用case_when让条件分支更直观,pmin直接取当前值和组内最小值的较小值,不管当前值是不是最小,最终结果都符合需求。同时分组后建议用ungroup()取消分组,避免后续操作受分组状态影响。
方案2:合并mutate的简洁写法
如果偏好if_else,可以把逻辑合并到单个mutate中,省去中间变量:
df1 %>% group_by(id) %>% mutate( time_1 = if_else(item == "item_1" & time_1 != min(time_1), min(time_1), time_1) ) %>% ungroup()
优化点说明
- 去掉了冗余的中间列
min_,直接在替换逻辑中计算组内最小值,避免数据框冗余 - 用单个
mutate完成所有逻辑,更贴合dplyr管道式操作的风格 case_when的写法比嵌套ifelse更易读,后续如果要加其他替换规则也更容易扩展
内容的提问来源于stack exchange,提问作者marc
相关产品推荐
相关产品推荐

