
本文介绍一种高效、向量化的方法,利用布尔索引、Index.repeat 和 groupby.cumcount,对包含特定关键词(如 "TestString")的行进行三等分扩展:每匹配一行,生成3个新行,均分原时长为5,并按+5递增起始时间点,同时追加 _1/_2/_3 后缀。
本文介绍一种高效、向量化的方法,利用布尔索引、index.repeat 和 groupby.cumcount,对包含特定关键词(如 "teststring")的行进行三等分扩展:每匹配一行,生成3个新行,均分原时长为5,并按+5递增起始时间点,同时追加 _1/_2/_3 后缀。
在神经科学或行为实验数据分析中,常需将一个代表复合事件(如 "SocTestString")的原始试次,拆解为多个等时长、连续时间窗的子试次(例如每个5秒),以适配后续的时序建模或BOLD响应建模。Pandas 提供了无需显式循环的向量化方案,核心思路是:先复制目标行,再批量修正新行的 onset、duration 和 type 字段。
以下为完整实现代码(含详细注释):
import pandas as pd
test = pd.DataFrame({
'onset': [1, 3, 18, 33, 35, 50],
'duration': [2, 15, 15, 2, 15, 15],
'type': ['Instr', 'Remember', 'SocTestString', 'Rating', 'SelfTestString', 'XXX']
})
N = 3 # 每个匹配行扩展为 N 行(此处固定为 3)
m = test['type'].str.contains('TestString') # 布尔掩码:标记需扩展的行
# 步骤1:重复行 —— 对 m 为 True 的行重复 (N-1) 次(即原行 + 新增2行 → 共3行)
# .add(1) 确保原行至少保留1次;.mul(N-1) 计算需额外复制次数
out = test.loc[test.index.repeat(m.mul(N - 1).add(1))]
# 步骤2:更新 duration —— 仅对原匹配行对应的所有新行设为 5(即原 duration / N)
out.loc[m, 'duration'] = 5 # 更直观:直接赋值,而非除法(避免浮点误差)
# 步骤3:计算累计序号(0,1,2)并更新 onset
cc = out.loc[m].groupby(level=0).cumcount() # level=0 按原始索引分组,确保同一原行内编号连续
out.loc[m, 'onset'] += cc * 5 # onset_i = original_onset + i * 5
# 步骤4:更新 type —— 追加 _1, _2, _3 后缀
out.loc[m, 'type'] += '_' + (cc + 1).astype(str)
# 步骤5:重置索引(可选,但推荐用于清晰性)
out = out.reset_index(drop=True)
print(out)
✅ 输出结果与预期完全一致:
onset duration type 0 1 2 Instr 1 3 15 Remember 2 18 5 SocTestString_1 3 23 5 SocTestString_2 4 28 5 SocTestString_3 5 33 2 Rating 6 35 5 SelfTestString_1 7 40 5 SelfTestString_2 8 45 5 SelfTestString_3 9 50 15 XXX
⚠️ 注意事项:
- 该方法要求原始 DataFrame 的索引无重复值(
test.index.is_unique == True),否则groupby(level=0)分组可能出错; - 若需支持任意
N(不局限于3),只需修改N变量并确保duration被均分为original_duration / N(注意整除或四舍五入逻辑); - 若
"TestString"匹配逻辑需更严格(如精确匹配、忽略大小写),可改用str.fullmatch或添加case=False参数; - 扩展后
onset严格递增,但若原始数据存在重叠或倒序,建议扩展前先按onset排序。
此方案兼顾性能与可读性,适用于数千行规模的数据处理,在 fMRI 实验设计解析、EEG 事件序列重构等场景中具有广泛实用性。










