如何用dplyr按class分组计算行间多列绝对差并生成差异列?
用dplyr实现分组计算分数绝对差并生成对应ID列
给定包含class、学生id及q1-q4科目分数的数据框,需完成以下操作:
- 按
class分组 - 计算每行与同组其他行的q1-q4列绝对差之和
- 基于该差值生成
diff0、diff1、diff2、diff3列,分别记录同组内与当前行差值为0、1、2、3的其他学生id(无匹配则填NA)
输入数据
class id q1 q2 q3 q4 Ali 12 1 2 3 3 Tom 16 1 2 4 2 Tom 18 1 2 3 4 Ali 24 2 2 4 3 Ali 35 2 2 4 3 Tom 36 1 2 4 2
数据的dput结构:
structure(list(class = c("Ali", "Tom", "Tom", "Ali", "Ali", "Tom"), id = c(12L, 16L, 18L, 24L, 35L, 36L), q1 = c(1L, 1L, 1L, 2L, 2L, 1L), q2 = c(2L, 2L, 2L, 2L, 2L, 2L), q3 = c(3L, 4L, 3L, 4L, 4L, 4L), q4 = c(3L, 2L, 4L, 3L, 3L, 2L)), row.names = c(NA, -6L), class = "data.frame")
dplyr实现方案
library(dplyr) library(tidyr) df <- structure(list(class = c("Ali", "Tom", "Tom", "Ali", "Ali", "Tom"), id = c(12L, 16L, 18L, 24L, 35L, 36L), q1 = c(1L, 1L, 1L, 2L, 2L, 1L), q2 = c(2L, 2L, 2L, 2L, 2L, 2L), q3 = c(3L, 4L, 3L, 4L, 4L, 4L), q4 = c(3L, 2L, 4L, 3L, 3L, 2L)), row.names = c(NA, -6L), class = "data.frame") result <- df %>% group_by(class) %>% # 为每组内的每行生成同组所有数据的列表并展开 mutate(temp = list(cur_data())) %>% unnest(temp) %>% # 排除行与自身的匹配 filter(id != temp.id) %>% # 计算四科分数的绝对差之和 mutate(diff_sum = abs(q1 - temp.q1) + abs(q2 - temp.q2) + abs(q3 - temp.q3) + abs(q4 - temp.q4)) %>% # 按原行的class和id分组,整理不同差值对应的id字符串 group_by(class, id, q1, q2, q3, q4) %>% summarise( diff0 = ifelse(any(diff_sum == 0), paste0(temp.id[diff_sum == 0], collapse = ","), NA), diff1 = ifelse(any(diff_sum == 1), paste0(temp.id[diff_sum == 1], collapse = ","), NA), diff2 = ifelse(any(diff_sum == 2), paste0(temp.id[diff_sum == 2], collapse = ","), NA), diff3 = ifelse(any(diff_sum == 3), paste0(temp.id[diff_sum == 3], collapse = ","), NA), .groups = "drop" ) print(result)
代码说明
- 分组匹配同组数据:通过
mutate(temp = list(cur_data()))为每组内的每行生成包含同组所有数据的列表,再用unnest展开,实现每行与同组其他行的全量匹配。 - 过滤自身匹配:用
filter(id != temp.id)剔除行与自身的无效匹配。 - 计算绝对差总和:对q1-q4四列分别计算绝对差后求和,得到每行与同组其他行的分数差异值
diff_sum。 - 生成目标列:按原行的
class和id分组,分别筛选出diff_sum为0、1、2、3的学生id,用paste0合并为字符串,无匹配项则设为NA。
输出结果
# A tibble: 6 × 10 class id q1 q2 q3 q4 diff0 diff1 diff2 diff3 <chr> <int> <int> <int> <int> <int> <chr> <chr> <chr> <chr> 1 Ali 12 1 2 3 3 NA NA 24,35 NA 2 Ali 24 2 2 4 3 35 NA 12 NA 3 Ali 35 2 2 4 3 24 NA 12 NA 4 Tom 16 1 2 4 2 36 NA NA 18 5 Tom 18 1 2 3 4 NA NA NA 16,36 6 Tom 36 1 2 4 2 16 NA NA 18
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

