使用Pandas实现时间序列前置NaN的观测值分配填充
解决时间序列累计值分配到前置NaN的问题
我完全懂你的需求——要把每个非NaN的累计总量,平均分配到它之前连续的NaN区间(包括自身所在时间步),这种特殊逻辑确实没法用bfill或者普通interpolate直接搞定,得自定义逻辑来实现。下面是针对Pandas的具体解决方案:
核心思路拆解
- 给每个需要分配的区间打「分组标签」:把每个非NaN值和它前面的连续NaN划进同一个组
- 统计每个组的关键信息:组内的时间步数、对应的累计总量
- 计算单步分配值:用累计总量除以组内步数,给组内每个时间步赋值
- 保留末尾无对应累计值的NaN:如果最后几个时间步都是NaN,保持原状态不变
代码实现(附示例数据)
假设你的DataFrame命名为df,包含Timestep和Value两列,代码如下:
import pandas as pd import numpy as np # 加载你的示例数据 data = { 'Timestep': [1,2,3,4,5,6,7,8,9,10], 'Value': [10, np.nan, np.nan, 9, np.nan, np.nan, np.nan, 16, np.nan, np.nan] } df = pd.DataFrame(data) # 1. 创建分组标签:遇到非NaN值就新建一个组,连续NaN自动归到下一个非NaN的组 df['group'] = df['Value'].notna().cumsum() # 2. 统计每个组的累计值和组内步数 group_info = df.groupby('group').agg( total=('Value', 'last'), # 取组内唯一的非NaN值(累计总量) step_count=('Value', 'count') # 组内包含的时间步数 ) # 3. 合并统计信息,计算每个时间步的分配值 df = df.merge(group_info, on='group', how='left') df['Value'] = df['total'] / df['step_count'] # 4. 处理末尾无累计值的NaN组 df.loc[df['total'].isna(), 'Value'] = np.nan # 清理临时辅助列 df = df.drop(['group', 'total', 'step_count'], axis=1) print(df)
输出结果
运行后会得到你期望的格式:
Timestep Value 0 1 10.0 1 2 3.0 2 3 3.0 3 4 3.0 4 5 4.0 5 6 4.0 6 7 4.0 7 8 4.0 8 9 NaN 9 10 NaN
逻辑细节解释
df['Value'].notna().cumsum():每碰到一个非NaN值,分组ID就+1,这样Timestep2-4会被归到同一个组(对应累计值9),Timestep5-8归到同一个组(对应累计值16)- 分组统计时,
total取组内最后一个值(也就是那个唯一的非NaN累计值),step_count是组内的总步数 - 用累计值除以步数,就得到每个时间步的平均分配值
- 最后一组(Timestep9-10)因为没有对应的非NaN累计值,
total为NaN,所以保持Value为NaN,完全符合你的要求
内容的提问来源于stack exchange,提问作者the_martian
相关产品推荐
相关产品推荐

