写在篇前
在数据处理实践中,数据不可能十全十美,总会由于总总原因,比如不可测、测量结果丢失等原因使得部分数据缺失,处理缺失值的策略一般分为以下两种:
通过维持一个覆盖全局的掩码表示缺失值
a)维持一个与数据表大小相同的bool值数组
b)用一个bit表示有缺失值的局部状态
用一个标签值(sentinel value)表示缺失值
一般而言,不存在所谓的最佳选择,不同的编程语言和系统会选择不同的处理方式,在pandas中选用了标签的方式标示缺失值,包括浮点类型缺失值NaN、Python单体对象None。好吧,talk is cheap,show you the code.
缺失值标示方法
None标示缺失值
>>> import numpy as np
>>> vals1 = np.array([1, None, 3, 4])
>>> vals1
array([1, None, 3, 4], dtype=object)
# None只能用于object类型数组,这将会比原生int,float类型更加消耗资源,所以谨慎使用
NaN标示缺失值
>>> vals2 = np.array([1, np.nan, 3, 4])
>>> vals2
array([ 1., nan, 3., 4.])
>>> vals2.dtype
dtype('float64')
# 在这种方式中,将以原生类型float64作为数组类型,将具有更加好的性能
# 原因是这会被编译C代码,从而实现快速操作
# 关于nan还需要注意几个问题
>>> vals2.sum()
nan
>>> np.nansum(vals2)
8.0
>>> vals2.min()
nan
>>> np.nanmin(vals2)
1.0
Pandas中None与NaN的差异
Pandas把它们看成是可以等价交换的,在适当的时候会将两者进行替换。
>>> pd.Series([1, np.nan, 2, None])
0 1.0
1 NaN
2 2.0
3 NaN
dtype: float64
缺失值处理方法
- isnull() 创建一个布尔类型的掩码标签缺失值
- notnull() 与 isnull() 操作相反
- dropna() 返回一个剔除缺失值的数据
- fillna() 返回一个填充了缺失值的数据副本
发现缺失值
>>> data = pd.Series([1, np.nan, 'hello', None])
>>> data.isnull()
0 False
1 True
2 False
3 True
dtype: bool
>>> data.notnull()
0 True
1 False
2 True
3 False
dtype: bool
# 应用
>>> data[data.notnull()]
0 1
2 hello
dtype: object
剔除缺失值
# 剔除缺失值,主要用dropna()函数
def dropna(self, axis=0, how='any', thresh=None, subset=None,
inplace=False)
# axis 标示轴向,0代表行,1代表列
# how 有选项 any、all
# thresh与 how选项互斥使用,指定一行或一列非缺失值的最小个数
# subset array-like, optional,表示只将非axis轴所指定行或列纳入剔除考虑
# inplace 表示是否要创建新的副本,个人建议为True,减少资源消耗
# subset参数稍微不好理解,show u the code
df = pd.DataFrame([
[1, np.nan, 2, 5],
[2, 3, 5, 6],
[np.nan, 4, 6, 7]
])
print(df.dropna(axis=0, how='any', subset=[0, 3]))
【output:】 0 1 2 3
0 1.0 NaN 2 5
1 2.0 3.0 5 6
解析:在本例中,明明指定了按行剔除,只要有any缺失值就踢除改行,但是第一行却保留了,纳尼?这就是subset的作用,因为我们在subset参数中指定了是看第0、3列有没有缺失值从而决定是否剔除该列。
填充缺失值
数据是宝贵的,有时候我们并不忍心删除一些记录,这时候我们就需要考虑用何种方式对缺失值进行修复,这就是缺失值填充。其实缺失值填充的策略非常之多,这里主要介绍Pandas内置的填充方式,具体用哪种方式,这也许真的需要具体任务具体分析。
# 函数主要参数
def fillna(self, value=None, method=None, axis=None, inplace=False)
# value 表示填充特定值
# method 表示填充方式,与value互斥使用,有从后往前填充(method='ffill'),从前往后填充(method='bfill')
# axis 表示轴向,在ffill与bfill方式中有区别,注意体会
# inplace 表示是否要创建新的副本,个人建议为True,减少资源消耗
# 例子
data = pd.Series([1, np.nan, 2, None, 3], index=list('abcde'))
print(data.fillna(value=0))
a 1.0
b 0.0
c 2.0
d 0.0
e 3.0
dtype: float64
总结
在这一篇中,主要总结了pandas中的缺失值处理方式,但是实际上处理缺失值的方式非常灵活,需要具体任务具体分析,甚至自定义一种处理方式。要自定义处理,就需要掌握好一些pandas数据集的处理技巧,可以参考我之前的博客pandas 数据分析常用技巧,比如其中的apply
、applymap
、map
等方法对于定义高级数据操作极其方便、重要。