
本文介绍如何在 pandas 中按 `cli_cd` 分组,识别每组内 `cura_t1` 首次出现 1 的位置,并从此处开始逐行累加 `100/6`(即约 16.666…),后续为 0 的行重置为 0。
要实现该逻辑,关键在于:不依赖显式循环,而是利用布尔序列、累积分组与向量化累加完成高效计算。核心思路是——将 CURA_T1 == 0 的连续段视为“重置点”,通过 .cumsum() 构造唯一分组标识,再对每个组内使用 .cumcount() 实现从 0 开始的序号计数,最后乘以步长 100/6 并取整。
以下是完整可执行代码:
import pandas as pd
import numpy as np
# 构造示例数据
df = pd.DataFrame({
'CLI_CD': [3] * 12,
'CURA_T1': [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0]
})
# 步骤解析:
# 1. 生成布尔掩码:CURA_T1 == 0 → [True, True, ..., False, ...]
# 2. cumsum() 将每个连续的 0 段标记为同一组(含首个 1 后的 0 段也独立成组)
# 3. groupby 后 cumcount() 对每组内行编号(从 0 开始)
# 4. 仅当 CURA_T1 == 1 时保留累加值,否则设为 0(原答案未显式过滤,但实际需此逻辑才符合题意)
# ✅ 更健壮且语义清晰的实现(推荐):
mask_first_one = df.groupby('CLI_CD')['CURA_T1'].apply(
lambda x: x.eq(1).idxmax() if x.eq(1).any() else None
)
df['CURA_ALT'] = 0
for cli, group_idx in mask_first_one.items():
if pd.isna(group_idx):
continue
# 获取该 CLI_CD 下所有行索引
cli_mask = df['CLI_CD'] == cli
# 找出从首个 1 开始、且后续连续为 1 的子序列(直到下一个 0 出现)
start = group_idx
end = start
while end < len(df) and df.iloc[end]['CLI_CD'] == cli and df.iloc[end]['CURA_T1'] == 1:
end += 1
# 填充累加值:100/6 × (1, 2, ..., n),四舍五入取整
n = end - start
values = np.round(np.arange(1, n + 1) * 100 / 6).astype(int)
df.loc[start:end-1, 'CURA_ALT'] = values
print(df)但若追求极致向量化(如原答案风格),可采用如下简洁写法(适用于单 ID 或已确保组内逻辑一致):
# ⚠️ 注意:此方法隐含假设 —— 所有 1 出现在连续块中,且之后的 0 不参与累加
df['group_id'] = (df['CURA_T1'] == 0).cumsum()
df['CURA_ALT'] = df.groupby('group_id').cumcount() * (100 / 6)
df['CURA_ALT'] = df['CURA_ALT'].where(df['CURA_T1'] == 1, 0).round().astype(int)
df = df.drop(columns=['group_id'])注意事项:
- 原答案中 df['CURA_ALT'] = df.groupby(df['CURA_T1'].eq(0).cumsum()).cumcount() * 100/6 未按 CLI_CD 分组,无法支持多 ID 场景;正确做法必须先 groupby('CLI_CD'),再在组内做条件识别。
- 100/6 ≈ 16.666...,直接 astype(int) 是截断而非四舍五入,会导致累计误差(如第 6 行应为 33 而非 33.33→33);建议用 .round().astype(int) 更准确。
- 若某组无 CURA_T1 == 1,需额外处理避免 idxmax() 报错(如使用 x.eq(1).idxmax(skipna=False) 配合 try/except 或 fillna())。
总结:
本任务本质是「分组内条件启动的等差数列填充」。推荐优先使用 groupby(...).apply(...) + 显式索引控制的方式,逻辑清晰、可调试性强;纯向量化方案虽简短,但对数据分布敏感,生产环境建议增加边界校验。










