如何用R(Base或dplyr)筛选英数测试间隔≤30天的学生
筛选英数测试间隔小于30天的学生:报错修复方案
现有包含student_id、date、test三个字段的数据集,需求是筛选出存在任意英语(english)测试与数学(math)测试间隔小于30天的学生(测试先后顺序不影响)。运行GPT提供的dplyr代码时触发如下报错:
Error: Problem with
mutate()columndiff.
idiff = abs(...).
idiffmust be size 3 or 1, not 2.
i The error occurred in group 1: student_id = "A".
数据集代码
df <- data.frame(student_id = c("A", "C", "A", "B", "C", "B", "C", "C", "B", "A", "A"), date = as.Date(c("2022-01-02", "2022-05-02", "2022-04-07", "2022-03-09", "2022-04-02", "2022-04-02", "2022-03-16", "2022-03-01", "2022-05-10", "2022-01-17", "2022-04-02")), test = c("english", "science", "math", "english", "math", "science", "english", "math", "math", "math", "science"))
报错原因
原代码的核心问题是:当单个学生有多条英语或数学测试记录时,date[test == "english"]和date[test == "math"]返回的向量长度不匹配,导致difftime计算出的时间差数量与当前分组的行数不一致。比如学生A有1条英语记录、2条数学记录,计算后会得到2个时间差,但分组后当前行有3条(1英+2数),mutate要求生成的diff列长度必须和行数完全一致,因此触发维度错误。
修复方案
以下两种方法均可实现需求,且能避免维度不匹配问题:
方法1:宽表化后生成所有英数日期组合
先将数据转换为宽表格式,给同一学生的同类型测试记录编号,再生成所有英语与数学日期的组合,最后筛选间隔小于30天的学生:
library(dplyr) library(tidyr) df_filtered <- df %>% # 只保留英语和数学测试记录 filter(test %in% c("english", "math")) %>% # 给同一学生的同类型测试记录编序号,避免宽表化时丢失数据 mutate(row_id = row_number(), .by = c(student_id, test)) %>% # 转换为宽表,英语和数学日期分别作为列 pivot_wider(names_from = test, values_from = date) %>% # 生成当前学生所有英语与数学日期的组合 expand_grid(english, math, .by = student_id) %>% # 计算时间差的绝对值(单位:天) mutate(diff = abs(as.numeric(difftime(english, math, units = "days")))) %>% # 筛选间隔小于等于30天的记录 filter(diff <= 30) %>% # 提取去重后的学生ID select(student_id) %>% distinct()
方法2:分组后生成测试记录两两组合
在学生分组内生成所有测试记录的两两组合,筛选出英语与数学的配对,再判断时间间隔:
library(dplyr) df_filtered <- df %>% # 只保留英语和数学测试记录 filter(test %in% c("english", "math")) %>% # 按学生分组 group_by(student_id) %>% # 生成当前学生所有测试记录的两两组合 cross_join(., ., suffix = c("_x", "_y")) %>% # 排除同类型测试的组合(如英语和英语、数学和数学) filter(test_x != test_y) %>% # 计算时间差的绝对值(单位:天) mutate(diff = abs(as.numeric(difftime(date_x, date_y, units = "days")))) %>% # 筛选间隔小于等于30天的记录 filter(diff <= 30) %>% # 提取去重后的学生ID select(student_id) %>% distinct() %>% # 取消分组 ungroup()
结果验证
运行上述代码后,会得到符合条件的学生ID:A和C。其中:
- 学生A的英语测试(2022-01-02)与数学测试(2022-01-17)间隔15天,满足条件;
- 学生C的英语测试(2022-03-16)与数学测试(2022-03-01)间隔15天,满足条件;
- 学生B的英语测试与所有数学测试间隔均超过30天,被排除。
内容的提问来源于stack exchange,提问作者Bahi8482
相关产品推荐
相关产品推荐

