基于分组的动态起始行反向累计工作日计算问题
问题描述
核心需求差异
- 此前需求:每组以最后一行作为起始日期反向计算
- 当前需求:每组以首个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)
代码解释
- 类型转换:先将Start列转为datetime类型,避免字符串格式导致的计算错误
- 分组处理函数:
- 用
first_valid_index()精准定位每组的计算起点 - 仅处理起点上方的行,下方行保持原日期不变
- 反向累积LT值后,通过
pd.offsets.BusinessDay将数值转换为工作日偏移量 - 用起点日期减去偏移量得到结果,再恢复原顺序赋值
- 用
- 结果格式化:将计算后的日期转为要求的dd-mm-yyyy字符串格式
内容的提问来源于Stack Exchange,提问作者Prmake
相关产品推荐
相关产品推荐

