自定义季度起始日期的Pandas DataFrame分组求和方案问询
自定义起始日期的季度分组求和方案
需求说明
我有一个包含网站日访问量(DailyViews)和日使用时长(DailyMinutes)的Pandas DataFrame,需要按季度分组求和,但有两个特殊要求:
- 季度起始为非标准日期(例如Q1为2月27日至5月26日)
- 支持指定任意月日作为季度起始,适配不同网站跨度较大的日期范围
数据复现代码
import random import pandas as pd date_range = pd.date_range(start='2018-1-1', end='2022-10-03') daily_views = [random.randint(1000,9999) for i in range(len(date_range))] daily_minutes = [random.randint(1000,9999) for i in range(len(date_range))] df = pd.DataFrame( {'DailyViews': daily_views, 'DailyMinutes': daily_minutes}, index=date_range, )
尝试过的无效方法
- 使用
df_grouped = df.groupby(df.index.shift(freq='Q')).sum():无法实现精细的季度偏移控制 - 使用
df_resampled = df.resample('Q', convention='end', offset=datetime.timedelta(days=25)).sum():修改offset后未达到预期效果
当前手动用apply()逐行判断的方法效率极低,还有一个仅支持整月调整的临时方案,但无法处理精确到日的起始,且生成的季度字符串转datetime会被识别为标准季度。
高效解决方案
方法1:自定义函数生成季度标签(简洁易用)
通过日期计算确定每个日期所属的自定义季度,避免逐行循环:
import numpy as np def get_custom_quarter(dt, start_month=2, start_day=27): # 生成当前年份的季度起始日期 start_date = pd.Timestamp(year=dt.year, month=start_month, day=start_day) # 若当前日期早于当年起始日,用上一年的起始日作为基准 if dt < start_date: start_date = start_date - pd.DateOffset(years=1) # 计算与基准日的天数差,每91天划分为一个季度(近似自然季度长度) days_diff = (dt - start_date).days quarter_num = (days_diff // 91) + 1 # 确定季度所属年份 quarter_year = start_date.year + (days_diff // 365) return f"{quarter_year}Q{quarter_num}" # 生成自定义季度标签 df['CustomQuarter'] = df.index.map(lambda x: get_custom_quarter(x, start_month=2, start_day=27)) # 按自定义季度分组求和 df_grouped = df.groupby('CustomQuarter')[['DailyViews', 'DailyMinutes']].sum()
方法2:向量化操作(超大数据量适用)
用向量化计算替代map,进一步提升效率:
start_month = 2 start_day = 27 # 生成每个索引对应的当年起始基准日 base_dates = pd.to_datetime( df.index.year.astype(str) + '-' + str(start_month) + '-' + str(start_day) ) # 调整早于当年起始日的基准为上一年 base_dates = np.where(df.index < base_dates, base_dates - pd.DateOffset(years=1), base_dates) base_dates = pd.DatetimeIndex(base_dates) # 计算每个日期与基准日的季度间隔 quarters_since_base = ((df.index - base_dates) / pd.Timedelta(days=91)).astype(int) + 1 # 计算季度所属年份 quarter_years = base_dates.year + ((df.index - base_dates) / pd.Timedelta(days=365)).astype(int) # 生成季度标签并分组求和 df['CustomQuarter'] = quarter_years.astype(str) + 'Q' + quarters_since_base.astype(str) df_grouped = df.groupby('CustomQuarter').sum()
附加问题:设置"2022Q1"格式的索引
分组后直接用生成的CustomQuarter字符串作为索引,就是"YYYYQX"格式。如果索引是datetime类型,可通过格式化转换:
# 若索引为datetime类型,转为自定义格式 df_grouped.index = df_grouped.index.strftime('%YQ%q')
内容的提问来源于stack exchange,提问作者dg141
相关产品推荐
相关产品推荐

