在Pandas中按行匹配日期范围求和日销售值至月度付款列
解决Pandas中自定义日历下的日期范围求和问题
嘿,这个需求确实有点棘手,尤其是那个特殊的35/28/28天循环的月份规则,不过咱们一步步拆解来搞定它:
核心思路
因为常规日期系统不匹配你的自定义月份天数,所以我们得先把所有日期转换成基于自定义日历的绝对累计天数,这样就能轻松判断某个日期是否落在指定的付款起止月份范围内,最后再对符合条件的sample_sales求和。
具体步骤&代码实现
1. 定义自定义日历的天数映射
首先我们要把每个“月份”转换成对应的绝对天数区间,比如第1个月是0-34天(共35天),第2个月是35-62天(28天),第3个月是63-90天(28天),第4个月又回到35天的循环,以此类推:
import pandas as pd import numpy as np # 自定义月份天数循环规则 month_days_cycle = [35, 28, 28] # 输入月份数,返回该月份对应的绝对天数起始和结束值 def get_month_date_range(month_num): cycle_length = len(month_days_cycle) # 确定当前月份在循环中的位置 cycle_idx = (month_num - 1) % cycle_length # 计算当前月份之前所有月份的总天数 total_days_before = sum( month_days_cycle[(i-1) % cycle_length] for i in range(1, month_num) ) start_day = total_days_before end_day = total_days_before + month_days_cycle[cycle_idx] - 1 return start_day, end_day
2. 转换日期列与付款起止列
假设你的数据中:
date列是类似"1-5"(第1个月第5天)的格式,或者是拆分好的月份+天数字段payment_start和payment_end是月份编号(比如1代表第1个月)
我们把这些都转换成绝对天数:
# 给数据添加付款起止的绝对天数列 df['payment_start_abs'] = df['payment_start'].apply(lambda x: get_month_date_range(x)[0]) df['payment_end_abs'] = df['payment_end'].apply(lambda x: get_month_date_range(x)[1]) # 把date列转换成绝对天数(这里假设date是"月-日"格式,可根据你的实际格式调整) def date_to_absolute_day(date_str): month, day = map(int, date_str.split('-')) month_start, _ = get_month_date_range(month) return month_start + day - 1 df['date_abs'] = df['date'].apply(date_to_absolute_day)
3. 计算符合条件的销售额总和
这里提供两种实现方式,按需选择:
方式一:用apply(适合小数据集,逻辑直观)
对每一行,筛选出所有日期落在当前行付款范围内的记录,然后求和:
def calculate_monthly_payment(row): # 生成筛选掩码:date_abs在当前行的付款起止范围内 mask = (df['date_abs'] >= row['payment_start_abs']) & (df['date_abs'] <= row['payment_end_abs']) return df.loc[mask, 'sample_sales'].sum() df['monthly_payment'] = df.apply(calculate_monthly_payment, axis=1)
方式二:用区间连接(适合大数据集,效率更高)
利用Pandas的IntervalIndex做区间匹配,避免逐行循环:
# 先按日期聚合销售额(如果同一个日期有多条记录的话) date_sales = df[['date_abs', 'sample_sales']].groupby('date_abs').sum().reset_index() # 生成付款起止的区间索引 payment_intervals = pd.IntervalIndex.from_arrays( df['payment_start_abs'], df['payment_end_abs'], closed='both' # 包含起止日期 ) # 按区间分组求和,再映射回原数据 interval_sums = date_sales.groupby( pd.cut(date_sales['date_abs'], payment_intervals) )['sample_sales'].sum() # 把结果赋值给monthly_payment列 df['monthly_payment'] = interval_sums.reindex(payment_intervals).values
注意事项
- 如果你的
date列格式不是"月-日",可以调整date_to_absolute_day函数的逻辑,比如如果是单独的month和day列,直接传入这两个值即可。 - 如果
payment_start和payment_end不是月份编号,而是其他格式(比如自定义的日期字符串),需要先把它们转换成对应的月份数再处理。
内容的提问来源于stack exchange,提问作者denister
相关产品推荐
相关产品推荐

