如何根据起始日期与当前日期之间的周数重复数据行

5次阅读

如何根据起始日期与当前日期之间的周数重复数据行

本文介绍如何基于每行记录的日期与当前日期之间的完整周数,对 dataframe 行进行智能重复,并为每一重复行生成对应的 iso 周编号(含跨年处理),避免手动循环和索引错位问题。

数据分析中,常需将单条时间记录“展开”为按周粒度的多行数据——例如,为每个 ID 生成从其原始日期起、持续至当前周的全部周序列。关键挑战在于:既要准确计算周数差(支持跨年),又要为每行分配正确的 ISO 年份与周号(isocalendar().year 和 .week),而非简单递增数字

原方法使用 for 循环修改 loc 并依赖 ffill(),存在两大缺陷:

  • ✅ 错误复用全局索引(如 final.loc[i, ‘WEEK’] 覆盖非目标行);
  • ❌ 忽略 ISO 周的语义(如 2023-12-24 属于 ISO 第51周,但下一周是2023年第52周或2024年第1周,不可线性+1)。

正确解法应以时间为本体:对每行原始日期,逐周递增7天,再统一提取其 ISO 周信息。以下是推荐实现:

import pandas as pd  # 构建示例数据 df = pd.DataFrame({     'ID': ['ID001', 'ID002', 'ID003'],     'date': ['24/12/2023', '01/02/2024', '12/02/2024'] }) df['DATE'] = pd.to_datetime(df['DATE'], dayfirst=True)  # 步骤1:计算每行需重复的周数(含起始周) now = pd.Timestamp('now') n_weeks = (now.to_period('W') - df['DATE'].dt.to_period('W')).apply(lambda x: x.n)  # 步骤2:按周数重复行(+1 包含起始周本身) repeated = df.loc[df.index.repeat(n_weeks + 1)].copy()  # 步骤3:为每组重复行添加递增的天数偏移(0, 7, 14, ...),并计算对应ISO周 group_cumcount = repeated.groupby(level=0).cumcount()  # 每组内计数:0,1,2,... offset_days = group_cumcount * 7 repeated['DATE_WEEK'] = df['DATE'].iloc[repeated.index].values + pd.to_timedelta(offset_days, unit='D') repeated['YEAR'] = repeated['DATE_WEEK'].dt.isocalendar().year repeated['WEEK'] = repeated['DATE_WEEK'].dt.isocalendar().week  # 最终结果(保留原始ID、原始DATE用于业务标识,但WEEK基于实际周日期计算) result = repeated[['ID', 'DATE', 'YEAR', 'WEEK']].reset_index(drop=True) print(result)

? 关键说明: to_period(‘W’) 将日期转为 ISO 周周期对象,相减直接得整数周差(自动处理跨年、闰周); groupby(level=0).cumcount() 精准获取每组重复行内的序号(0起始),避免索引污染; DATE_WEEK 是逻辑上的“第N周对应的实际日期”,由此派生的 YEAR/WEEK 严格符合 ISO 8601 标准(如2023-12-24所在周为2023-W51,+7天后为2023-W52,+14天后为2024-W01); 输出中 DATE 列保持原始值(便于溯源),YEAR/WEEK 列反映各周的真实归属。

优势总结

  • 向量化运算,性能远超显式循环;
  • 完全规避索引操作风险;
  • 原生支持 ISO 周跨年逻辑;
  • 代码简洁,可读性与可维护性高。

此方法适用于销售预测归因、用户活跃周分析、计划排期展开等场景,是时间维度数据展开的标准实践。

text=ZqhQzanResources