1. 利用fillna进行填充
在 fillna
中有三个参数是常用的: value, method, limit
。其中, value
为填充值,可以是标量,也可以是索引到元素的字典映射; method
为填充方法,有用前面的元素填充 ffill
和用后面的元素填充 bfill
两种类型, limit
参数表示连续缺失值的最大填充次数。
下面构造一个简单的 Series
来说明用法:
In [19]: s = pd.Series([np.nan, 1, np.nan, np.nan, 2, np.nan],
....: list('aaabcd'))
....:
In [20]: s
Out[20]:
a NaN
a 1.0
a NaN
b NaN
c 2.0
d NaN
dtype: float64
In [21]: s.fillna(method='ffill') # 用前面的值向后填充
Out[21]:
a NaN
a 1.0
a 1.0
b 1.0
c 2.0
d 2.0
dtype: float64
In [22]: s.fillna(method='ffill', limit=1) # 连续出现的缺失,最多填充一次
Out[22]:
a NaN
a 1.0
a 1.0
b NaN
c 2.0
d 2.0
dtype: float64
In [23]: s.fillna(s.mean()) # value为标量
Out[23]:
a 1.5
a 1.0
a 1.5
b 1.5
c 2.0
d 1.5
dtype: float64
In [24]: s.fillna({'a': 100, 'd': 200}) # 通过索引映射填充的值
Out[24]:
a 100.0
a 1.0
a 100.0
b NaN
c 2.0
d 200.0
dtype: float64
有时为了更加合理地填充,需要先进行分组后再操作。例如,根据年级进行身高的均值填充:
In [25]: df.groupby('Grade')['Height'].transform(
....: lambda x: x.fillna(x.mean())).head()
....:
Out[25]:
0 158.900000
1 166.500000
2 188.900000
3 163.075862
4 174.000000
Name: Height, dtype: float64
练一练
对一个序列以如下规则填充缺失值:如果单独出现的缺失值,就用前后均值填充,如果连续出现的缺失值就不填充,即序列[1, NaN, 3, NaN, NaN]填充后为[1, 2, 3, NaN, NaN],请利用
fillna
函数实现。(提示:利用limit
参数)
2. 插值函数
在关于 interpolate
函数的 文档 描述中,列举了许多插值法,包括了大量 Scipy
中的方法。由于很多插值方法涉及到比较复杂的数学知识,因此这里只讨论比较常用且简单的三类情况,即线性插值、最近邻插值和索引插值。
对于 interpolate
而言,除了插值方法(默认为 linear
线性插值)之外,有与 fillna
类似的两个常用参数,一个是控制方向的 limit_direction
,另一个是控制最大连续缺失值插值个数的 limit
。其中,限制插值的方向默认为 forward
,这与 fillna
的 method
中的 ffill
是类似的,若想要后向限制插值或者双向限制插值可以指定为 backward
或 both
。
In [26]: s = pd.Series([np.nan, np.nan, 1,
....: np.nan, np.nan, np.nan,
....: 2, np.nan, np.nan])
....:
In [27]: s.values
Out[27]: array([nan, nan, 1., nan, nan, nan, 2., nan, nan])
例如,在默认线性插值法下分别进行 backward
和双向限制插值,同时限制最大连续条数为1:
In [28]: res = s.interpolate(limit_direction='backward', limit=1)
In [29]: res.values
Out[29]: array([ nan, 1. , 1. , nan, nan, 1.75, 2. , nan, nan])
In [30]: res = s.interpolate(limit_direction='both', limit=1)
In [31]: res.values
Out[31]: array([ nan, 1. , 1. , 1.25, nan, 1.75, 2. , 2. , nan])
第二种常见的插值是最近邻插补,即缺失值的元素和离它最近的非缺失值元素一样:
In [32]: s.interpolate('nearest').values
Out[32]: array([nan, nan, 1., 1., 1., 2., 2., nan, nan])
最后来介绍索引插值,即根据索引大小进行线性插值。例如,构造不等间距的索引进行演示:
In [33]: s = pd.Series([0,np.nan,10],index=[0,1,10])
In [34]: s
Out[34]:
0 0.0
1 NaN
10 10.0
dtype: float64
In [35]: s.interpolate() # 默认的线性插值,等价于计算中点的值
Out[35]:
0 0.0
1 5.0
10 10.0
dtype: float64
In [36]: s.interpolate(method='index') # 和索引有关的线性插值,计算相应索引大小对应的值
Out[36]:
0 0.0
1 1.0
10 10.0
dtype: float64
同时,这种方法对于时间戳索引也是可以使用的,有关时间序列的其他话题会在第十章进行讨论,这里举一个简单的例子:
In [37]: s = pd.Series([0,np.nan,10],
....: index=pd.to_datetime(['20200101',
....: '20200102',
....: '20200111']))
....:
In [38]: s
Out[38]:
2020-01-01 0.0
2020-01-02 NaN
2020-01-11 10.0
dtype: float64
In [39]: s.interpolate()
Out[39]:
2020-01-01 0.0
2020-01-02 5.0
2020-01-11 10.0
dtype: float64
In [40]: s.interpolate(method='index')
Out[40]:
2020-01-01 0.0
2020-01-02 1.0
2020-01-11 10.0
dtype: float64
关于polynomial和spline插值的注意事项
在
interpolate
中如果选用polynomial
的插值方法,它内部调用的是scipy.interpolate.interp1d(*,*,kind=order)
,这个函数内部调用的是make_interp_spline
方法,因此其实是样条插值而不是类似于numpy
中的polyfit
多项式拟合插值;而当选用spline
方法时,pandas
调用的是scipy.interpolate.UnivariateSpline
而不是普通的样条插值。这一部分的文档描述比较混乱,而且这种参数的设计也是不合理的,当使用这两类插值方法时,用户一定要小心谨慎地根据自己的实际需求选取恰当的插值方法。