处理与插补时间序列数据

除了我们在上一篇《Python中的缺失数据处理方法》遇到的float,int或者分类数据之外,还有一种常见的数据类型——时间序列数据。那么当时间序列对应的数据存在缺失的时候,我们需要使用什么方法进行处理和插补呢?

这篇例文所使用的数据:点击下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pandas as pd
df = pd.read_csv('datasets/air-quality.csv', parse_dates=['Date'], index_col='Date')
df.head()
Out [1]:
Ozone Solar Wind Temp
Date
1976-05-01 41.0 190.0 7.4 67
1976-05-02 36.0 118.0 8.0 72
1976-05-03 12.0 149.0 12.6 74
1976-05-04 18.0 313.0 11.5 62
1976-05-05 NaN NaN 14.3 56
#查看一下各变量缺失值的数量
df.isnull().sum()
Out [2]:
Ozone 37
Solar 7
Wind 0
Temp 0
dtype: int64
#查看一下各变量缺失值所占比例
df.isnull().mean() * 100
Out [3]:
Ozone 24.183007
Solar 4.575163
Wind 0.000000
Temp 0.000000
dtype: float64

对于处理时间序列缺失数据,我们有两大类处理方式:第一个是.fillna(),第二个是.interpolate() 。我们在这里将会首先使用.fillna(),然后再使用.interpolate()来处理df中的缺失数据,并且我们会用可视化的方式,来评估和选取表现最好的方法。

.fillna()

.fillna()有两种插补数据的方式,分别是向前插补和向后插补。使用何种插补方式需要修改.fillna()中的method参数:

  • ‘ffill’ or ‘pad’
  • ‘bfill’ or ‘backwardfill’

其中,向前插补ffill顾名思义就是使用缺失数据前面一个不是缺失数据的值来进行插补,向后插补bfill与ffill相反,使用缺失数据后一个不是缺失数据的值来进行插补。

我们看一下ffill和bfill的插补效果及差别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
df['Ozone'][30:40]
df.fillna(method='ffill', inplace=True)
df.fillna(method='bfill', inplace=True)
Out:
#原数据 #ffill #bfill
Date Date Date
1976-05-31 37.0 1976-05-31 37.01976-05-31 37.0
1976-06-01 NaN 1976-06-01 37.0* 1976-06-01 29.0*
1976-06-02 NaN 1976-06-02 37.0* 1976-06-02 29.0*
1976-06-03 NaN 1976-06-03 37.0* 1976-06-03 29.0*
1976-06-04 NaN 1976-06-04 37.0* 1976-06-04 29.0*
1976-06-05 NaN 1976-06-05 37.0* 1976-06-05 29.0*
1976-06-06 NaN 1976-06-06 37.0* 1976-06-06 29.0*
1976-06-07 29.0 1976-06-07 29.01976-06-07 29.0
1976-06-08 NaN 1976-06-08 29.0* 1976-06-08 71.0*
1976-06-09 71.0 1976-06-09 71.0 1976-06-09 71.0
Name: Ozone, dtype: float64

.interpolate()

.interpolate()有三种插补方式,分别是:线性‘linear’,二元’quadratic’,和最近’nearest’。下面将展示这三种方式的插补效果及差别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
df.interpolate(method='linear', inplace=True)
df.interpolate(method='quadratic', inplace=True)
df.interpolate(method='nearest', inplace=True)
Out:
#linear #quadratic #nearest
Date Date Date
1976-05-31 37.000000 1976-05-31 37.000000 1976-05-31 37.0
1976-06-01 35.857143* 1976-06-01 -38.361123* 1976-06-01 37.0*
1976-06-02 34.714286* 1976-06-02 -79.352735* 1976-06-02 37.0*
1976-06-03 33.571429* 1976-06-03 -85.974836* 1976-06-03 37.0*
1976-06-04 32.428571* 1976-06-04 -62.354606* 1976-06-04 29.0*
1976-06-05 31.285714* 1976-06-05 -33.255133* 1976-06-05 29.0*
1976-06-06 30.142857* 1976-06-06 -2.803598* 1976-06-06 29.0*
1976-06-07 29.000000 1976-06-07 29.000000 1976-06-07 29.0
1976-06-08 50.000000* 1976-06-08 62.155660* 1976-06-08 29.0*
1976-06-09 71.000000 1976-06-09 71.000000 1976-06-09 71.0
Name: Ozone, dtype: float64

可视化时间序列数据

我们可以通过对原数据及插补后的数据进行可视化来评估哪种插补方式更为合理:



首先是ffill和bfill:

1
2
3
ffill_imp = df.fillna(method='ffill')
ffill_imp['Ozone'].plot(color='red', marker='o', linestyle='dotted', figsize=(30, 5))
df['Ozone'].plot(title='Ozone', marker='o')


1
2
3
bfill_imputed = df.fillna(method='bfill')
bfill_imputed['Ozone'].plot(color='red', marker='o', linestyle='dotted', figsize=(30, 5))
df['Ozone'].plot(title='Ozone', marker='o', figsize=(30, 5))


然后我们来看下线性‘linear’,二元’quadratic’,和最近’nearest’:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import matplotlib.pyplot as plt
fig, axes = plt.subplots(3, 1, figsize=(30, 20))
linear = df.interpolate(method='linear')
quadratic = df.interpolate(method='quadratic')
nearest = df.interpolate(method='nearest')
interpolations = {'Linear Interpolation': linear, 'Quadratic Interpolation': quadratic,
'Nearest Interpolation': nearest}
for ax, df_key in zip(axes, interpolations):
interpolations[df_key].Ozone.plot(color='red', marker='o',
linestyle='dotted', ax=ax)
df.Ozone.plot(title=df_key + ' - Ozone', marker='o', ax=ax)
plt.show()


经过我们对比5种处理和插补方式,发现线性linear插补表现最佳。二元quadratic的插补数据一些超出了范围。fill方式和nearest方式处理效果也欠佳。

0%