如何用R的case_when分组获取最小正值/近0负值对应的animal
问题
需要实现以下操作:
- 按
id对dataframe进行分组 - 当所有
difft值为正时,将最小正值对应的animal赋值给myfilter - 当存在
difft值为负时,将最接近0的负值对应的animal赋值给myfilter
编写的代码无法正常运行:
tibble( id = c(1, 1, 1, 2, 2, 2), animal = c("dog", "snake", "cat", "monkey", "fox", "mouse"), difft = c(-4, -33, 30, 9, 33, 1) ) %>% group_by(id) %>% mutate(myfilter = case_when( all(difft > 0) ~ as.character(animal[difft == min(difft)]), #当所有值为正,取最小正值 !all(difft > 0) ~ as.character(animal[difft == (min(abs(difft[difft <0]))*-1)]) #当存在负值,取最接近0的负值 )) %>% select(id, myfilter)
期望结果:
id myfilter 1 dog 1 dog 1 dog 2 mouse 2 mouse 2 mouse
单独将每条case_when的逻辑放在summarize中可以正常运行,但无法让case_when同时识别两种场景,需要解决。
解决方案
你的代码问题出在case_when里的索引逻辑——当用向量索引时,在mutate的上下文里可能返回长度不匹配的结果(比如某个条件下返回多个值或空值),而且逻辑可以更简洁。
以下是两种可行的修正方案:
方案1:优化case_when逻辑,确保返回单一值
library(tibble) library(dplyr) tibble( id = c(1, 1, 1, 2, 2, 2), animal = c("dog", "snake", "cat", "monkey", "fox", "mouse"), difft = c(-4, -33, 30, 9, 33, 1) ) %>% group_by(id) %>% mutate( myfilter = case_when( # 所有值为正:取difft最小的行的animal all(difft > 0) ~ animal[which.min(difft)], # 存在负值:取difft最大的负值(最接近0)的行的animal TRUE ~ animal[which.max(difft[difft < 0])] ) ) %>% select(id, myfilter)
方案2:先分组筛选目标行,再左连接回原数据
这种方式逻辑更清晰,彻底避免索引出错:
library(tibble) library(dplyr) df <- tibble( id = c(1, 1, 1, 2, 2, 2), animal = c("dog", "snake", "cat", "monkey", "fox", "mouse"), difft = c(-4, -33, 30, 9, 33, 1) ) # 先分组获取每个id对应的目标animal target_animals <- df %>% group_by(id) %>% slice( if (all(difft > 0)) { which.min(difft) } else { which.max(difft[difft < 0]) } ) %>% select(id, myfilter = animal) # 左连接回原数据 df %>% left_join(target_animals, by = "id") %>% select(id, myfilter)
两种方案都能得到你期望的结果,核心是确保每个分组只返回一个目标animal值,避免mutate时出现长度不匹配的问题。
内容的提问来源于stack exchange,提问作者mdb_ftl
相关产品推荐
相关产品推荐

