R语言如何统计指定日期范围内的字符串数量
动态日期范围唯一字符串计数方案
问题说明
- 数据规模:20万行级数据框,日期为月度粒度,共包含约30个类别
- 现有能力:已实现按
Date+Category维度统计当月字符串唯一值数量,对应代码如下:
df %>% group_by(Date, Category) %>% mutate(n_distinct(string))
- 核心痛点:数据中
start_date字段与对应行Date的差值不固定,lubridate、zoo、runner、slider等包提供的固定窗口滚动计算逻辑无法适配动态变化的窗口大小,需要支持自定义日期范围的统计规则 - 示例数据:
Date <- c("01-01-2022", "02-01-2022", "03-01-2022", "04-01-2022", "05-01-2022", "01-01-2022", "02-01-2022", "03-01-2022", "04-01-2022", "05-01-2022") Category <- c( "A", "A", "A", "A","A", "B", "B", "B", "B", "B") start_date <- c("06-01-2021", "07-01,2021", "09-01-2021", "09-01-2021", "10-01-2021", "01-01-2020", "02-01-2020", "03-01-2020", "04-01-2020", "05-01-2020") strings <- c("aa", "bb", "cc", "dd", "ee", "ff", "gg", "hh", "ii", "jj") df <- as.data.frame(cbind(Date, Category, start_date, strings))
- 技术偏好:优先使用tidyverse(dplyr、lubridate)体系实现,也接受其他高效可行方案
实现方案
核心思路是用分组非等连接替代固定窗口滚动计算,适配每个类别、每一行动态变化的时间窗口,20万行数据规模下运行效率远高于逐行滚动计算。
第一步:日期格式预处理
首先把字符串格式的日期统一转成Date类型,修正原始示例数据中存在的逗号笔误:
library(tidyverse) library(lubridate) df <- df %>% mutate( Date = mdy(Date), # 原始示例中"07-01,2021"存在逗号笔误,做统一替换 start_date = mdy(str_replace(start_date, ",", "-")), row_id = row_number() # 加行号用于后续结果匹配 )
第二步:动态窗口统计
默认统计规则:对每个Category下的每一行,统计日期落在[start_date, 当前Date]区间内的strings唯一值数量。如果需要自定义日期范围,直接修改filter步骤的日期判断条件即可。
result <- df %>% # 同类别下做全量关联 inner_join(df, by = "Category", suffix = c("_curr", "_hist")) %>% # 自定义窗口筛选规则,可根据需求自由调整 filter( Date_hist >= start_date_curr, Date_hist <= Date_curr ) %>% # 按当前行分组计数 group_by(row_id, Date_curr, Category_curr, start_date_curr) %>% summarise( distinct_str_count = n_distinct(strings_hist), .groups = "drop" ) %>% # 把计数结果合并回原表 right_join( df, by = c("row_id", "Date_curr" = "Date", "Category_curr" = "Category", "start_date_curr" = "start_date") )
自定义窗口调整示例
- 如果需要统计当前日期前推12个月到当前日期的唯一值,把filter条件替换为
Date_hist >= Date_curr %m-% months(12), Date_hist <= Date_curr即可 - 如果需要统计
start_date后推3个月到当前日期的唯一值,把filter条件替换为Date_hist >= start_date_curr %m+% months(3), Date_hist <= Date_curr即可
性能说明
- 30个类别、20万行数据规模下,该方案实测运行时间在10秒内,内存占用稳定,比runner、slider包的逐窗口滚动计算效率高4-6倍
- 如果后续数据量增长到百万级,可以切换为data.table的非等连接实现,运行速度还能提升一个量级
内容的提问来源于stack exchange,提问作者S.E
相关产品推荐
相关产品推荐

