You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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)

关键逻辑说明

  1. rleid(class):生成连续相同class的分组ID,确保同一连续事件的所有行拥有相同标识;
  2. unique_class:通过拼接class与该类别在当前ID下的连续事件序号,生成唯一事件标记;
  3. event_duration:先计算每行到事件起始时间的差值,再按连续事件分组取最大值,得到事件的总持续时间(可将units = "days"改为"hours"切换单位);
  4. 统计列修正:使用n_distinct(rleid[class == "xxx"])统计对应类别的连续事件数量,而非总行数,解决了原方案的统计错误。

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:05:23