Pandas如何按规则填充日期区间内的空值?
问题描述
现有如下DataFrame:
| date | id | value1 | value2 |
|---|---|---|---|
| 2022-01-01 | 11 | 1 | 12 |
| 2022-01-02 | 11 | 2 | 13 |
| 2022-01-03 | 11 | NaN | NaN |
| 2022-01-04 | 11 | NaN | NaN |
| 2022-01-05 | 11 | 5 | 15 |
| 2022-01-01 | 22 | 11 | 3 |
| 2022-01-02 | 22 | NaN | NaN |
| 2022-01-03 | 22 | NaN | NaN |
| 2022-01-04 | 22 | NaN | NaN |
| 2022-01-05 | 22 | 12 | 34 |
| 2022-01-01 | 33 | 2 | 4 |
| 2022-01-02 | 33 | 4 | 8 |
| 2022-01-03 | 33 | NaN | NaN |
| 2022-01-04 | 33 | NaN | NaN |
| 2022-01-05 | 33 | NaN | NaN |
| 2022-01-01 | 44 | NaN | NaN |
| 2022-01-02 | 44 | 34 | 89 |
| 2022-01-03 | 44 | NaN | NaN |
| 2022-01-04 | 44 | 35 | NaN |
| 2022-01-05 | 44 | NaN | NaN |
需要满足以下处理要求:
- 按
id列分组 - 仅当整行为空时填充空值(NaN)
- 仅当分组后存在后续有值的日期时才填充空值:比如id=11的2022-01-03、04因为后续05有有效值,所以填充;id=33的后续无有效值,所以不填充
尝试用ffill()无法满足第3点要求,求解决方案。
解决方案
可以通过以下步骤实现需求:
- 标记整行空值的行:判断每行的value1、value2是否全为NaN,生成辅助列标记这类行
- 标记分组内后续是否有有效值:对每个分组从后往前判断,标记当前行之后是否存在非整行空值的记录
- 条件式前向填充:先做分组前向填充,再仅对符合条件的行替换填充值
具体代码实现:
import pandas as pd import numpy as np # 构建原始DataFrame df = pd.DataFrame({ 'date': ['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05', '2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05', '2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05', '2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05'], 'id': [11,11,11,11,11,22,22,22,22,22,33,33,33,33,33,44,44,44,44,44], 'value1': [1,2,np.nan,np.nan,5,11,np.nan,np.nan,np.nan,12,2,4,np.nan,np.nan,np.nan,np.nan,34,np.nan,35,np.nan], 'value2': [12,13,np.nan,np.nan,15,3,np.nan,np.nan,np.nan,34,4,8,np.nan,np.nan,np.nan,np.nan,89,np.nan,np.nan,np.nan] }) # 步骤1:标记整行空值的行 df['is_full_nan'] = df[['value1', 'value2']].isna().all(axis=1) # 步骤2:标记分组内当前行之后是否有非空行 def mark_future_valid(group): # 从后往前累积判断,只要后续有非全空行则标记为True group['has_future'] = (~group['is_full_nan'])[::-1].cumsum().astype(bool)[::-1] return group df = df.groupby('id').apply(mark_future_valid).reset_index(drop=True) # 步骤3:生成填充后的数据,仅替换符合条件的行 filled_values = df.groupby('id')[['value1', 'value2']].ffill() df[['value1', 'value2']] = np.where( df[['is_full_nan', 'has_future']].all(axis=1), filled_values, df[['value1', 'value2']] ) # 移除辅助列 df = df.drop(['is_full_nan', 'has_future'], axis=1) print(df)
代码说明:
is_full_nan列:精准筛选出需要处理的整行空值行,避免误填充部分空值的行has_future列:通过从后往前的累积判断,确保只填充那些后续还有有效值的空行,解决ffill()会填充到分组末尾的问题- 条件替换:仅将“整行空+后续有有效值”的行替换为前向填充结果,其余行保留原始数据,完全匹配需求
执行后得到的结果与期望输出一致。
内容的提问来源于stack exchange,提问作者twagh24
相关产品推荐
相关产品推荐

