基于pandas DataFrame中ffill()的条件填充问题求助
解决Pandas DataFrame按规则填充TEST列的问题
我明白你之前试过一个方案但没达到预期效果,现在咱们来搞定这个按规则填充TEST列的需求。先把规则再明确一遍:
- 当
IN列和200D_MA列同时为1时,TEST列设为1; - 一旦
200D_MA列变为0,TEST列要跟着设为0,并且必须等IN列再次出现1(且此时200D_MA也为1),TEST才能重新变回1; - 在
TEST已经是1的状态下,只要200D_MA保持为1,哪怕IN是0,TEST也继续维持1。
先来看看你提供的测试数据(标注了预期结果和问题点):
| Date | IN | 200D | TEST(预期) | 说明 |
|---|---|---|---|---|
| 12/6/2013 | 0 | 1 | 0 | |
| 12/9/2013 | 0 | 1 | 0 | |
| 12/10/2013 | 1 | 1 | 1 | IN和200D均为1 >> TEST=1 |
| 12/11/2013 | 0 | 1 | 1 | 只要200D=1则延续1 |
| 12/16/2013 | 0 | 1 | 1 | 只要200D=1则延续1 |
| 12/17/2013 | 0 | 1 | 1 | |
| 12/18/2013 | 0 | 0 | 0 | TEST=0 因为200D=0 |
| 12/19/2013 | 0 | 0 | 0 | |
| 12/20/2013 | 0 | 0 | 0 | |
| 12/23/2013 | 0 | 1 | 0 | 错误>> TEST应为0 因为IN不为1 |
| 12/24/2013 | 0 | 1 | 0 | 错误>> TEST应为0 因为IN不为1 |
| 12/25/2013 | 0 | 1 | 0 | 错误>> TEST应为0 因为IN不为1 |
| 12/26/2013 | 1 | 0 | 0 | |
| 12/27/2013 | 1 | 0 | 0 | |
| 12/28/2013 | 0 | 1 | 0 | |
| 12/29/2013 | 1 | 1 | 1 | IN和200D均为1 >> TEST=1 |
| 12/30/2013 | 0 | 1 | 1 | |
| 12/31/2013 | 0 | 1 | 1 | |
| 1/1/2014 | 0 | 0 | 0 | |
| 1/2/2014 | 1 | 0 | 0 | TEST本应为1但200D=0 >> TEST=0 |
| 1/3/2014 | 0 | 0 | 0 | |
| 1/6/2014 | 0 | 0 | 0 | |
| 1/7/2014 | 1 | 1 | 1 | IN和200D均为1 >> TEST=1 |
| 1/8/2014 | 0 | 1 | 1 |
你之前的代码问题在于,用ffill()会在200D_MA变回1时直接延续之前的1,但没有考虑必须先有IN=1的触发条件。咱们来写一段符合规则的代码:
import pandas as pd # 构造测试数据 data = { 'Date': ['12/6/2013', '12/9/2013', '12/10/2013', '12/11/2013', '12/16/2013', '12/17/2013', '12/18/2013', '12/19/2013', '12/20/2013', '12/23/2013', '12/24/2013', '12/25/2013', '12/26/2013', '12/27/2013', '12/28/2013', '12/29/2013', '12/30/2013', '12/31/2013', '1/1/2014', '1/2/2014', '1/3/2014', '1/6/2014', '1/7/2014', '1/8/2014'], 'IN': [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0], '200D_MA': [1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1] } df = pd.DataFrame(data) # 1. 标记触发点:IN和200D_MA同时为1的位置 df['trigger'] = (df['IN'] == 1) & (df['200D_MA'] == 1) # 2. 标记重置点:200D_MA从1变为0的位置 df['reset'] = (df['200D_MA'].shift(1) == 1) & (df['200D_MA'] == 0) # 3. 生成组ID:每次重置后组ID+1,划分独立周期 df['group_id'] = df['reset'].cumsum() # 4. 组内填充:触发点后延续1,未触发则为0 df['TEST'] = df.groupby('group_id')['trigger'].ffill().fillna(0).astype(int) # 删除辅助列 df.drop(['trigger', 'reset', 'group_id'], axis=1, inplace=True) print(df)
代码逻辑解释:
- 标记触发点:先找出所有
IN和200D_MA同时为1的行,这些是TEST变为1的起始开关; - 标记重置点:找出
200D_MA从1跳转为0的行,这些点之后TEST必须归零,且需要新的触发点才能重新激活; - 生成组ID:用重置点的累积和划分不同的周期组,每个组内的填充逻辑独立;
- 组内填充:在每个组内,把触发点的1向前填充(触发后的所有行延续1,直到下一个重置点),最后把空值转为0并转成整数类型。
运行这段代码后,TEST列的结果就完全符合你的预期了,比如12/23/2013那几行TEST会保持0,直到12/29/2013的触发点出现才变回1。
内容的提问来源于stack exchange,提问作者J Westwood
相关产品推荐
相关产品推荐

