You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用前序非NaN值滚动均值填充NaN的实现与优化问询

需求解析

要实现的逻辑:用前序两个非NaN值的滚动均值填充NaN,且新生成的均值会作为有效值参与下一个NaN的计算。比如示例中:

  • 第5行NaN用(6.0 + 5.0)/2 = 5.5填充
  • 第6行NaN用(5.5 + 6.0)/2 = 5.75填充
  • 第7行NaN用(5.75 + 5.5)/2 = 5.625填充

现有reduce方案拆解

核心参数说明

  • 第三个参数df是reduce的初始操作对象:reduce会从这个原始DataFrame开始,重复执行lambda函数,迭代次数等于第二个参数range(df['col1'].isna().sum())的长度(也就是NaN的总个数,示例中是3次)。
  • lambda函数逻辑:每次迭代时,先对当前DataFrame计算滚动2期均值(必须有2个非NaN值),通过shift(1)把均值移动到下一行的位置,再用这个结果填充当前DataFrame中的NaN。

整体填充流程

以示例的3个NaN为例,逐轮执行:

  1. 第一次迭代:基于原始df计算滚动均值,得到[NaN, 2, 3.5, 4.5, 5.5, NaN, NaN, NaN],shift后变为[NaN, NaN, 2, 3.5, 4.5, 5.5, NaN, NaN],用这个值填充第5行的NaN,此时df第5行变为5.5。
  2. 第二次迭代:基于更新后的df计算滚动均值,得到[NaN, 2, 3.5, 4.5, 5.5, 5.75, NaN, NaN],shift后填充第6行的NaN,变为5.75。
  3. 第三次迭代:再次计算滚动均值,得到[NaN,2,3.5,4.5,5.5,5.75,5.625,NaN],shift后填充第7行的NaN,得到最终结果。

本质是逐轮填充一个NaN,每次用前两个有效值(包括上一轮填充的均值)计算新的均值,循环次数等于NaN总数。


更高效的替代方案

Pandas + Numpy 数组循环方案(性能最优)

直接操作numpy数组,避免多次DataFrame复制,适合大数据量场景:

import pandas as pd
import numpy as np

df = pd.DataFrame({'col1': [1, 3, 4, 5, 6, np.NaN, np.NaN, np.NaN]})
arr = df['col1'].values

# 定位第一个NaN的位置
first_nan_idx = np.argmax(np.isnan(arr))
# 初始化前两个有效值
prev_prev, prev = arr[first_nan_idx-2], arr[first_nan_idx-1]

# 迭代填充所有NaN
for i in range(first_nan_idx, len(arr)):
    curr_val = (prev_prev + prev) / 2
    arr[i] = curr_val
    # 更新前两个值,为下一次计算做准备
    prev_prev, prev = prev, curr_val

df['col1'] = arr

Numpy 纯向量化递推方案(适合NaN数量极大的场景)

观察填充规律,这是一个递推序列,可直接生成填充值:

import numpy as np

arr = df['col1'].values
nan_count = np.sum(np.isnan(arr))
# 取最后两个非NaN值作为递推起点
x_prev2, x_prev1 = arr[4], arr[3]  # 对应示例中的6.0和5.0

# 生成填充序列
fill_vals = np.zeros(nan_count)
fill_vals[0] = (x_prev2 + x_prev1) / 2
for i in range(1, nan_count):
    fill_vals[i] = (fill_vals[i-1] + fill_vals[i-2]) / 2

# 填充到原数组
arr[np.isnan(arr)] = fill_vals
df['col1'] = arr

Pandas 简洁写法(用accumulate生成序列)

借助itertools.accumulate生成填充序列,代码更简洁:

from itertools import accumulate

def rolling_mean_fill(series):
    non_nan_vals = series.dropna().tolist()
    nan_count = len(series) - len(non_nan_vals)
    
    # 定义递推规则:输入前两个值,返回(后一个值, 新均值)
    def gen_next_pair(prev_pair):
        return (prev_pair[1], (prev_pair[0] + prev_pair[1]) / 2)
    
    # 生成所有填充值
    fill_seq = [pair[1] for pair in accumulate(
        [(non_nan_vals[-2], non_nan_vals[-1])]*nan_count,
        gen_next_pair
    )]
    
    return pd.Series(non_nan_vals + fill_seq, index=series.index)

df['col1'] = rolling_mean_fill(df['col1'])

内容的提问来源于stack exchange,提问作者Anoushiravan R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 00:50:49