Python数据科学手册(六)【Pandas 处理丢失的数据】
在很多情况下,有些数据并不是完整的,丢失了部分值,这一节将学习如何处理这些丢失的数据。
处理机制的权衡
常见的处理丢失数据的方法有两种:
- 使用掩码全局的指明丢失了哪些数据
- 使用哨兵值直接替换丢失的值
上述都两种方法各有弊利,使用掩码需要提供一个格外的布尔值数组,占用更多的空间;使用哨兵则在计算时需要更多的时间。
Pandas中的数据丢失
Pandas中处理数据丢失的方法受制于Numpy,尽管Numpy提供了掩码机制,但是在存储、计算和代码维护来说,并不划算,所以Pandas使用哨兵机制来处理丢失的数据。Pandas使用NaN或者None来代替丢失的值。
None代替丢失值
第一个被Pandas使用的哨兵值是None, 由于None是Python对象,所以它并不适合所有情况,只能用于数组的类型为对象的情况。
import numpy as np
import pandas as pd
vals1 = np.array([1, None, 3, 4])
对象类型也就意味着数组的元素内容为Python对象,所以计算速度会大打折扣。
for dtype in ['object', 'int']:
print("dtype =", dtype)
%timeit np.arange(1E6, dtype=dtype).sum()
print()
结果如下:
dtype = object
10 loops, best of 3: 78.2 ms per loop
dtype = int
100 loops, best of 3: 3.06 ms per loop
使用None会导致一些聚合操作,比如sum()
和min()
会报错。
NaN 代替丢失值
另外一中哨兵是使用NaN,它时一种特殊的浮点型数据,可以被所有的系统识别。
vals2 = np.array([1, np.nan, 3, 4])
不管什么操作,只要有NaN,结果都为NaN:
1 + np.nan # nan
0 * np.nan # nan
这也就意味着所有的聚合操作都能进行,但结果都是NaN
vals2.sum(), vals2.min(), vals2.max()
# (nan, nan, nan)
Numpy还提供了一些函数用于聚合运算,可以忽略掉丢失的数据:
np.nansum(vals2), np.nanmin(vals2), np.nanmax(vals2)
Pandas中的None和NaN
None和NaN在Pandas有其独特的地位,Pandas同时支持它们,并可以相互转换。
pd.Series([1, np.nan, 2, None])
结果为:
0 1.0
1 NaN
2 2.0
3 NaN
dtype: float64
对于某些不支持哨兵值的数据类型,当遇到NA值时Pandas会自动转型,例如下面的例子,integer会转型为浮点型:
x = pd.Series(range(2), dtype=int)
x[0] = None
针对Null值的操作
由上可知,Pandas将None和NaN视为可交换的,它们都可以用来指示丢失的数据。Pandas提供了一些便利函数用于处理这个数据。
- isnull():用于创建掩码数组
- notnull():isnull()的反操作
- dropna(): 返回过滤后的数据
- fillna(): 返回填充后的数据
检测null值
Pandas提供的isnull()和notnull()函数可用于检查null值,它们都会返回一个布尔值数组:
data = pd.Series([1, np.nan, 'hello', None])
data.isnull()
结果如下:
0 False
1 True
2 False
3 True
dtype: bool
前面说过,布尔值掩码可直接用于索引对象:
data[data.notnull()]
删除null值
使用dropna()来删除NA值,使用fillna()填充NA值。
data.dropna()
再看下DataFrame的情况:
df = pd.DataFrame([[1, np.nan, 2],
[2, 3, 5],
[np.nan, 4, 6]])
image.png
从DataFrame中无法删除单个的值,只能删除整行或者整列数据。
df.dropna()
如果axis为1,则删除出现NA的列:
df.dropna(axis='columns')
但是这种处理方式还是过于粗暴,有没有更为精细的控制呢?Pandas提供了更为精细的控制,通过参数how
和thresh
来控制。
how
的默认值为any
, 也就是说任意行或者列只要出现NA值就删除,如果修改为all
,则只有所有值都为NA的时候才会删除。
df[3] = np.nan
df.dropna(axis='columns', how='all')
image.png
如果需要进一步的控制,可以通过
thresh
来指定最少保留多少个非NA值。
df.dropna(axis='rows', thresh=3)
填充null值
有些时候,并不想抛弃NA值,而想填充成其他的值,Pandas提供了fillna()方法:
data = pd.Series([1, np.nan, 2, None, 3], index=list('abcde'))
输出为
a 1.0
b NaN
c 2.0
d NaN
e 3.0
dtype: float64
将上面的NA填充为0:
data.fillna(0)
也可以使用前一个值来填充:
# forward-fill
data.fillna(method='ffill')
结果为
a 1.0
b 1.0
c 2.0
d 2.0
e 3.0
dtype: float64
还可以使用后一个值来填充:
# back-fill
data.fillna(method='bfill')
结果为:
a 1.0
b 2.0
c 2.0
d 3.0
e 3.0
dtype: float64
对于DataFrame,可以指定填充的轴:
df.fillna(method='ffill', axis=1)
结果为:
image.png