如何高效填充时间序列?



我的一般问题是我有一个数据框,其中列对应于特征值。数据框中还有一个日期列。每个特征列可能缺少 NaN 值。我想用一些填充逻辑(例如"fill_mean"或"填充零")填充列。

但我不想只将填充逻辑应用于整个列,因为如果较早的值之一是 NaN,我不希望我为这个特定 NaN 填充的平均值被后来的平均值污染,而模型应该不知道。从本质上讲,这是不向模型泄露有关未来的信息的常见问题 - 特别是在尝试填充我的时间序列时。

无论如何,我已经将我的问题简化为几行代码。这是我对上述一般问题的简化尝试:

#assume ts_values is a time series where the first value in the list is the oldest value and the last value in the list is the most recent.
ts_values = [17.0, np.NaN, 12.0, np.NaN, 18.0]
nan_inds = np.argwhere(np.isnan(ts_values))
for nan_ind in nan_inds:
nan_ind_value = nan_ind[0]
ts_values[nan_ind_value] = np.mean(ts_values[0:nan_ind_value])

上述脚本的输出为:

[17.0, 17.0, 12.0, 15.333333333333334, 18.0]

这正是我所期望的。

我唯一的问题是,相对于数据集中的 NaN 数量,这将是线性时间。有没有办法在常量或日志时间内执行此操作,我不遍历 nan 索引值。

如果您希望将nan值替换为熊猫系列s上的滚动平均值(全窗口),WeNYoBen 指出,这不会在填充期间继续滚动平均值计算。(所以你的 15.3 变成了 12.0)。

s.fillna(s.expanding(1).mean())

如果您希望在填充nan时更新滚动平均值,则此就地numba解决方案可能会有所帮助

import numpy as np
import numba
from numba import jit

@jit(nopython=True)
def rolling_fill(a): 
for i, e in enumerate(a):
if np.isnan(e):
a[i] = np.mean(a[:i])
ts_values = np.array([17.0, np.NaN, 12.0, np.NaN, 18.0])
rolling_fill(ts_values)
print(ts_values)

这给了

[17.         17.         12.         15.33333333 18.        ]

您可以通过保留一笔款项而不是每次都打电话给.mean来改善这一点。

时间复杂度

这不是logconstant的时间,因为您必须从长度数组中插入最多n-2缺失的项目n这是O(n)- 但它应该得到充分优化(通过避免在本机 python 中迭代),理论上你不能做得更好,但上述的较低级别的实现将使这大大加快。


编辑:我最初误读了,并认为你在问插值

你想interpolate这个系列,熊猫直接支持这个。

>>> s = pd.Series([0, 1, np.nan, 5])
>>> s
0    0.0
1    1.0
2    NaN
3    5.0
dtype: float64
>>> s.interpolate()
0    0.0
1    1.0
2    3.0
3    5.0
dtype: float64

或者,如果您不想使用pandas,因为您的示例是ndarray,请相应地使用numpy.interp

最新更新