如何基于Pandas计算物品借出与归还的滚动累计数?
计算借出物品的滚动累计数量
你遇到的这个问题本质是事件流的状态跟踪问题:我们需要把每一次借出和归还都当作独立事件,通过按时间顺序统计这些事件的累计变化,得到任意时间点的未归还物品总数(也就是示例中的rollingTotalCheckedOut)。滚动窗口不适用的原因是窗口无法覆盖跨时长极长的借还记录,而事件重构的方法更适合这类场景。
解决思路
核心逻辑是把每条借还记录拆成两个事件:
- 借出事件:发生时间为
out_ts,对累计数的影响是**+1** - 归还事件:发生时间为
in_ts,对累计数的影响是**-1**
把所有事件按时间排序后,计算累计和,就能得到每个时间点的滚动借出总数。如果需要和原数据集对应,只需根据每条记录的借出时间,找到对应的累计值即可。
实现代码
import pandas as pd # 原数据集 df = pd.DataFrame([ ['A', 1624990605, 1627102404, 1], ['A', 1624990635, 1625015061, 2], ['A', 1624990790, 1624991096, 3], ['A', 1624990790, 1624990913, 4], ['A', 1624990822, 1624991711, 5], ['A', 1624990945, 1624991096, 5], ['A', 1624991036, 1624991066, 6], ['A', 1624991067, 1624991188, 6], ], columns = ['ID', 'out_ts', 'in_ts', 'rollingTotalCheckedOut']) # 转换时间格式(可选,方便查看,核心计算用时间戳即可) df['checkoutTime'] = pd.to_datetime(df['out_ts'], unit='s', origin='unix') df['checkinTime'] = pd.to_datetime(df['in_ts'], unit='s', origin='unix') # 步骤1:拆出借出和归还事件 checkout_events = df[['ID', 'out_ts']].rename(columns={'out_ts': 'event_ts'}) checkout_events['delta'] = 1 checkin_events = df[['ID', 'in_ts']].rename(columns={'in_ts': 'event_ts'}) checkin_events['delta'] = -1 # 合并所有事件并按时间排序 all_events = pd.concat([checkout_events, checkin_events]).sort_values(by=['ID', 'event_ts']) # 步骤2:计算滚动累计数 all_events['rolling_total'] = all_events.groupby('ID')['delta'].cumsum() # 步骤3:将结果关联回原数据集(按ID和借出时间匹配) df = df.merge(all_events[['ID', 'event_ts', 'rolling_total']], left_on=['ID', 'out_ts'], right_on=['ID', 'event_ts'], how='left') # 验证结果(和原rollingTotalCheckedOut列对比) print(df[['ID', 'out_ts', 'rollingTotalCheckedOut', 'rolling_total']])
结果说明
运行代码后,rolling_total列会和示例中的rollingTotalCheckedOut完全匹配。这种方法的优势是:
- 不受借还时长限制,哪怕借出时间跨数年也能正确统计
- 处理大型数据集效率较高,因为事件拆分和排序都是Pandas的高效操作
- 逻辑清晰,容易扩展到多ID的场景(只需按ID分组即可)
内容的提问来源于stack exchange,提问作者John Stud
相关产品推荐
相关产品推荐

