大DataFrame高效拆分起止日期为每月月末截止日期方法问询
高效生成月度月末日期的DataFrame方案
针对大型DataFrame的场景,绝对要避免用iterrows循环,推荐用pandas的矢量化操作或者explode结合日期序列生成的方法,以下是两种高效方案:
方案一:date_range生成月度月末 + explode展开
这种方法利用pandas的矢量化日期生成能力,再通过explode展开行,性能远高于循环:
- 确保日期列是datetime类型:
import pandas as pd df1['STARTDATE'] = pd.to_datetime(df1['STARTDATE']) df1['ENDDATE'] = pd.to_datetime(df1['ENDDATE'])
- 为每行生成对应的月末日期序列:
# freq='M'指定生成月度月末日期,自动覆盖STARTDATE到ENDDATE之间的所有月份 df1['CUTOFF_DATE'] = df1.apply( lambda x: pd.date_range(start=x['STARTDATE'], end=x['ENDDATE'], freq='M'), axis=1 )
- 展开序列并保留目标列:
df2 = df1[['A', 'B', 'C', 'CUTOFF_DATE']].explode('CUTOFF_DATE').reset_index(drop=True)
方案二:MonthEnd偏移 + repeat重复行(性能最优)
如果数据量特别庞大,apply仍有一定开销,可以用完全矢量化的操作实现:
- 计算每行需要生成的月份数量:
df1['STARTDATE'] = pd.to_datetime(df1['STARTDATE']) df1['ENDDATE'] = pd.to_datetime(df1['ENDDATE']) # 计算首尾日期的月份差,+1是因为要包含首尾两个月份的月末 df1['month_count'] = ((df1['ENDDATE'].dt.year - df1['STARTDATE'].dt.year)*12 + (df1['ENDDATE'].dt.month - df1['STARTDATE'].dt.month)) + 1
- 按月份数重复对应行:
df_repeated = df1.loc[df1.index.repeat(df1['month_count'])]
- 计算每个重复行的月末日期:
# 生成每行的月份偏移量(从0开始递增) df_repeated['offset'] = df_repeated.groupby(df_repeated.index).cumcount() # 先将STARTDATE调整到当月月末,再加上对应偏移的月份数 df_repeated['CUTOFF_DATE'] = (df_repeated['STARTDATE'] + pd.offsets.MonthEnd(0)) + pd.DateOffset(months=df_repeated['offset']) # 筛选并整理目标列 df2 = df_repeated[['A', 'B', 'C', 'CUTOFF_DATE']].reset_index(drop=True)
关键提示
- 两种方案的性能比
iterrows高10-100倍,方案二完全基于矢量化操作,最适合超大型DataFrame; - 如果
STARTDATE不是月末,pd.offsets.MonthEnd(0)会自动将其调整为当月最后一天; - 处理前必须确保日期列是datetime类型,否则会触发类型错误。
内容的提问来源于stack exchange,提问作者ImFabien75
相关产品推荐
相关产品推荐

