用滚动均值或其他插值替换NaN或缺失值
我有一个包含每月数据的 pandas 数据框,我想计算一个12个月的移动平均值。不过,每年一月份的数据都是缺失的(显示为 NaN),所以我使用了
pd.rolling_mean(data["variable"]), 12, center=True)
但是结果却全是 NaN 值。
有没有简单的方法可以忽略这些 NaN 值呢?我明白这样做实际上会变成11个月的移动平均。
这个数据框里还有其他变量在一月份有数据,所以我不想直接把一月份的列删掉,然后只计算11个月的移动平均。
2 个回答
真正的关键在于设置 min_periods=1
。另外,从第18版开始,正确的调用方式是使用一个 滚动对象。所以,你的代码应该是
data["variable"].rolling(min_periods=1, center=True, window=12).mean()
。
处理这个问题的方法有很多,最合适的方式取决于一月份的数据是否和其他月份有系统性的不同。大多数现实世界的数据可能会有季节性变化,所以我们用北半球某个随机城市的平均最高气温(华氏度)作为例子。
df=pd.DataFrame({ 'month' : [10,11,12,1,2,3],
'temp' : [65,50,45,np.nan,40,43] }).set_index('month')
你可以像你提到的那样使用滚动平均,但问题是这样会得到一整年的平均气温,这忽略了一月是最冷的这个事实。为了纠正这个问题,你可以把窗口缩小到3,这样一月的温度就会是十二月和二月的平均值。(我还使用了min_periods=1
,这是@user394430的建议。)
df['rollmean12'] = df['temp'].rolling(12,center=True,min_periods=1).mean()
df['rollmean3'] = df['temp'].rolling( 3,center=True,min_periods=1).mean()
这些方法有所改进,但仍然存在用滚动平均覆盖已有值的问题。为了避免这个问题,你可以结合使用update()
方法(查看文档)。
df['update'] = df['rollmean3']
df['update'].update( df['temp'] ) # note: this is an inplace operation
还有一些更简单的方法,可以在不改变已有值的情况下,用上一个月、下一个月,或者上下两个月的平均值来填补缺失的一月温度。
df['ffill'] = df['temp'].ffill() # previous month
df['bfill'] = df['temp'].bfill() # next month
df['interp'] = df['temp'].interpolate() # mean of prev/next
在这种情况下,interpolate()
默认使用简单的线性插值,但你还有其他几种插值选项可以选择。想了解更多信息,可以查看pandas插值的文档,或者这个Stack Overflow的问题:在pandas中对DataFrame进行插值
这里是包含所有结果的示例数据:
temp rollmean12 rollmean3 update ffill bfill interp
month
10 65.0 48.6 57.500000 65.0 65.0 65.0 65.0
11 50.0 48.6 53.333333 50.0 50.0 50.0 50.0
12 45.0 48.6 47.500000 45.0 45.0 45.0 45.0
1 NaN 48.6 42.500000 42.5 45.0 40.0 42.5
2 40.0 48.6 41.500000 40.0 40.0 40.0 40.0
3 43.0 48.6 41.500000 43.0 43.0 43.0 43.0
特别注意,“update”和“interp”在所有月份的结果是一样的。虽然在这里使用哪个都没关系,但在其他情况下,可能会有一种方法更好。