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

基于业务日的按组带条件反向累积起始日期计算

按组反向计算工作日起始日期(保留已有有效日期)

原始DataFrame

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

需求说明

  • 按Group列分组,对Start为NaT的行从下往上计算起始日期
  • 以组内首个非NaT的Start值(从下往上数第一个有效日期)作为反向累积计算的起点,组切换时停止计算
  • LT代表工作日,计算时需跳过周末
  • 必须保留已有有效Start值(此类行的LT值无效)
  • 每组最后一行必有有效Start日期,因此必有计算起点

期望输出

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

已尝试的代码及问题

尝试1:按组筛选NaT行但未跳过周末

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]

问题:直接用自然日减法,未跳过周末,不符合LT为工作日的要求。

尝试2:支持工作日计算但覆盖有效日期

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))

问题:覆盖所有已有有效Start值,仅保留每组最后一行的计算结果,违反保留原始有效日期的需求。

解决方案

代码实现

import pandas as pd

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

def fill_group_dates(group):
    # 反转组内顺序,从下往上处理
    reversed_group = group.iloc[::-1].copy()
    cum_workdays = 0
    last_valid_date = None
    
    for idx, row in reversed_group.iterrows():
        if pd.notna(row['Start']):
            # 记录当前有效日期,重置累积工作日偏移
            last_valid_date = row['Start']
            cum_workdays = 0
        else:
            # 累积工作日,计算当前行的起始日期
            cum_workdays += row['LT']
            reversed_group.loc[idx, 'Start'] = last_valid_date - pd.offsets.BusinessDay(cum_workdays)
    
    # 反转回原顺序返回
    return reversed_group.iloc[::-1]

# 按Group分组处理,保留原始索引
df = df.groupby('Group', group_keys=False).apply(fill_group_dates)

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

思路说明

  1. 日期类型转换:先将Start列转为datetime类型,避免字符串格式导致的计算错误
  2. 组内反向遍历:对每个组反转行顺序,从下往上处理,确保以首个有效日期(从下数)为计算起点
  3. 累积工作日计算:仅对NaT行累积LT值,用最近的有效日期减去累积工作日偏移(自动跳过周末)
  4. 保留有效日期:遇到有效日期时重置累积偏移,不修改原始有效日期
  5. 格式还原:计算完成后将日期转回原字符串格式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 23:31:04