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

基于分组的动态起始行反向累计工作日计算问题

问题描述

核心需求差异

  • 此前需求:每组以最后一行作为起始日期反向计算
  • 当前需求:每组以首个Start列非NaN的行作为起始日期,反向按工作日(跳过周末)计算上方行的Start值,且忽略起点之后的所有日期

示例数据

Group    LT         Start
0      A     5           NaT
1      A    10           NaT
2      A     0    20-03-2024
3      B     3           NaT
4      B     0    04-03-2024
5      C     2           NaT
6      C     4           NaT
7      C     0    04-04-2024
8      C     0    10-04-2024
9      C     0    24-04-2024

计算规则

  • 每组定位首个Start不为NaN的行作为计算起点
  • 起点之后的行(组内位置在起点下方的)保持原有Start值,不参与计算
  • 从起点向上(反向),用起点日期减去对应LT值的工作日天数,得到上方行的Start值
  • LT代表工作日数,计算时自动跳过周末

预期输出

Group    LT         Start
0      A     5    28-02-2024
1      A    10    06-03-2024
2      A     0    20-03-2024
3      B     3    28-02-2024
4      B     0    04-03-2024
5      C     2    27-03-2024
6      C     4    29-03-2024
7      C     0    04-04-2024
8      C     0    10-04-2024
9      C     0    24-04-2024

现有代码的问题

第一段代码(起点正确,但未处理工作日)

能正确定位每组首个非NaN的Start行,但用自然日计算,未区分工作日:

groups = df.groupby('Group')
nan_start_mask = df['Start'].isna()
df['Start'] = groups['Start'].bfill()
start_date = df['Start'] - pd.to_timedelta(df['LT'], unit='D')
df.loc[nan_start_mask, 'Start'] = start_date[nan_start_mask]

第二段代码(工作日处理正确,但起点错误)

能正确计算工作日,但始终以每组最后一行作为起点,不符合当前需求:

s = (df.loc[::-1, 'LT'].groupby(df['Group']).cumsum().apply(pd.offsets.BusinessDay))
g = df.groupby('Group')
df['Start'] = pd.to_datetime(g['Start'].transform('first').sub(s))

解决方案代码

import pandas as pd

# 转换Start列为datetime类型,确保日期计算有效
df['Start'] = pd.to_datetime(df['Start'], format='%d-%m-%Y')

def process_group(group):
    # 定位组内首个非NaN的Start行索引
    first_valid_idx = group['Start'].first_valid_index()
    if pd.isna(first_valid_idx):
        return group  # 无有效日期的组直接返回
    
    # 获取起点日期
    base_date = group.loc[first_valid_idx, 'Start']
    
    # 仅处理起点上方的行(索引小于起点的行)
    upper_rows_mask = group.index < first_valid_idx
    if not upper_rows_mask.any():
        return group
    
    # 反向累积LT值(从起点往上),转换为工作日偏移
    reversed_cum_lt = group.loc[upper_rows_mask, 'LT'][::-1].cumsum()
    calculated_dates = base_date - reversed_cum_lt.apply(pd.offsets.BusinessDay)
    
    # 将计算结果按原顺序赋值回组
    group.loc[upper_rows_mask, 'Start'] = calculated_dates[::-1].values
    
    return group

# 按Group分组处理,禁用组键避免额外列
df = df.groupby('Group', group_keys=False).apply(process_group)

# 格式化日期为dd-mm-yyyy格式
df['Start'] = df['Start'].dt.strftime('%d-%m-%Y')
print(df)

代码解释

  1. 类型转换:先将Start列转为datetime类型,避免字符串格式导致的计算错误
  2. 分组处理函数:
    • 用first_valid_index()精准定位每组的计算起点
    • 仅处理起点上方的行,下方行保持原日期不变
    • 反向累积LT值后,通过pd.offsets.BusinessDay将数值转换为工作日偏移量
    • 用起点日期减去偏移量得到结果,再恢复原顺序赋值
  3. 结果格式化:将计算后的日期转为要求的dd-mm-yyyy字符串格式

内容的提问来源于Stack Exchange,提问作者Prmake

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 00:03:26