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

如何在Pandas中按条件从另一DataFrame生成求和列(类Excel SUMIFS)

批量计算参考日期前后7天的商品总销量问题

需求说明

现有两个DataFrame:

  • sales:销售数据表,包含商品类型(Fruit)、销售日期(Date)和销量(Quantity)
  • ref:参考表,包含商品类型(Fruit)和参考日期(Date)

需要给ref新增一列Total,显示对应商品在参考日期前后7天内的总销量。

示例数据

import pandas as pd

sales = pd.DataFrame({'Fruit': {0: 'apples',  1: 'oranges',  2: 'pears',  3: 'apples',  4: 'apples',  5: 'bananas',  6: 'oranges', 7: 'pears',  8: 'pears',  9: 'oranges',  10: 'bananas',  11: 'apples',  12: 'pears',  13: 'pears', 14: 'apples',  15: 'pears',  16: 'oranges',  17: 'oranges',  18: 'pears'},
                      'Date': {0: '2023-07-07',  1: '2023-02-05',  2: '2023-08-16',  3: '2023-07-26',  4: '2023-07-14', 5: '2024-02-01',  6: '2023-09-19',  7: '2023-04-08',  8: '2023-06-08',  9: '2023-05-15', 10: '2023-10-20',  11: '2023-07-25',  12: '2023-07-31',  13: '2023-10-08',  14: '2023-06-28', 15: '2023-08-15',  16: '2023-05-14',  17: '2023-07-28',  18: '2023-07-29'},
                      'Quantity': {0: 18,  1: 10,  2: 10,  3: 20,  4: 16,  5: 14,  6: 18,  7: 18,  8: 14,  9: 19,  10: 16, 11: 16,  12: 17,  13: 10,  14: 16,  15: 15,  16: 18,  17: 20,  18: 19}})
sales['Date'] = pd.to_datetime(sales['Date'])

ref = pd.DataFrame({'Fruit': {0: 'apples', 1: 'bananas',  2: 'oranges',  3: 'apples',  4: 'pears', 5: 'oranges',  6: 'bananas',  7: 'oranges',  8: 'oranges'}, 
                    'Date': {0: '2023-07-25',  1: '2023-12-27',  2: '2023-07-13',  3: '2023-06-27',  4: '2023-07-08',  5: '2023-09-17', 6: '2023-10-25',  7: '2023-10-05',  8: '2023-04-14'}})
ref['Date'] = pd.to_datetime(ref['Date'])

比如ref第一行(apples,2023-07-25)的Total应为36,对应sales中2023-07-25的16个苹果和2023-07-26的20个苹果之和。

对应Excel公式:

=SUMIFS(sales.Quantity, sales.Fruit, ref.Fruit, sales.Date, ">="&ref.Date-7, sales.Date, "<="&ref.Date+7)

遇到的问题

单行计算时逻辑正常,比如:

# 硬编码参数
sales[(sales['Fruit']=='apples')&
      (sales['Date']>=pd.to_datetime('2023-07-25')-pd.to_timedelta(7, unit='d'))&
      (sales['Date']<=pd.to_datetime('2023-07-25')+pd.to_timedelta(7, unit='d'))]['Quantity'].sum()

# 用iloc取ref第一行
sales[(sales['Fruit']==ref.iloc[0,0])&
      (sales['Date']>=ref.iloc[0,1]-pd.to_timedelta(7, unit='d'))&
      (sales['Date']<=ref.iloc[0,1]+pd.to_timedelta(7, unit='d'))]['Quantity'].sum()

但批量计算时执行以下代码报错ValueError: Can only compare identically-labeled Series objects:

ref['Total'] = sales[(sales['Fruit']==ref.iloc[ref.index,0])&
      (sales['Date']>=ref.iloc[ref.index,1]-pd.to_timedelta(7, unit='d'))&
      (sales['Date']<=ref.iloc[ref.index,1]+pd.to_timedelta(7, unit='d'))]['Quantity'].sum()

错误原因:ref.iloc[ref.index,0]的写法错误,iloc接收的行索引是单个位置或切片,用ref.index会返回整个索引序列,导致生成的Series和sales的Series维度不匹配,无法直接比较。

解决方案

方法1:用apply逐行处理(直观易理解)

把单行计算的逻辑封装成函数,用apply遍历ref的每一行:

def get_total_sales(row):
    # 生成匹配条件的掩码
    match_mask = (sales['Fruit'] == row['Fruit']) & \
                 (sales['Date'] >= row['Date'] - pd.Timedelta(days=7)) & \
                 (sales['Date'] <= row['Date'] + pd.Timedelta(days=7))
    # 返回符合条件的销量总和
    return sales.loc[match_mask, 'Quantity'].sum()

# 新增Total列
ref['Total'] = ref.apply(get_total_sales, axis=1)

方法2:合并后分组求和(适合中小数据量)

先按商品类型合并两个表,过滤日期范围后再分组求和:

# 按Fruit合并,区分两个表的日期列
merged_df = ref.merge(sales, on='Fruit', suffixes=('_ref', '_sales'))

# 过滤出参考日期前后7天的销售记录
filtered = merged_df[(merged_df['Date_sales'] >= merged_df['Date_ref'] - pd.Timedelta(days=7)) & 
                     (merged_df['Date_sales'] <= merged_df['Date_ref'] + pd.Timedelta(days=7))]

# 按ref的每一行(Fruit+Date_ref)分组求和
total_sales = filtered.groupby(['Fruit', 'Date_ref'])['Quantity'].sum().reset_index(name='Total')

# 合并回ref,没有匹配的销量填0
ref = ref.merge(total_sales, 
                left_on=['Fruit', 'Date'], 
                right_on=['Fruit', 'Date_ref'], 
                how='left').fillna(0)

# 清理多余列
ref = ref.drop('Date_ref', axis=1)

方法3:向量化广播计算(效率最高,适合大数据量)

利用numpy的广播特性,一次性完成所有条件判断和求和:

# 转换为numpy数组,方便广播操作
sales_fruit = sales['Fruit'].to_numpy()
sales_date = sales['Date'].to_numpy()
sales_qty = sales['Quantity'].to_numpy()

ref_fruit = ref['Fruit'].to_numpy()[:, None]  # 转成列向量,实现广播
ref_date = ref['Date'].to_numpy()[:, None]

# 生成全局掩码:商品匹配 + 日期在前后7天内
mask = (sales_fruit == ref_fruit) & \
       (sales_date >= ref_date - pd.Timedelta(days=7)) & \
       (sales_date <= ref_date + pd.Timedelta(days=7))

# 按ref的行求和
ref['Total'] = (sales_qty * mask).sum(axis=1)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 15:05:00