【Chapter 7】 数据清洗和准备
其实数据分析中80%的时间都是在数据清理部分,loading, clearning, transforming, rearranging。而pandas非常适合用来执行这些任务。
7.1 处理缺失数据
在许多数据分析工作中,缺失数据是经常发生的。pandas的目标之一就是尽量轻松地处理缺失数据。例如,pandas对象的所有描述性统计默认都不包括缺失数据。
缺失数据在pandas中呈现的方式有些不完美,但对于大多数用户可以保证功能正常。对于数值数据,pandas使用浮点值NaN(Not a Number)表示缺失数据。我们称其为哨兵值,可以方便的检测出来:
string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data
Out[417]:
0 aardvark
1 artichoke
2 NaN
3 avocado
dtype: object
string_data.isnull()
Out[418]:
0 False
1 False
2 True
3 False
dtype: bool
在pandas中,我们采用了R语言中的惯用法,即将缺失值表示为NA,它表示不可用not available。在统计应用中,NA数据可能是不存在的数据或者虽然存在,但是没有观察到(例如,数据采集中发生了问题)。当进行数据清洗以进行分析时,最好直接对缺失数据进行分析,以判断数据采集的问题或缺失数据可能导致的偏差。
Python内置的None值在对象数组中也可以作为NA:
string_data[0] = None
string_data.isnull()
Out[420]:
0 True
1 False
2 True
3 False
dtype: bool
pandas项目中还在不断优化内部细节以更好处理缺失数据,像用户API功能,例如pandas.isnull,去除了许多恼人的细节。表7-1列出了一些关于缺失数据处理的函数。
1 Filtering Out Missing Data(过滤缺失值)
有一些方法来过滤缺失值。可以使用pandas.isnull和boolean indexing, 配合使用dropna。对于series,只会返回non-null数据和index values:
from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
data.dropna()
Out[423]:
0 1.0
2 3.5
4 7.0
dtype: float64
上面的等同于:
data[data.notnull()]
Out[424]:
0 1.0
2 3.5
4 7.0
dtype: float64
对于DataFrame,会复杂一些。你可能想要删除包含有NA的row和column。dropna默认会删除包含有缺失值的row:
data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
[NA, NA, NA], [NA, 6.5, 3.]])
data
Out[426]:
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 = data.dropna()#dropna默认会删除包含有缺失值的row
cleaned
Out[428]:
0 1 2
0 1.0 6.5 3.0
设定how=all
只会删除那些全是NA的行:
data.dropna(how='all')#设定how=all只会删除那些全是NA的行:
Out[429]:
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
3 NaN 6.5 3.0
删除列也一样,设置axis=1:
data[4] = NA
data
Out[430]:
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
data.dropna(axis=1, how='all')
Out[431]:
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 row的相关应用是是time series data。假设你想要保留有特定数字的观测结果,可以使用thresh参数:
df = pd.DataFrame(np.random.randn(7, 3))
df
Out[433]:
0 1 2
0 0.699758 -1.364918 -0.158327
1 0.479697 -1.088628 -0.513005
2 0.040280 0.267692 -0.740376
3 0.429005 0.767700 0.200298
4 0.440503 1.318111 -0.441456
5 -0.727582 -1.780834 -1.030871
6 0.979551 -0.339133 -0.504945
df.iloc[:4, 1] = NA
dfdf.iloc[:4, 1] = NA
df
0 1 2
0 -0.986575 NaN -0.251823
1 2.008704 NaN 1.827761
2 2.240856 NaN 0.273062
3 0.777182 NaN -0.220044
4 0.327522 0.781662 -0.651949
5 1.454611 -0.170581 -1.740959
6 -0.711897 0.074983 1.343807
df.iloc[:2, 2] = NA
df
Out[435]:
0 1 2
0 0.699758 NaN NaN
1 0.479697 NaN NaN
2 0.040280 NaN -0.740376
3 0.429005 NaN 0.200298
4 0.440503 1.318111 -0.441456
5 -0.727582 -1.780834 -1.030871
6 0.979551 -0.339133 -0.504945
df.dropna()
Out[436]:
0 1 2
4 0.440503 1.318111 -0.441456
5 -0.727582 -1.780834 -1.030871
6 0.979551 -0.339133 -0.504945
df.dropna(thresh=2)#删除缺失2个值以上的行
Out[437]:
0 1 2
2 0.040280 NaN -0.740376
3 0.429005 NaN 0.200298
4 0.440503 1.318111 -0.441456
5 -0.727582 -1.780834 -1.030871
6 0.979551 -0.339133 -0.504945
2 Filling In Missing Data(填补缺失值)
不是删除缺失值,而是用一些数字填补。对于大部分目的,fillna是可以用的。调用fillna的时候设置好一个常用用来替换缺失值:
df.fillna(0)
Out[438]:
0 1 2
0 0.699758 0.000000 0.000000
1 0.479697 0.000000 0.000000
2 0.040280 0.000000 -0.740376
3 0.429005 0.000000 0.200298
4 0.440503 1.318111 -0.441456
5 -0.727582 -1.780834 -1.030871
6 0.979551 -0.339133 -0.504945
给fillna传入一个dict,可以给不同列替换不同的值:
df.fillna({1: 0.5, 2: 0})
Out[439]:
0 1 2
0 0.699758 0.500000 0.000000
1 0.479697 0.500000 0.000000
2 0.040280 0.500000 -0.740376
3 0.429005 0.500000 0.200298
4 0.440503 1.318111 -0.441456
5 -0.727582 -1.780834 -1.030871
6 0.979551 -0.339133 -0.504945
fillna返回一个新对象,但你可以使用in-place来直接更改原有的数据:
_ = df.fillna(0, inplace=True)
df
Out[440]:
0 1 2
0 0.699758 0.000000 0.000000
1 0.479697 0.000000 0.000000
2 0.040280 0.000000 -0.740376
3 0.429005 0.000000 0.200298
4 0.440503 1.318111 -0.441456
5 -0.727582 -1.780834 -1.030871
6 0.979551 -0.339133 -0.504945
在使用fillna的时候,这种插入法同样能用于reindexing:
df = pd.DataFrame(np.random.randn(6, 3))
df
Out[441]:
0 1 2
0 -0.191850 -1.956573 -1.217991
1 1.103721 -0.326794 -0.518728
2 1.351338 -0.580486 1.993749
3 -1.124328 0.164845 0.000960
4 1.715065 0.416267 0.795450
5 0.162924 -0.831497 -0.164063
df.iloc[2:, 1] = NA
df
Out[442]:
0 1 2
0 -0.191850 -1.956573 -1.217991
1 1.103721 -0.326794 -0.518728
2 1.351338 NaN 1.993749
3 -1.124328 NaN 0.000960
4 1.715065 NaN 0.795450
5 0.162924 NaN -0.164063
df.fillna(method='ffill') #根据最后一位有效值,自动补全
Out[443]:
0 1 2
0 -0.191850 -1.956573 -1.217991
1 1.103721 -0.326794 -0.518728
2 1.351338 -0.326794 1.993749
3 -1.124328 -0.326794 0.000960
4 1.715065 -0.326794 0.795450
5 0.162924 -0.326794 -0.164063
limit : int, 默认值 None; 如果指定了方法,则这是连续的NaN值的前向/后向填充的最大数量。 换句话说,如果连续NaN数量超过这个数字,它将只被部分填充。 如果未指定方法,则这是沿着整个轴的最大数量,其中NaN将被填充。 如果不是无,则必须大于0。
df.fillna(method='ffill', limit=2)
Out[444]:
0 1 2
0 -0.191850 -1.956573 -1.217991
1 1.103721 -0.326794 -0.518728
2 1.351338 -0.326794 1.993749
3 -1.124328 -0.326794 0.000960
4 1.715065 NaN 0.795450
5 0.162924 NaN -0.164063
只要有些创新,你就可以利用fillna实现许多别的功能。比如说,你可以传入Series的平均值或中位数:
In [43]: data = pd.Series([1., NA, 3.5, NA, 7])
In [44]: data.fillna(data.mean())#平均值
Out[44]:
0 1.000000
1 3.833333
2 3.500000
3 3.833333
4 7.000000
dtype: float64
表7-2列出了fillna的参考。