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

如何在按Car分组后高效扩展Pandas二进制序列的活跃区域?

问题:按分组扩展Pandas DataFrame中的二进制活跃区域

我有一个记录车辆状态的DataFrame,需要按Car列分组后,将Open列的活跃区域(连续1组成的片段,满足:被0包围、仅前序为0或仅后序为0;全1的序列忽略)向左扩展n个单位。现有代码在不分组的场景下可以正常工作,但不知道如何高效适配分组需求。


原始DataFrame

import pandas as pd

df = pd.DataFrame(
    {
        "Car": ["Audi A5"]*10 + ["Mercedes Class A"]*10,
        "Time" : list(range(10)) + list(range(10)),
        "Open" : [0,0,0,1,1,0,0,0,1,1,1,1,1,0,0,1,1,0,0,1]
    }
)

对应的表格展示:

CarOpenTime
Audi A500
Audi A501
Audi A502
Audi A513
Audi A514
Audi A505
Audi A506
Audi A507
Audi A518
Audi A519
Mercedes Class A10
Mercedes Class A11
Mercedes Class A12
Mercedes Class A03
Mercedes Class A04
Mercedes Class A15
Mercedes Class A16
Mercedes Class A07
Mercedes Class A08
Mercedes Class A19

期望输出(n=1时)

CarOpenTime
Audi A500
Audi A501
Audi A512
Audi A513
Audi A514
Audi A505
Audi A506
Audi A517
Audi A518
Audi A519
Mercedes Class A10
Mercedes Class A11
Mercedes Class A12
Mercedes Class A03
Mercedes Class A14
Mercedes Class A15
Mercedes Class A16
Mercedes Class A07
Mercedes Class A18
Mercedes Class A19

现有代码(未分组时可用)

def enlarge(dataframe : pd.DataFrame, sensor : str, n : int = 1) -> pd.DataFrame:
    get_group_indexes = (
        lambda x: x.index[0] if x.index[-1] - x.index[0] >= 1 else None
    )
    groups = (
        dataframe[sensor]
        .eq(0)
        .cumsum()[dataframe[sensor].ne(0)]
        .to_frame()
        .groupby(sensor)
        .apply(get_group_indexes)
        .dropna()
    )
    if groups.empty:
        return dataframe
    for index in groups:
        dataframe.loc[index-n:index, sensor] = 1
    return dataframe

解决方案:适配分组场景的高效实现

我们可以利用Pandas的groupby().apply()机制,将扩展逻辑封装为分组内的处理函数,避免全局索引干扰,同时保证效率。

优化后的完整代码

import pandas as pd

def enlarge_group(group_df, sensor_col, n=1):
    # 复制分组数据,避免修改原DataFrame
    df = group_df.copy()
    open_series = df[sensor_col]
    
    # 生成连续0的分组标记,用于区分不同的1片段
    zero_groups = open_series.eq(0).cumsum()
    # 获取所有1片段对应的0组标记
    one_segments = zero_groups[open_series.ne(0)].unique()
    
    for seg in one_segments:
        # 过滤全1的情况:如果分组内所有行都是1,zero_groups的最小/最大值会一致
        if (zero_groups.min() == seg) and (zero_groups.max() == seg):
            continue
        
        # 获取当前1片段的第一个1的索引
        first_one_idx = df[zero_groups == seg].index[0]
        # 计算扩展的起始位置(不超出分组的第一行)
        expand_start = max(df.index[0], first_one_idx - n)
        # 扩展活跃区域
        df.loc[expand_start:first_one_idx, sensor_col] = 1
    
    return df

# 应用分组处理
result_df = df.groupby('Car', group_keys=False).apply(enlarge_group, sensor_col='Open', n=1)

代码说明

  1. 分组内独立处理:enlarge_group函数专门处理单个分组的DataFrame,所有逻辑基于分组内的行顺序,避免全局索引冲突。
  2. 精准识别活跃区域:通过eq(0).cumsum()生成连续0的分组标记,每个1片段会对应唯一的标记,方便区分不同的活跃区域。
  3. 过滤全1场景:通过判断zero_groups的最小和最大值是否一致,直接跳过全1的分组。
  4. 安全扩展:计算扩展起始位置时,用max()确保不会超出分组的边界,避免索引越界。
  5. 高效适配:groupby().apply()配合group_keys=False,保持输出的索引结构与原始数据一致,无需额外调整。

运行上述代码后,result_df将完全匹配你给出的期望输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 17:32:42