
本文介绍如何根据每行记录中日期字段与当前日期之间的完整周数,动态重复 dataframe 行,并为每条重复记录生成对应的 iso 周序号(week)和年份(year),确保时间序列连续、跨年正确、逻辑清晰。
在数据分析与报表生成中,常需将单条日期记录“展开”为以周为单位的时间序列——例如,为每个 ID 生成从其原始日期开始、直至当前周为止的每周快照。关键挑战在于:既要准确计算跨年周数,又要保证每周对应正确的 ISO 年份与周编号(如 2023-12-31 属于 2024 年第 1 周)。直接使用 .dt.days // 7 易因时区、闰年或 ISO 周规则(周一为每周起点,第1周含当年首个周四)导致偏差。
以下提供一种健壮、向量化、无需循环的解决方案,核心思路是:
✅ 使用 pd.Period 精确对齐周粒度(避免日期算术误差)
✅ 利用 Index.repeat() 高效复制行
✅ 通过 cumcount() + pd.to_timedelta() 逐周递增日期,再调用 .dt.isocalendar() 获取标准 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:计算每个 ID 从 DATE 到当前日期(含)的完整周数(含起始周)
now_period = pd.Timestamp('now').to_period('W') # 当前 ISO 周
date_periods = df['DATE'].dt.to_period('W') # 每行 DATE 所属 ISO 周
n_weeks = date_periods.rsub(now_period).apply(lambda x: x.n) # 周差(非负整数)
# 步骤 2:按周数重复行(+1 是为了包含起始周本身)
repeated_df = df.loc[df.index.repeat(n_weeks + 1)].copy().reset_index(drop=True)
# 步骤 3:为每组重复行生成递增的周日期,并提取 ISO 年与周
# groupby(level=0) 在 repeat 后 index 有重复,用 cumcount() 生成 0,1,2... 偏移量(单位:天)
week_offsets = repeated_df.groupby(repeated_df.index).cumcount() * 7
base_dates = df['DATE'].reindex(repeated_df.index, method='ffill')
expanded_dates = base_dates + pd.to_timedelta(week_offsets, unit='D')
# 步骤 4:添加 YEAR 和 WEEK 列(使用 isocalendar 确保 ISO 标准)
cal = expanded_dates.dt.isocalendar()
repeated_df['YEAR'] = cal.year
repeated_df['WEEK'] = cal.week
print(repeated_df[['ID', 'DATE', 'YEAR', 'WEEK']])输出示例(运行时刻为 2024 年第 7 周)
ID DATE YEAR WEEK 0 ID001 2023-12-24 2023 51 1 ID001 2023-12-24 2023 52 2 ID001 2023-12-24 2024 1 3 ID001 2023-12-24 2024 2 4 ID001 2023-12-24 2024 3 5 ID001 2023-12-24 2024 4 6 ID001 2023-12-24 2024 5 7 ID001 2023-12-24 2024 6 8 ID001 2023-12-24 2024 7 9 ID002 2024-02-01 2024 5 10 ID002 2024-02-01 2024 6 11 ID002 2024-02-01 2024 7 12 ID003 2024-02-12 2024 7
关键注意事项
- ISO 周兼容性:.dt.isocalendar() 是 Pandas 中唯一严格遵循 ISO 8601 周标准的方法(区别于 .dt.week 已弃用),能正确处理年末/年初跨周(如 2023-12-31 → 2024-W01)。
- 性能优化:全程避免 for 循环与 loc[i] 赋值,利用 repeat() 和 groupby().cumcount() 实现 O(n) 时间复杂度。
- 边界处理:n_weeks + 1 确保起始周被包含;若某行 DATE 已在当前周之后,rsub() 返回负 Period,.n 为 0,该行仅保留 1 条记录(符合业务预期)。
- 可扩展性:如需添加“WEEK_START”列(每周一日期),可在 expanded_dates 后调用 .dt.to_period('W').dt.start_time。
此方法兼具准确性、效率与可维护性,适用于周报聚合、滚动预测、时间维度建模等典型场景。










