一、删除缺失值
在进行数据分析和建模的过程中,我们80%的时间往往花在数据准备上:加载、清理、转换、处理和重新排列。为了提高这一过程的效率,Pandas提供了一系列的高级、灵活和快速的工具集,配合Python语言内置的处理功能,可以满足绝大多数场景下的使用需求。
Pandas中,使用numpy.nan标识缺失值,在打印的时候,经常以空字符串、NA、NaN、NULL等形式出现。Python内置的None值也被当作缺失值处理。但合法的缺失值只有NaN和None,另外一些我们自己定义的缺失值不属于语法上的,比如你将-999看作缺失值。
在Pandas中, None和NaN的区别:
- None被看作一个object对象,需要消耗更多的资源,处理速度更慢。不支持一些数学操作,因为None+数字是错误的语法。很多时候None会自动转换成NaN。
- NaN是float64类型,虽然名字叫做‘不是一个数’,但却属于数字类,可以进行数学运算不会报错,虽然所有和它进行计算的最终结果依然是NaN。它的运算速度更快,还支持全局性的操作。
以下是主要的缺失值处理方法:
- dropna:删除缺失值
- fillna: 用某些值填充缺失的数据或使用插值方法(比如ffill\bfill)
- isnull:判断哪些值是缺失值,返回布尔
- notnull:isnull的反函数
通常使用dropna方法滤删除缺失值,默认它不直接修改数据,而是返回一个新对象。如果想原地修改,举一反三,请尝试inplace参数:
In [12]: from numpy import nan as NA # 导入惯例
In [13]: s = pd.Series([1, NA, 3.5, NA, 7]) In [14]: s Out[14]: 0 1.0
1 NaN 2 3.5
3 NaN 4 7.0 dtype: float64 In [15]: s.dropna() # 本质上就是把缺失值删除
Out[15]: 0 1.0
2 3.5
4 7.0 dtype: float64 In [16]: s[s.notnull()] # 等同于上面的操作
Out[16]: 0 1.0
2 3.5
4 7.0 dtype: float64
在处理DataFrame对象的缺失值的时候,可能会复杂点。无法删除df的单个元素,只能整行整列的删除,dropna默认情况下会删除包含缺失值的行!为什么呢?因为DataFrame对象的每一行数据,在实际中,相当于一个样本,大量的样本中删除了一条,关系不大。但DataFrame对象的每一列相当于大量样本中共同的某个特征的值,如果删除了一个特征,那么对整个样本集的影响非常大。
In [20]: df = pd.DataFrame([[1, 6.5, 3],[1, NA, NA],[NA, NA, NA],[NA, 6.5,3]]) In [21]: df Out[21]: 0 1 2 0 1.0 6.5 3.0
1 1.0 NaN NaN 2 NaN NaN NaN 3 NaN 6.5 3.0 In [22]: df.dropna() # 只剩1行了
Out[22]: 0 1 2 0 1.0 6.5 3.0 In [23]: df.dropna(how='all') # 只将整行都是缺失值的删除
Out[23]: 0 1 2 0 1.0 6.5 3.0
1 1.0 NaN NaN 3 NaN 6.5 3.0 In [24]: df[4] = NA # 新增一列全是缺失值
In [25]: df Out[25]: 0 1 2 4 0 1.0 6.5 3.0 NaN 1 1.0 NaN NaN NaN 2 NaN NaN NaN NaN 3 NaN 6.5 3.0 NaN In [26]: df.dropna(axis=1, how='all') # 指定以列的形式删除
Out[26]: 0 1 2 0 1.0 6.5 3.0
1 1.0 NaN NaN 2 NaN NaN NaN 3 NaN 6.5 3.0
注意上面axis和how参数的用法。
还可以通过thresh参数设置行或列中非缺失值的最小数量,在此数量以下的将被删除。
二、补全缺失值
所有数据都是宝贵的,大多数时候,我们不希望丢弃原始数据,而是补全缺失值。
fillna是补全缺失值的方法,为它提供一个固定值即可:
In [31]: df = pd.DataFrame(np.random.randn(7,3)) In [32]: df Out[32]: 0 1 2 0 -0.229682 -0.483246 -0.063835
1 0.716649 1.593639 -1.364550
2 -1.362614 1.628310 -1.617992
3 1.128828 -1.120265 -0.657313
4 1.078143 1.136835 -0.427125
5 0.441696 0.219477 0.695700
6 -0.501183 1.453678 -2.734985 In [33]: df.iloc[:4, 1] = NA In [35]: df.iloc[:2, 2] = NA In [36]: df Out[36]: 0 1 2 0 -0.229682 NaN NaN 1 0.716649 NaN NaN 2 -1.362614 NaN -1.617992
3 1.128828 NaN -0.657313
4 1.078143 1.136835 -0.427125
5 0.441696 0.219477 0.695700
6 -0.501183 1.453678 -2.734985 In [37]: df.fillna(0) Out[37]: 0 1 2 0 -0.229682 0.000000 0.000000
1 0.716649 0.000000 0.000000
2 -1.362614 0.000000 -1.617992
3 1.128828 0.000000 -0.657313
4 1.078143 1.136835 -0.427125
5 0.441696 0.219477 0.695700
6 -0.501183 1.453678 -2.734985
也可以提供一个字典,为不同的列设定不同的填充值。
In [38]: df.fillna({1:1, 2:2}) Out[38]: 0 1 2 0 -0.229682 1.000000 2.000000
1 0.716649 1.000000 2.000000
2 -1.362614 1.000000 -1.617992
3 1.128828 1.000000 -0.657313
4 1.078143 1.136835 -0.427125
5 0.441696 0.219477 0.695700
6 -0.501183 1.453678 -2.734985
当然,fillna也不会原地修改数据,如果你想,请使用inplace参数:
In [39]: _ = df.fillna(0, inplace=True) In [40]: df Out[40]: 0 1 2 0 -0.229682 0.000000 0.000000
1 0.716649 0.000000 0.000000
2 -1.362614 0.000000 -1.617992
3 1.128828 0.000000 -0.657313
4 1.078143 1.136835 -0.427125
5 0.441696 0.219477 0.695700
6 -0.501183 1.453678 -2.734985
也可以使用ffill和bfill这种插值法填充缺失值:
In [41]: df = pd.DataFrame(np.random.randn(6,3)) In [42]: df.iloc[2:, 1]=NA In [43]: df.iloc[4:, 2]=NA In [44]: df Out[44]: 0 1 2 0 -0.858762 0.083342 -0.315598
1 -0.211846 0.076648 1.188298
2 -0.513364 NaN 0.079216
3 0.398399 NaN -0.290225
4 -1.375898 NaN NaN 5 0.932812 NaN NaN In [45]: df.fillna(method='ffill') # 使用前一个值进行填充
Out[45]: 0 1 2 0 -0.858762 0.083342 -0.315598
1 -0.211846 0.076648 1.188298
2 -0.513364 0.076648 0.079216
3 0.398399 0.076648 -0.290225
4 -1.375898 0.076648 -0.290225
5 0.932812 0.076648 -0.290225 In [46]: df.fillna(method='ffill',limit=2) # 限制填充次数
Out[46]: 0 1 2 0 -0.858762 0.083342 -0.315598
1 -0.211846 0.076648 1.188298
2 -0.513364 0.076648 0.079216
3 0.398399 0.076648 -0.290225
4 -1.375898 NaN -0.290225
5 0.932812 NaN -0.290225 In [47]: df.fillna(method='bfill') # 后向填充此时无效
Out[47]: 0 1 2 0 -0.858762 0.083342 -0.315598
1 -0.211846 0.076648 1.188298
2 -0.513364 NaN 0.079216
3 0.398399 NaN -0.290225
4 -1.375898 NaN NaN 5 0.932812 NaN NaN
其实使用fillna有很多技巧,需要大家平时多收集多尝试,比如使用平均值来填充:
In [48]: s = pd.Series([1, NA, 3.5, NA, 7]) In [49]: s.fillna(s.mean()) Out[49]: 0 1.000000
1 3.833333
2 3.500000
3 3.833333
4 7.000000 dtype: float64
三、删除重复值
原始数据中,往往包含大量重复的行,需要我们删除,让数据集更健康。
In [51]: df = pd.DataFrame({'k1':['one','two']*3 + ['two'], 'k2':[1,1,2,3,3,4,4]}) In [52]: df # 最后一行是重复的
Out[52]: k1 k2 0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
6 two 4 In [53]: df.duplicated() #
Out[53]: 0 False 1 False 2 False 3 False 4 False 5 False 6 True dtype: bool In [54]: df.drop_duplicates() Out[54]: k1 k2 0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4 In [55]: df # 并没有改变原数据
Out[55]: k1 k2 0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
6 two 4
上面,使用duplicated方法判断各行是否有重复,并返回一个布尔值Series。然后使用drop_duplicates方法将重复行删除,留下那些不重复的。
如果想指定根据某列的数据进行去重判断和操作,可以指定列名:
In [56]: df['v1'] = range(7) In [57]: df Out[57]: k1 k2 v1 0 one 1 0 1 two 1 1
2 one 2 2
3 two 3 3
4 one 3 4
5 two 4 5
6 two 4 6 In [58]: df.drop_duplicates(['k1']) Out[58]: k1 k2 v1 0 one 1 0 1 two 1 1
默认情况下都是保留第一个观察到的值,如果想保留最后一个,可以使用参数keep='last':
In [59]: df.drop_duplicates(['k1','k2'], keep='last') Out[59]: k1 k2 v1 0 one 1 0 1 two 1 1
2 one 2 2
3 two 3 3
4 one 3 4
6 two 4 6
四、替换值
可以使用replace将pandas对象中的指定值替换为别的值:
In [77]: df = pd.DataFrame(np.random.randint(12,size=(4,3))) In [78]: df Out[78]: 0 1 2 0 4 5 10
1 3 0 3
2 7 7 4
3 6 4 4 In [79]: df.replace(4, NA) # 将4替换为缺失值
Out[79]: 0 1 2 0 NaN 5.0 10.0
1 3.0 0.0 3.0
2 7.0 7.0 NaN 3 6.0 NaN NaN In [80]: df.replace([3,4], NA) # 将3和4都替换为缺失值
Out[80]: 0 1 2 0 NaN 5.0 10.0
1 NaN 0.0 NaN 2 7.0 7.0 NaN 3 6.0 NaN NaN In [81]: df.replace([3,4], [NA,0]) # 3和4分别替换为缺失值和0
Out[81]: 0 1 2 0 0.0 5.0 10.0
1 NaN 0.0 NaN 2 7.0 7.0 0.0
3 6.0 0.0 0.0 In [82]: df.replace({3:NA,4:0}) # 参数的字典形式
Out[82]: 0 1 2 0 0.0 5.0 10.0
1 NaN 0.0 NaN 2 7.0 7.0 0.0
3 6.0 0.0 0.0