在R中按分组生成唯一事件列并修复统计结果异常
鸟类迁徙数据事件统计异常排查与修正
问题背景
用户持有包含BirdsID_season(原ID字段)、time(日期时间类型)、class(因子类型)的鸟类迁徙数据集,示例数据如下:
ID time class <chr> <dttm> <fct> 1 BBR-b172021-M_fall_winter_4 2022-11-01 19:03:31 migrating 2 BBR-b172021-M_fall_winter_4 2022-11-04 22:03:33 migrating ...(其余数据省略)
需求
需通过group_by、mutate等方法为数据集添加以下新列:
unique_class:标记连续的唯一事件(如migrating1、stopover1);migrate_sum/stopover_sum/winter_sum:按BirdsID_season统计对应类别的事件总次数;event_duration:计算每个事件的持续时间(天/小时)。
问题现象
使用@akrun的方案后,unique_class列生成正常,但统计列结果异常。例如针对BirdsID_season为BBR-b432021-M_fall_winter_4的数据,stopover_sum预期为1,但实际显示为3。测试代码如下:
# 查看目标ID的相关字段 fall_mig2 %>% filter(BirdsID_season == "BBR-b432021-M_fall_winter_4") %>% select(BirdsID_season, x, y, time, unique_class, class, stopover_sum) # 提取首尾10行验证stopover_sum slice_head <- fall_mig2 %>% filter(BirdsID_season == "BBR-b432021-M_fall_winter_4") %>% slice_head(n = 10) slice_tail <- fall_mig2 %>% filter(BirdsID_season == "BBR-b432021-M_fall_winter_4") %>% slice_tail(n = 10) bind_rows(slice_head, slice_tail) %>% select(BirdsID_season, x, y, time, stopover_sum)
输出结果中,迁移阶段的stopover_sum显示为3,冬季阶段显示为1,与预期不符。
解决方案
问题核心是原方案错误统计了对应类别的总行数,而非连续事件的数量。以下是修正后的完整代码:
library(dplyr) library(data.table) # 用于生成连续事件分组的rleid函数 fall_mig2 <- fall_mig2 %>% # 第一步:按ID分组,生成连续事件的分组标识 group_by(BirdsID_season) %>% mutate( rleid = rleid(class), # 连续相同class的行共享同一rleid unique_class = paste(class, match(rleid, unique(rleid)), sep = ""), # 先计算每行到事件起始时间的差,后续统一取最大值 event_duration_temp = difftime(time, first(time), units = "days") ) %>% # 第二步:按ID+连续事件分组,确定每个事件的总持续时间 group_by(BirdsID_season, rleid) %>% mutate(event_duration = max(event_duration_temp)) %>% ungroup() %>% # 第三步:再次按ID分组,统计各类连续事件的数量 group_by(BirdsID_season) %>% mutate( migrate_sum = n_distinct(rleid[class == "migrating"]), stopover_sum = n_distinct(rleid[class == "stopover"]), winter_sum = n_distinct(rleid[class == "winter"]) ) %>% ungroup() %>% # 清理中间临时字段 select(-rleid, -event_duration_temp)
关键逻辑说明
rleid(class):生成连续相同class的分组ID,确保同一连续事件的所有行拥有相同标识;unique_class:通过拼接class与该类别在当前ID下的连续事件序号,生成唯一事件标记;event_duration:先计算每行到事件起始时间的差值,再按连续事件分组取最大值,得到事件的总持续时间(可将units = "days"改为"hours"切换单位);- 统计列修正:使用
n_distinct(rleid[class == "xxx"])统计对应类别的连续事件数量,而非总行数,解决了原方案的统计错误。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

