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

Pandas按周分组时如何保留完整的时序记录?

解决方案:精确拆分跨周期停机事件并聚合

要解决跨周/跨月停机事件的时长拆分问题,核心思路是将单个跨周期事件拆分为多个对应周期的子事件,计算每个子事件的实际时长,之后再进行聚合。以下是基于Pandas的成熟实现方案:

步骤1:转换时间列类型

首先将字符串格式的开始/结束时间转为Pandas datetime类型,方便后续时间计算:

import pandas as pd
import numpy as np

# 示例DataFrame
df = pd.DataFrame({
    'RSNCODE': ['300.306', '100.102', '300.306'],
    'BEGTIME': ['2022-06-08 22:21:47', '2022-06-22 14:00:00', '2022-07-25 21:19:22'],
    'ENDTIME': ['2022-06-10 00:05:40', '2022-06-30 04:23:32', '2022-07-26 17:41:21'],
    'Reason': ['Planned shutdown', 'Shiftpatterns / Not planned shift days', 'Planned shutdown'],
    'Break_duration': [25.731667, 182.392500, 20.366667],
    'month': ['2022-06', '2022-06', '2022-07'],
    'week': ['2022-06-06/2022-06-12', '2022-06-20/2022-06-26', '2022-07-25/2022-07-31']
})

# 转换时间列
df['BEGTIME'] = pd.to_datetime(df['BEGTIME'])
df['ENDTIME'] = pd.to_datetime(df['ENDTIME'])

步骤2:拆分事件到对应周

定义函数将单个停机事件拆分为覆盖的所有周,计算每周内的实际停机时长:

def split_event_to_weeks(row):
    start = row['BEGTIME']
    end = row['ENDTIME']
    # 生成事件覆盖的所有周的起始日期(周一为周起始,可根据需求调整)
    week_starts = pd.date_range(
        start=start - pd.Timedelta(days=start.weekday()),
        end=end,
        freq='W-MON'
    )
    split_rows = []
    for week_start in week_starts:
        # 计算周结束时间(周日23:59:59)
        week_end = week_start + pd.Timedelta(days=6, hours=23, minutes=59, seconds=59)
        # 取事件与周区间的交集时间
        actual_start = max(start, week_start)
        actual_end = min(end, week_end)
        # 计算该周内的停机时长(小时)
        duration = (actual_end - actual_start).total_seconds() / 3600
        # 生成周标识
        week_label = f"{week_start.strftime('%Y-%m-%d')}/{week_end.strftime('%Y-%m-%d')}"
        # 组装子事件记录
        split_row = row.drop(['BEGTIME', 'ENDTIME', 'Break_duration', 'week']).to_dict()
        split_row.update({
            'week': week_label,
            'period_duration': duration,
            'period_start': actual_start,
            'period_end': actual_end
        })
        split_rows.append(split_row)
    return pd.DataFrame(split_rows)

# 拆分所有事件并合并结果
weekly_split_df = pd.concat(df.apply(split_event_to_weeks, axis=1).tolist(), ignore_index=True)

步骤3:按周聚合统计

现在可以正常按周进行聚合,比如统计每个故障代码每周的总停机时长:

weekly_agg = weekly_split_df.groupby(['week', 'RSNCODE'])['period_duration'].sum().reset_index()

扩展:拆分到月份

如果需要按月拆分,逻辑类似,只需调整周期生成逻辑:

def split_event_to_months(row):
    start = row['BEGTIME']
    end = row['ENDTIME']
    # 生成事件覆盖的所有月份的第一天
    month_starts = pd.date_range(
        start=start.replace(day=1),
        end=end,
        freq='MS'
    )
    split_rows = []
    for month_start in month_starts:
        # 计算月份最后一天的23:59:59
        month_end = (month_start + pd.DateOffset(months=1)) - pd.Timedelta(seconds=1)
        actual_start = max(start, month_start)
        actual_end = min(end, month_end)
        duration = (actual_end - actual_start).total_seconds() / 3600
        month_label = month_start.strftime('%Y-%m')
        # 组装子事件记录
        split_row = row.drop(['BEGTIME', 'ENDTIME', 'Break_duration', 'month']).to_dict()
        split_row.update({
            'month': month_label,
            'period_duration': duration,
            'period_start': actual_start,
            'period_end': actual_end
        })
        split_rows.append(split_row)
    return pd.DataFrame(split_rows)

# 拆分到月份并聚合
monthly_split_df = pd.concat(df.apply(split_event_to_months, axis=1).tolist(), ignore_index=True)
monthly_agg = monthly_split_df.groupby(['month', 'RSNCODE'])['period_duration'].sum().reset_index()

注意事项

  • 周起始可根据业务需求调整:如果需要周日为周起始,只需将freq='W-MON'改为freq='W-SUN',并调整周起始日期的计算逻辑。
  • 该方案无需手动遍历复杂控制流,利用Pandas的时间序列工具实现,代码可维护性和效率更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 15:10:16