基于起止日期的日/周/月周期x列求和实现求助
看起来你之前的循环没实现核心逻辑——没有判断当前日期是否落在每行的start和end区间内,所以每次都输出了所有x的总和,完全没达到需求。下面我会用高效的方法帮你实现日、周、月的求和,同时也会修正你的循环写法(虽然不推荐用循环,效率太低)。
先处理每日求和(高效版:用data.table非等值连接)
因为你的日期范围跨度很大(2017到2021年),用for循环遍历每天会非常慢,优先用data.table的非等值连接来实现,这是处理这类区间匹配问题的最优方案之一。
首先确保你的数据是data.table格式,然后生成日期序列:
library(data.table) library(lubridate) # 把你的数据转成data.table(已经做了日期转换,这里再确认下) setDT(df) df[, c("start", "end") := .(as.Date(start), as.Date(end))] # 生成所有需要统计的日期,转成data.table days_dt <- data.table(date = seq(from = as.Date("2017-01-01"), to = as.Date("2021-07-31"), by = "day"))
接下来用非等值连接匹配每个日期对应的区间,然后按日期求和:
# 非等值连接:找到每个日期落在哪些start-end区间内,再按日期汇总x的总和 daily_sum <- days_dt[df, on = .(date >= start, date <= end), allow.cartesian = TRUE, .(date, x)][, .(total_x = sum(x)), by = date]
这段代码的逻辑是:把每个日期和所有包含它的区间行做连接,然后提取日期和对应的x值,最后按日期分组求和,得到每天的total_x。
如果你一定要用循环(低效但易理解)
如果觉得非等值连接有点复杂,也可以修正你的循环代码,核心是加入日期是否在区间内的判断:
days <- seq(from=as.Date("2017-01-01"), to=as.Date("2021-07-31"), by="days") daily_sum_loop <- data.frame(date = days, total_x = 0) for (i in seq_along(days)){ current_day <- days[i] # 筛选出start <= 当前日期 <= end的行,然后求和x daily_sum_loop$total_x[i] <- sum(df$x[df$start <= current_day & df$end >= current_day]) }
⚠️ 注意:当日期数量多或者数据行数多的时候,这个循环会非常慢,所以尽量用前面的data.table方法。
扩展到每周求和
有了每日的求和结果,每周求和就很简单了——把日期按「年-周」分组,汇总每日的总和即可:
# 用lubridate的yearweek函数生成标准的年周格式(比如"2017-W01") weekly_sum <- daily_sum[, .(total_x = sum(total_x)), by = .(year_week = yearweek(date))]
如果你需要区分不同年份的周(比如2017年第52周和2018年第52周),yearweek生成的格式已经自动处理了这个问题。
扩展到每月求和
同理,每月求和只需要把日期按「年月」分组:
# 用floor_date把日期转成当月第一天,作为分组依据 monthly_sum <- daily_sum[, .(total_x = sum(total_x)), by = .(year_month = floor_date(date, "month"))] # 或者转成字符串格式(比如"2017-01"),方便阅读 monthly_sum <- daily_sum[, .(total_x = sum(total_x)), by = .(year_month = format(date, "%Y-%m"))]
验证结果
你可以拿样本数据里的某一行来验证,比如第8行start=2020-04-12, end=2021-06-01, x=28,那么从2020-04-12到2021-06-01的每一天,total_x都会包含28,对应的周和月总和也会累计这个值,符合你的需求。
内容的提问来源于stack exchange,提问作者fjurt

