如何在 Pandas 中按组动态计算累计百分比并自动对齐至 100%

11次阅读

如何在 Pandas 中按组动态计算累计百分比并自动对齐至 100%

本文介绍一种高效方法:基于每组连续 `1` 的实际长度,动态分配等距递增的百分比值(如 4 个 `1` 则分配 `[50, 66, 83, 100]`),确保末位恒为 100%,并自动将 `target=0` 行置零。

在使用 pandas 处理二值序列(如 TARGET 列)时,常需为连续的 1 段生成线性增长的百分比进度列(PERCENTAGE),且要求该序列严格以 100% 结尾——无论该段长度是否恰好为预设最大值(如 6)。常见误区是硬编码步长(如 100/6 ≈ 16.67),导致短序列无法抵达 100%;正确解法应根据每段实际长度动态缩放。

✅ 核心思路:按组截取、动态对齐

我们不预先固定步长,而是:

  1. 定义全局最大长度 MAX_ONES = 6,预计算完整序列 PCTS = [16, 33, 50, 66, 83, 100](即 int(100 * n / MAX_ONES),n 从 1 到 6);
  2. 对每个 ID 分组内、TARGET==1 的连续块(注意:此处需先识别连续段,而非简单按 ID 和 TARGET 分组——原答案中 groupby([“ID”, “TARGET”]) 仅适用于 TARGET 全局分段,但题目隐含“连续 1 序列”,故需更精确的连续段识别);
  3. 关键技巧:对每个连续 1 段,取 PCTS 的后 k 个元素(k 为该段长度),实现“右对齐至 100%”。

? 正确实现(支持连续段识别)

import pandas as pd import numpy as np  # 示例数据 df = pd.DataFrame({     'ID': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'],     'TARGET': [1, 1, 1, 1, 0, 0, 0, 0] })  # 步骤1:识别连续的1段(关键!) df['block_id'] = (df['TARGET'] == 0).cumsum()  # TARGET=0 时递增,形成连续1段的唯一标识 df['is_one'] = df['TARGET'] == 1 # 过滤出所有1的位置,并标记其所属连续段 ones_df = df[df['is_one']].copy() ones_df['segment'] = (ones_df['block_id'] != ones_df['block_id'].shift()).cumsum()  # 步骤2:预定义完整百分比序列(MAX_ONES=6) MAX_ONES = 6 PCTS = [int((100 / MAX_ONES) * n) for n in range(1, MAX_ONES + 1)]  # [16,33,50,66,83,100]  # 步骤3:按连续段分组,为每段分配后缀序列 def assign_percentage(group):     k = len(group)     # 取PCTS后k个值(保证末位为100)     values = PCTS[-k:] if k <= MAX_ONES else PCTS     return pd.Series(values, index=group.index)  # 应用到ones_df ones_df['PERCENTAGE'] = ones_df.groupby('segment').apply(assign_percentage).explode().astype(int)  # 步骤4:合并回原df,缺失处填0 df = df.merge(ones_df[['PERCENTAGE']], left_index=True, right_index=True, how='left') df['PERCENTAGE'] = df['PERCENTAGE'].fillna(0).astype(int)  print(df[['ID', 'TARGET', 'PERCENTAGE']])

⚠️ 注意事项

  • 连续段识别是关键:直接 groupby(['ID','TARGET']) 会错误合并非连续的 1(如 1,0,1 被视为同一组),必须用 (TARGET==0).cumsum() 或 diff().ne(0).cumsum() 提取物理连续块。
  • 整数精度处理:int() 截断可能导致小数点后误差(如 100/6*3=50.0→50 合理,但 100/7*5≈71.4→71),若需四舍五入可用 round()。
  • 超长序列处理:若某段 1 数量 > MAX_ONES(如 8 个),当前逻辑截取全部 PCTS(6 个值),剩余位置可扩展为 100 或报错,需按业务补充逻辑。

✅ 总结

该方案摆脱了固定步长的限制,通过预计算+后缀截取策略,确保任意长度的连续 1 序列都能生成以 100% 结尾的等差百分比列。结合连续段识别与向量化操作,兼具可读性与执行效率,是处理此类“动态归一化进度条”场景的推荐实践。

text=ZqhQzanResources