You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Pandas中计算不跨两个月的周数并生成周变量?

解决Pandas中生成不跨月的年内周数问题

要实现周数不跨月的需求(比如2023年1月30-31日归为第5周,2月1日起归为第6周),需要自定义周区间逻辑,替代默认的ISO周计算,同时用Pandas矢量化操作提升效率。

核心思路

确保每个周的所有日期都属于同一个月份:

  • 以周一作为周起始(和你原函数逻辑一致)
  • 当标准周一到周日的周跨月时,拆分该周:当月剩余日期单独算一个周,下月起始日期算下一个周
  • 周数按年内累计编号

完整实现代码

import pandas as pd
import datetime

def generate_non_crossing_weeks(year):
    weeks = []
    current_week_num = 1
    # 找到当年第一个周一
    first_day_of_year = datetime.date(year, 1, 1)
    first_monday_of_year = first_day_of_year + datetime.timedelta(days=(0 - first_day_of_year.weekday()) % 7)
    
    # 处理1月1日到第一个周一前的单独日期(可选,这里设为第0周)
    jan_first = datetime.date(year, 1, 1)
    if jan_first < first_monday_of_year:
        weeks.insert(0, (0, jan_first, first_monday_of_year - datetime.timedelta(days=1)))
    
    current_day = first_monday_of_year
    while current_day.year == year:
        month = current_day.month
        last_day_of_month = datetime.date(current_day.year, month, 1) + pd.offsets.MonthEnd(0)
        last_day_of_month = last_day_of_month.date()
        
        week_end = current_day + datetime.timedelta(days=6)
        if week_end <= last_day_of_month:
            weeks.append((current_week_num, current_day, week_end))
            current_week_num += 1
            current_day = week_end + datetime.timedelta(days=1)
        else:
            # 拆分跨月周:当月部分单独成周
            weeks.append((current_week_num, current_day, last_day_of_month))
            current_week_num += 1
            # 跳到下月第一天继续
            current_day = datetime.date(current_day.year, month+1, 1) if month <12 else datetime.date(year+1,1,1)
    
    return weeks

# ----------------------
# 测试用例
# ----------------------
# 创建测试数据集
dates = pd.date_range(start='2023-01-29', end='2023-02-02')
dataset = pd.DataFrame({'starttime': dates})

# 转换日期列格式
dataset['starttime'] = pd.to_datetime(dataset['starttime'])

# 生成2023年不跨月周的区间
weeks_df = pd.DataFrame(generate_non_crossing_weeks(2023), columns=['week_DP_2023', 'week_start', 'week_end'])
weeks_df['week_start'] = pd.to_datetime(weeks_df['week_start'])
weeks_df['week_end'] = pd.to_datetime(weeks_df['week_end'])

# 排序后匹配周数(merge_asof要求左表按on字段排序)
dataset = dataset.sort_values('starttime')
dataset = pd.merge_asof(dataset, weeks_df, left_on='starttime', right_on='week_start', direction='backward')
dataset = dataset[dataset['starttime'] <= dataset['week_end']]

# 查看结果
print(dataset[['starttime', 'week_DP_2023']])

运行结果

starttime  week_DP_2023
0 2023-01-29              4
1 2023-01-30              5
2 2023-01-31              5
3 2023-02-01              6
4 2023-02-02              6

完全符合需求:1月30-31日为第5周,2月1日起为第6周。

原代码的问题说明

  1. 跨月周问题:你原函数getDateRangeFromWeek用的是ISO周逻辑,会生成跨月的周(比如2023年第5周是2023-01-30到2023-02-05),不符合不跨月的要求。
  2. 效率问题:循环赋值的方式在大数据集下效率极低,Pandas的merge_asof是矢量化操作,速度快得多。
  3. 语法错误:Python没有内置的frange函数,应该用range(1, 54)来遍历1-53周。

内容的提问来源于stack exchange,提问作者Ajay Mishra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:40:59