Pandas缺失处理

实际分析的数据常常存在缺失,要使用统计方法或机器学习的算法,必须先处理好缺失的数据。

1
import pandas as pd

查看缺失

缺失数据可以使用 isna 或 isnull (两个函数没有区别)来查看每个单元格是否缺失,如果想要查看某一列缺失或者非缺失的行,可以利用 Series 上的 isna 或者 notna 进行布尔索引。如果想要同时对几个列,检索出全部为缺失或者至少有一个缺失或者没有缺失的行,可以使用 isna, notna 和 any, all 的组合。

1
2
3
4
5
6
7
8
9
df = pd.read_csv('./data/learn_pandas.csv',
usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight', 'Transfer'])
print(df.isna().head())
print(df.isna().sum()/df.shape[0]) # 查看缺失的比例

sub_set = df[['Height', 'Weight', 'Transfer']] # 关注的三个列
print(df[sub_set.isna().all(1)]) # 三个特征全部缺失
print(df[sub_set.isna().any(1)].head()) # 三个特征至少有一个缺失
print(df[sub_set.notna().all(1)].head()) # 没有任何缺失

删除缺失

Pandas 中提供了 dropna 函数来进行缺失值的删除操作,其主要参数为轴方向 axis (默认为0,即删除行)、删除方式 how 、删除的非缺失值个数阈值 thresh(非缺失值没有达到这个数量的相应维度会被删除)、备选的删除子集 subset,其中 how 主要有 any 和 all 两种参数可以选择。

1
2
3
4
5
6
7
print(df.shape)
res = df.dropna(how='any', subset=['Height', 'Weight']) # 删除身高体重至少一个缺失的行
print(res.shape)
print(df.dropna(1, thresh=df.shape[0]-15).head()) # 删除超过15个缺失值的列,“身高”列被删除
# 使用布尔索引来完成上述两个操作
oper_1 = df.loc[df[['Height', 'Weight']].notna().all(1)] # 等同操作一
oper_2 = df.loc[:, ~(df.isna().sum()>15)] # 等同操作二

填充缺失

一种填充缺失的方法是使用 fillna 函数,其中有三个参数是常用的:value, method, limit。value 为填充值,可以是标量,也可以是索引到元素的字典映射;method 为填充方法;有用前面的元素填充ffill 和用后面的元素填充bfill 两种类型;limit 参数表示连续缺失值的最大填充次数。

1
2
3
4
5
6
7
8
9
10
import numpy as np

s = pd.Series([np.nan, 1, np.nan, np.nan, 2, np.nan], list('aaabcd'))
print(s.fillna(method='ffill')) # 用前面的值向后填充
print(s.fillna(method='ffill', limit=1)) # 连续出现的缺失,最多填充一次
print(s.fillna(s.mean())) # 用均值填充,value为标量
print(s.fillna({'a': 100, 'd': 200})) # 通过索引映射填充的值,value为字典

# 根据年级进行身高的均值填充
print(df.groupby('Grade')['Height'].transform(lambda x: x.fillna(x.mean())).head())

另一种填充缺失的方法是通过插值函数 interpolate 完成,该函数除了插值方法(默认为linear 线性插值)之外,有与 fillna 类似的两个常用参数,一个是控制方向的 limit_direction ,另一个是控制最大连续缺失值插值个数的 limit 。其中,限制插值的方向默认为 forward ,这与 fillna 的method 中的 ffill 是类似的,若想要后向限制插值或者双向限制插值可以指定为 backward 或 both 。

1
2
3
4
5
6
7
8
9
10
11
s = pd.Series([np.nan, np.nan, 1, np.nan, np.nan, np.nan, 2, np.nan, np.nan])
res = s.interpolate(limit_direction='backward', limit=1) # 默认是线性插值法,后向限制
print(res.values)
res = s.interpolate(limit_direction='both', limit=1) # 双向限制,还有前向(forward)
print(res.values)
res = s.interpolate('nearest') # 最近邻插值
print(res.values)

s = pd.Series([0, np.nan, 10], index=pd.to_datetime(['20200101', '20200102', '20200111']))
print(s.interpolate()) # 线性插值,NaN -> 5.0
print(s.interpolate(method='index')) # 用索引插值,NaN -> 1.0

Nullable类型

进入 1.0.0 版本后,Pandas 尝试设计了一种新的缺失类型 pd.NA 以及三种 Nullable 序列类型来应
对这些缺陷,它们分别是 Int, boolean 和 string。从字面意义上看 Nullable 就是可空的,言下之意就是序列类型不受缺失值的影响。例如,在上述三个 Nullable 类型中存储缺失值,都会转为 pandas 内置的 pd.NA。一般在实际数据处理时,可以在数据集读入后,先通过 convert_dtypes 转为 Nullable 类型。