一、处理缺失数据
1.1 判断缺失数据
缺失数据在pandas中呈现的方式有些不完美,但对于大多数用户可以保证功能正常。对于数值数据,pandas使用浮点值NaN(Not a Number)表示缺失数据。常被称为哨兵值,可以方便的检查出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import pandas as pd import numpy as np string_data = pd.Series(["aardvark","artichoke",np.nan,"avocado"])
print(string_data) print(string_data.isnull())
0 aardvark 1 artichoke 2 NaN 3 avocado dtype: object 0 False 1 False 2 True 3 False dtype: bool
|
在pandas中,我们采用了R语言中的惯用法,即将缺失值表示为NA,它表示不可用not available。在统计应用中,NA数据可能是不存在的数据或者虽然存在,但是没有观察到(例如,数据采集中发生了问题)。当进行数据清洗以进行分析时,最好直接对缺失数据进行分析,以判断数据采集的问题或缺失数据可能导致的偏差。
Python内置的None值在对象数组中也可以作为NA:
1 2 3 4 5 6 7 8 9 10
| ··· string_data[0] = None print(string_data.isnull())
0 True 1 False 2 True 3 False dtype: bool
|
pandas项目中还在不断优化内部细节以更好处理缺失数据,像用户API功能,例如pandas.isnull,去除了许多恼人的细节。下表列出来一些关于缺失数据处理的函数。
1.2 滤除缺失数据
过滤掉缺失数据的办法有很多种。你可以通过pandas.isnull或布尔索引的手工方法,但dropna可能会更实用一些。对于一个Series,dropna返回一个仅含非空数据和索引值的Series:
1 2 3 4 5 6 7 8 9 10
| import numpy as np import pandas as pd data = pd.Series([1, np.nan, 3.5, np.nan, 7]) print(data.dropna())
0 1.0 2 3.5 4 7.0 dtype: float64
|
等价于:
1 2 3 4 5 6 7 8
| ··· print(data[data.notnull()])
0 1.0 2 3.5 4 7.0 dtype: float64
|
而对于DataFrame对象,事情就有点复杂了。你可能希望丢弃全NA或含有NA的行或列。dropna默认丢弃任何含有缺失值的行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from numpy import nan as NA data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA], [NA, NA, NA], [NA, 6.5, 3.]]) print("data\n",data) cleaned = data.dropna() print("cleaned\n", cleaned)
data 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 cleaned 0 1 2 0 1.0 6.5 3.0
|
传入how='all'
将只会丢弃全为NA的行:
1 2 3 4 5 6 7 8
| ··· print(data.dropna(how='all'))
0 1 2 0 1.0 6.5 3.0 1 1.0 NaN NaN 3 NaN 6.5 3.0
|
用这种方式丢弃列,只需要传入axis=1
即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ··· data[4] = NA print(data) print(data.droupna(axis=1, how="all"))
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 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
|
另一个滤除DataFrame行的问题涉及时间序列数据。假设只想只想保留下一部分观测数据,可以用thresh参数实现目的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| df = pd.DataFrame(np.random.randn(7,3)) print(df) df.iloc[:4, 1] = NA print(df) df.iloc[:2, 2] = NA print(df) print("-----滤除缺失值-----") print(df.dropna()) print(df.dropna(thresh=2))
0 1 2 0 0.338764 0.555826 0.982879 1 1.377187 1.301776 0.293192 2 0.671224 0.887082 -1.901940 3 -0.246175 0.756483 -0.034251 4 1.776606 -1.924073 -0.141436 5 1.107341 -0.935829 -1.276003 6 0.352095 1.011681 -0.849741 0 1 2 0 0.338764 NaN 0.982879 1 1.377187 NaN 0.293192 2 0.671224 NaN -1.901940 3 -0.246175 NaN -0.034251 4 1.776606 -1.924073 -0.141436 5 1.107341 -0.935829 -1.276003 6 0.352095 1.011681 -0.849741 0 1 2 0 0.338764 NaN NaN 1 1.377187 NaN NaN 2 0.671224 NaN -1.901940 3 -0.246175 NaN -0.034251 4 1.776606 -1.924073 -0.141436 5 1.107341 -0.935829 -1.276003 6 0.352095 1.011681 -0.849741 -----滤除缺失值----- 0 1 2 4 1.776606 -1.924073 -0.141436 5 1.107341 -0.935829 -1.276003 6 0.352095 1.011681 -0.849741 0 1 2 2 0.671224 NaN -1.901940 3 -0.246175 NaN -0.034251 4 1.776606 -1.924073 -0.141436 5 1.107341 -0.935829 -1.276003 6 0.352095 1.011681 -0.849741
|
1.3 填充缺失数据
你可能不想滤除缺失数据(有可能会丢弃跟它有关的其他数据),而是希望通过其他方式填补那些“空洞”。对于大多数情况而言,fillna方法是最主要的函数。通过一个常数调用fillna就会将缺失值替换为那个常数值:
1 2 3 4 5 6 7 8 9 10 11 12
| ··· df.fillna(0)
0 1 2 0 1.305311 0.000000 0.000000 1 -0.073801 0.000000 0.000000 2 -0.060627 0.000000 -0.593883 3 0.548139 0.000000 0.641778 4 -1.184230 1.254661 -1.126495 5 -1.200294 -0.782388 -1.953092 6 0.250766 -0.331018 0.574662
|
若是通过一个字典调用fillna,就可以实现对不同的列填充不同的值:
1 2 3 4 5 6 7 8 9 10 11 12
| ··· print(df.fillna({1:0.5, 2:0}))
0 1 2 0 -0.198972 0.500000 0.000000 1 -0.146856 0.500000 0.000000 2 0.895844 0.500000 -0.072794 3 -0.597936 0.500000 -1.414083 4 0.388478 2.227255 1.836909 5 0.049245 -0.598432 -0.133057 6 0.081092 -0.244341 0.211493
|
fillna默认返回新对象,但也可以对现有对象进行就地修改:
1 2 3 4 5 6 7 8 9 10 11 12
| ··· _ = df.fillna(0, inplace=True)
0 1 2 0 -1.154844 0.500000 0.000000 1 -1.027803 0.500000 0.000000 2 0.729127 0.500000 -0.622004 3 0.544168 0.500000 -0.743561 4 -0.308872 0.654360 -0.208569 5 -0.052516 0.406727 -0.322353 6 -1.189762 -0.297617 0.503907
|
对reindexing有效的那些插值方法也可用于fillna:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| ··· df = pf.DataFrame(np.random.randon(6,3)) df.iloc[2:, 1] = NA df.iloc[4:, 2] = NA
print(df) print(df.fillna(method="ffill")) print(df.fillna(method="ffill", limit=2))
0 1 2 0 0.607541 0.954887 -0.417059 1 -2.160793 -0.328896 -0.480799 2 -0.632083 NaN -1.436467 3 -0.707566 NaN -1.436972 4 -0.140556 NaN NaN 5 -0.148451 NaN NaN 0 1 2 0 0.607541 0.954887 -0.417059 1 -2.160793 -0.328896 -0.480799 2 -0.632083 -0.328896 -1.436467 3 -0.707566 -0.328896 -1.436972 4 -0.140556 -0.328896 -1.436972 5 -0.148451 -0.328896 -1.436972 0 1 2 0 0.607541 0.954887 -0.417059 1 -2.160793 -0.328896 -0.480799 2 -0.632083 -0.328896 -1.436467 3 -0.707566 -0.328896 -1.436972 4 -0.140556 NaN -1.436972 5 -0.148451 NaN -1.436972
|
只要有些创新,你就可以利用fillna实现许多别的功能。比如说,你可以传入Series的平均值或中位数:
1 2 3 4 5 6 7 8 9 10 11
| ··· data = pd.Series([1., NA, 3.5, NA, 7]) print(data.fillna(data.mean()))
0 1.000000 1 3.833333 2 3.500000 3 3.833333 4 7.000000 dtype: float64
|