前言
玩弄过机器学习的各位想必都或多或少有遇到过数据集中含有缺失值的情况。如果数据集是一个多维的数据(矩阵形式),那可以考虑KNN或missforest这一类方法,但如果是一个一维向量表示的时间序列呢?这里给大家提供一个简单有效的方法——滑动平均插补(Moving Average Imputation)。你可别瞧不起它,不信你接着看…
一、滑动平均插补的基本思路
移动平均插补其实就是用一个滑动窗口在时间序列上滑动处理缺失值,比如下面这幅图
虚线框表示滑动窗口,框中心点为处理点(注意只有缺失的位置才做处理,不缺的直接跳过)。绿色的部分为滑动窗口处理过的部分,红色的部分为未处理的部分。它的思路非常简单,核心就在于滑动窗口的设计。理解了一个滑动窗口内的缺失值的插补思路,其实就等于理解了滑动平均插值。
1.1滑动窗口
- 固定长度的滑动窗口:处理点左右各取K个数,构成大小为2K+1的滑动窗口,对窗口内n个非缺失元素求和后取均值来填补缺失值,这里2≤n≤2K。当n取值过大时,可能出现窗口中非缺失元素的个数<n的情况,从而导致无法填补缺失值。这时候就要用到自适应长度的滑动窗口。
- 自适应长度的滑动窗口:与固定长度的滑动窗口思想类似,不同的是,如果窗口内的非缺失元素<n,则扩大窗口(左边不够就扩大左边,右边不够就扩大右边),直到窗口内的元素满足n个为止。
1.2窗口的滑动方式
- 单向:往一个方向滑动,一般为正向滑动。
- 双向交替:正反交替滑动。
- 双向平均:做一次正向滑动和一次反向滑动后取两个结果的平均。
通过组合不同的滑动窗口和滑动方式就能得到不同的滑动平均模型。
二、缺失值生成
在实验开始前,我们先人为的生成数据缺失以便考察模型对这些缺失的插补效果。下面是生成了两种类型的缺失:
2.1随机点缺失
往原始序列中随机添加nan来表示缺失,代码如下:
import numpy as np
def random_missing(Time_series, missing_rate):
mask = np.round(np.random.rand(len(Time_series)) + 0.5 - missing_rate) == 0
Time_series[mask] = np.nan
return Time_series
2.2随机段缺失
有的时候数据可能会出现呈现出一段一段的缺失。为了生成这种缺失,可以在随机点缺失的代码上稍微调整。首先把向量数据重塑为矩阵,然后随机挑选一些行并让这些行全部缺失,最后再将这个矩阵展开为向量,就得到了长度不固定的段缺失。
代码如下:
import numpy as np
def random_missing2(Time_series, missing_rate, miss_gap=2):
if len(Time_series) % miss_gap:
print('无法重构为指定形状')
raise Exception
else:
length = len(Time_series) // miss_gap
M = np.round(np.random.rand(length) + 0.5 - missing_rate).reshape(-1, 1) == 0
mask = np.tile(M, (1, miss_gap)).ravel()
Time_series[mask] = np.nan
return Time_series
三、缺失值插补
3.1插补模型
把前面介绍的2种滑动窗口和3种滑动方式进行排列组合得到以下6中滑动平均模型,分别是:
- 固定长度的滑动窗口+单向滑动(MA1):
def MA1(series, neighbor=1):
# 给时间序列两端都填充数值(这里填充的是未缺失部分的均值)以方便处理两端
series = np.pad(series, (neighbor, neighbor), 'constant'