使用dplyr的filter、mutate、case_when处理带NA的绝对差求和问题
问题描述
给定样本数据:
structure(list(role = c("r1", "c1", "r2", "c2"), v1 = c(1200, 0, 1200, 0), v2 = c(900, 500, 900, 900), v3 = c(600, 800, 600, 1800), v4 = c(300, 900, 300, 2700)), class = "data.frame", row.names = c(NA, -4L), codepage = 65001L)
需求:
- 根据
role列的值,对v1-v4分别与指定数值计算绝对差后求和,生成accuracy列:- 当
role为r1或r2时,v1-v4分别与3600、2700、1800、300计算绝对差后求和; - 当
role为c1或c2时,v1-v4分别与0、300、600、900计算绝对差后求和;
- 当
原尝试的dplyr代码:
data <- data %>% mutate(accuracy = case_when((role == 'r1') ~ abs(v1 - 3600) + abs(v2 - 2700) + abs(v3 - 1800) + abs(v4 - 300), (role == 'r2') ~ abs(v1 - 3600) + abs(v2 - 2700) + abs(v3 - 1800) + abs(v4 - 300), (role == 'c1') ~ abs(v1 - 0) + abs(v2 - 300) + abs(v3 - 600) + abs(v4 - 900), (role == 'c2') ~ abs(v1 - 0) + abs(v2 - 300) + abs(v3 - 600) + abs(v4 - 900)))
问题:该代码在小数据集正常,但含大量NA的大数据集中accuracy列全为NA,需要将NA视为0参与计算,避免结果异常。
解决方案
核心思路
- 用
coalesce()函数将v1-v4中的NA替换为0,消除绝对差计算时的NA传递; - 简化
case_when的条件判断,将同规则的role值合并,减少冗余代码; - 处理
role为NA或未知值的边界情况,确保所有行都能生成有效结果。
修正后的代码(不修改原始v列)
library(dplyr) data <- data %>% mutate( accuracy = case_when( # 处理r1/r2组,嵌套coalesce将NA转0 role %in% c("r1", "r2") ~ abs(coalesce(v1, 0) - 3600) + abs(coalesce(v2, 0) - 2700) + abs(coalesce(v3, 0) - 1800) + abs(coalesce(v4, 0) - 300), # 处理c1/c2组 role %in% c("c1", "c2") ~ abs(coalesce(v1, 0) - 0) + abs(coalesce(v2, 0) - 300) + abs(coalesce(v3, 0) - 600) + abs(coalesce(v4, 0) - 900), # 处理role为NA或其他未知值,默认按0计算,可按需调整 TRUE ~ abs(coalesce(v1, 0)) + abs(coalesce(v2, 0)) + abs(coalesce(v3, 0)) + abs(coalesce(v4, 0)) ) )
可选优化(提前替换v列NA)
如果允许修改原始v1-v4列,可提前统一替换NA,让计算代码更简洁:
data <- data %>% mutate( v1 = coalesce(v1, 0), v2 = coalesce(v2, 0), v3 = coalesce(v3, 0), v4 = coalesce(v4, 0), accuracy = case_when( role %in% c("r1", "r2") ~ abs(v1 - 3600) + abs(v2 - 2700) + abs(v3 - 1800) + abs(v4 - 300), role %in% c("c1", "c2") ~ abs(v1) + abs(v2 - 300) + abs(v3 - 600) + abs(v4 - 900), TRUE ~ v1 + v2 + v3 + v4 ) )
内容的提问来源于stack exchange,提问作者user14250906
相关产品推荐
相关产品推荐

