如何基于另一DataFrame的日期区间对DataFrame数据求和?
高效计算Pandas DataFrame区间总和的方法
给定以下两个Pandas DataFrame:
import pandas as pd df1 = pd.DataFrame({'Name': ['A', 'B', 'C'], 'Date1':['2023-01-01', '2023-01-02', '2023-01-03'], 'Date2':['2023-01-03', '2023-01-04', '2023-01-05']}) df1.loc[:, ['Date1', 'Date2']] = df1.loc[:, ['Date1', 'Date2']].apply(pd.to_datetime, errors='coerce') df2 = pd.DataFrame({'Date': ['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04', '2023-01-05'], 'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10], 'C': [11,12,13,14,15]}) df2['Date'] = pd.to_datetime(df2['Date'])
需要为df1添加Sum列,计算df2中对应Name的数据在Date1(不含)至Date2(含)区间内的总和,期望结果如下:
df_result = pd.DataFrame({'Name': ['A', 'B', 'C'], 'Date1':['2023-01-01', '2023-01-02', '2023-01-03'], 'Date2':['2023-01-03', '2023-01-04', '2023-01-05'], 'Sum': [5, 17, 29]})
高效矢量化实现方案
这种方法避免逐行循环,通过长格式转换+累积和+区间匹配实现,适合大数据量场景:
- 将
df2转换为长格式,统一Name和Value字段:
df2_long = df2.melt(id_vars='Date', var_name='Name', value_name='Value')
- 按
Name和Date排序,计算每个分组的累积和:
df2_long = df2_long.sort_values(['Name', 'Date']) df2_long['CumSum'] = df2_long.groupby('Name')['Value'].cumsum()
- 用
merge_asof匹配df1中Date1和Date2对应的累积和,进而计算区间差:
# 匹配Date1对应的累积和(Date <= Date1的总和) df1_sorted = df1.sort_values(['Name', 'Date1']) df1_sorted = pd.merge_asof(df1_sorted, df2_long[['Name', 'Date', 'CumSum']], left_on='Date1', right_on='Date', by='Name', direction='backward') df1_sorted.rename(columns={'CumSum': 'Sum_Date1'}, inplace=True) # 匹配Date2对应的累积和(Date <= Date2的总和) df1_sorted = pd.merge_asof(df1_sorted, df2_long[['Name', 'Date', 'CumSum']], left_on='Date2', right_on='Date', by='Name', direction='backward') df1_sorted.rename(columns={'CumSum': 'Sum_Date2'}, inplace=True) # 计算区间总和:Sum_Date2 - Sum_Date1 df1_sorted['Sum'] = df1_sorted['Sum_Date2'] - df1_sorted['Sum_Date1'] # 恢复原索引和列顺序 df1 = df1_sorted.sort_index()[['Name', 'Date1', 'Date2', 'Sum']]
执行后df1的结果与期望一致,该方案的时间复杂度主要来自排序(O(n log n)),远高于逐行循环的效率。
简易实现方案(小数据量适用)
如果数据量较小,可直接用apply逐行计算,代码更简洁但效率较低:
def get_interval_sum(row): # 筛选Date在(Date1, Date2]区间内的行 date_mask = (df2['Date'] > row['Date1']) & (df2['Date'] <= row['Date2']) return df2.loc[date_mask, row['Name']].sum() df1['Sum'] = df1.apply(get_interval_sum, axis=1)
内容的提问来源于stack exchange,提问作者CTXR
相关产品推荐
相关产品推荐

