解决Pandas中使用ffill填充多事件值时的代码覆盖问题
问题描述
我有一个可简化为如下形式的DataFrame:
import numpy as np import pandas as pd TR = [17,18,19,20,21,22,23,24,25,26,27,28,29] Event = ['SRNeg', np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, 'Rating', np.NaN, np.NaN, 'ArrowTask', np.NaN] df = pd.DataFrame({'Event':Event,'TR':TR})
需要将事件值后的部分NaN填充为该事件值(并非所有NaN都需填充),理想输出如下:
TR = [17,18,19,20,21,22,23,24,25,26,27,28,29] Event = ['SRNeg', np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, 'Rating', np.NaN, np.NaN, 'ArrowTask', np.NaN] Event_BOLD_Duration = ['SRNeg', 'SRNeg', 'SRNeg', 'SRNeg', 'SRNeg', 'SRNeg', 'SRNeg', 'SRNeg', 'Rating', 'Rating', np.NaN, 'ArrowTask', 'ArrowTask'] df = pd.DataFrame({'Event':Event,'Event_BOLD_Duration':Event_BOLD_Duration,'TR':TR})
我原本编写了如下代码,但每行代码都会覆盖前一行的输出结果:
# line 1 scores_full['Event_BOLD_Duration'] = scores_full['Event'].where(scores_full['Event'].isin(['SRNeg', 'SINeg'])).ffill(limit=7).fillna(scores_full['Event']) # line 2 scores_full['Event_BOLD_Duration'] = scores_full['Event'].where(scores_full['Event'].isin(['ArrowTask'])).ffill(limit=4).fillna(scores_full['Event']) # line 3 scores_full['Event_BOLD_Duration'] = scores_full['Event'].where(scores_full['Event'].isin(['Rating'])).ffill(limit=1).fillna(scores_full['Event'])
解决方案
你的问题出在每次赋值都直接从原始Event列重新生成数据,完全覆盖了之前的处理结果。正确的做法是先初始化目标列,再针对不同事件类型逐个填充后续指定数量的NaN。
实现代码
import numpy as np import pandas as pd # 初始化DataFrame TR = [17,18,19,20,21,22,23,24,25,26,27,28,29] Event = ['SRNeg', np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, 'Rating', np.NaN, np.NaN, 'ArrowTask', np.NaN] df = pd.DataFrame({'Event':Event,'TR':TR}) # 初始化目标列,先复制原始Event值 df['Event_BOLD_Duration'] = df['Event'].copy() # 1. 处理SRNeg/SINeg:每个事件后填充7行 for event in ['SRNeg', 'SINeg']: # 获取所有该事件的索引 event_idx_list = df[df['Event'] == event].index for idx in event_idx_list: # 计算填充的结束位置,避免超出DataFrame范围 fill_end = min(idx + 7, df.index[-1]) # 填充从idx+1到fill_end的行 df.loc[idx+1:fill_end, 'Event_BOLD_Duration'] = event # 2. 处理Rating:每个事件后填充1行 event_idx_list = df[df['Event'] == 'Rating'].index for idx in event_idx_list: fill_end = min(idx + 1, df.index[-1]) if fill_end > idx: # 确保存在可填充的行 df.loc[idx+1:fill_end, 'Event_BOLD_Duration'] = 'Rating' # 3. 处理ArrowTask:每个事件后填充1行(匹配你的理想输出) event_idx_list = df[df['Event'] == 'ArrowTask'].index for idx in event_idx_list: fill_end = min(idx + 1, df.index[-1]) if fill_end > idx: df.loc[idx+1:fill_end, 'Event_BOLD_Duration'] = 'ArrowTask'
代码说明
- 先复制原始
Event列到Event_BOLD_Duration,保留所有原始非NaN值 - 针对每个事件类型,找到其出现的索引,然后向后填充指定数量的行
- 使用
min(idx + N, df.index[-1])避免超出DataFrame的最后一行索引,防止报错
执行后即可得到你想要的理想输出。
内容的提问来源于stack exchange,提问作者jo_
相关产品推荐
相关产品推荐

