Pandas数据处理之处理缺失值

《Python数据科学手册》读书笔记

3.5 处理缺失值

  在实际数据处理过程中,缺失值和异常值是最常见的,很少有数据是干净整齐,直接可用的。所以需要进行数据清洗。

3.5.1 选择处理缺失值的方法

 在DataFrame中有很多识别缺失值的方法。一般情况下分为两种:

  • 通过一个单独的同样大小的掩码df来表示缺失值
  • 用一个标签值(如NaN)表示缺失值

  在掩码方法中,掩码可能是一个于原数组维度相同的完整布尔类型数组,也可能是用一个比特(0或1)表示有缺失值.但是,使用单独的掩码数组会额外出现一个布尔类型数组,从而增加存储与计算的负担.

  在标签方法中,标签值可能是具体的数据(如-9999代表缺失值),也可能是更全局的值,比如NaN表示确实的浮点数。但是缩小了可以被表示为有效值的范围。而且NaN也不能表示所有数据类型。

3.5.2 Pandas的缺失值

  Pandas使用标签方法表示缺失值,包括两种Python原有的缺失值:浮点数据类型的NaN值、以及Python的None对象。

1.None:Python对象的缺失值

  Pandas可以使用的第一种缺失值标签是None,他是一个Python单体对象(object),经常在代码中表示缺失值。由于None是一个Python对象,所以不能作为任何Numpy/Pandas数组类型的缺失值,只能用于‘object’数组类型(即由Python对象构成的数组):

In [1]:import pandas as pd
	   import numpy as np
In [2]:v1 = np.array([1,None,3,4])
	   v1 
Out[2]:array([1, None, 3, 4], dtype=object)

  这里dtype=object 表示NumPy 认为由于这个数组是Python 对象构成的,因此将其类型判断为object。虽然这种类型在某些情景中非常有用,对数据的任何操作最终都会在Python 层面完成,但是在进行常见的快速操作时,这种类型比其他原生类型数组要消耗更多的资源:

In [3]:for dtype in ['object', 'int']:
	   print("dtype =", dtype)
	   %timeit np.arange(1E6, dtype=dtype).sum()
       print()
       
dtype= object
93 ms ± 5.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

dtype= int
3.89 ms ± 67.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

  而且在使用None作为缺失值标签时,如果使用sum()或者min(),通常会发生类型错误:

In [4]:v1.sum()

TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

2.NaN:数值类型的缺失值

  另一种缺失值的标签是NaN(Not a Number),本质上是一个浮点数,这意味着和之前的object类型数组不同,他会被编译成C语言而快速操作(Numpy是基于C语言的,object是基于Python的)。
  你可以把NaN看作是一个数据类病毒-------他会将与它接触过的数据同化。无论和NaN进行何种操作,最终结果都是NaN:

In [5]: type(np.nan)
Out[5]: float

In [6]: 1 + np.nan
Out[6]: nan

In [7]: 0 * np.nan
Out[7]: nan

  但是在进行累计操作时,虽然不会出现异常,但有时并不是我们想要的结果:

In [8]: v2 = np.array([1,np.nan,3,4])
		v2
Out[8]: array([ 1., nan,  3.,  4.])

In [9]:	v2.sum(), v2.min(), v2.max()
Out[9]: (nan, nan, nan)

  Numpy提供了一些特殊的累计函数,他们可以忽略缺失值的影响:

In [10]: np.nansum(v2) # 忽略缺失值,计算有效值的和
Out[10]: 8.0

3.5.3 处理缺失值

  Pandas 提供了一些方法来发现、剔除、替换数据结构中的缺失值,主要包
括以下几种:

  • isnull() : 创建一个布尔类型的掩码标签缺失值。
  • notnull():与isnull() 操作相反。
  • dropna():返回一个剔除缺失值的数据。
  • fillna():返回一个填充了缺失值的数据副本。

1.发现缺失值

  Pandas 数据结构有两种有效的方法可以发现缺失值:isnull() 和notnull()。每种方法都返回布尔类型的掩码数据,例如:

In [11]: data = pd.Series([1, np.nan, 'hello', None])
		 data
Out[11]: 0        1
		 1      NaN
		 2    hello
		 3     None
		 dtype: object
		 
In [12]: a = data.isnull()
		 a		 
Out[12]: a    False
		 b     True
		 c    False
		 d     True
		 e    False
		 dtype: bool
# 可以将布尔掩码数组作为索引使用
In [13]: data[data.notnull()]
Out[13]: 0        1
		 2    hello
		 dtype: object

  isnull()和notnull()在DataFrame中同样适用。

2.剔除缺失值

  除了前面介绍的掩码方法,还有两种很好用的缺失值处理方法,分别是dropna()(剔除缺失值)和fillna()(填充缺失值)。

In [14]:data.dropna()
Out[14]:0        1
		2    hello
		dtype: object

  在DataFrame中,也同样使用,不过需要做一些改变。

In [15]:df = pd.DataFrame([[1, np.nan, 2],
						   [2, 3, 5],
						   [np.nan, 4, 6]])
		df
Out[15]:
	0	1	2
0	1.0	NaN	2
1	2.0	3.0	5
2	NaN	4.0	6

In [16]:df.dropna()
Out[16]:
	0	1	2
1	2.0	3.0	5

  可以看出,当我们在DataFrame中使用dropna()而不加任何参数的时候,会剔除缺失值所在的整行或者整列。控制参数为axis,默认情况下会剔除任何包含缺失值的整行数据,axis=0。当我们想删除列时,令axis=1(或axis=‘columns’)。

In [17]:df.dropna(axis=1)
Out[17]:
		2
	0	2
	1	5
	2	6

  但是这么做也会把非缺失值一并剔除,因为可能有时候只需要剔除全部是缺失值的行或列,或者绝大多数是缺失值的行或列。这些需求可以通过设置how 或thresh 参数来满足,它们可以设置剔除行或列缺失值的数量阈值。
  默认设置是how=‘any’,也就是说只要有缺失值就剔除整行或整列(通过axis 设置坐标轴)。你还可以设置how=‘all’,这样就只会剔除全部是缺失值的行或列了:

In [18]:df[3] = np.nan
		df
Out[18]:
	0	1	2	3
0	1.0	NaN	2	NaN
1	2.0	3.0	5	NaN
2	NaN	4.0	6	NaN

In [19]:df.dropna(axis='columns', how='all')
Out[19]:
	0	1	2
0	1.0	NaN	2
1	2.0	3.0	5
2	NaN	4.0	6

  我们也可以设置一个阈值thresh,当有效值大于某个数值时,就不删除该行或该列。

In [20]:df.dropna(axis='rows', thresh=3)
Out[20]:
	0	 1	  2    3
1  2.0  3.0   5   NaN

3.填充缺失值

  其实,有时候我们并不想移除缺失值,特别当数据量较小时,而是想把他们替换成有效地数值。这时候我们使用fillna()方法,它将返回填充了缺失值后的数组副本。

In [21]:data = pd.Series([1, np.nan, 2, None, 3], index=list('abcde'))
		data
Out[21]:
a    1.0
b    NaN
c    2.0
d    NaN
e    3.0
dtype: float64

  我们使用一个任意值来填充缺失值:

In [22]:data.fillna(-99)
Out[22]:
a     1.0
b   -99.0
c     2.0
d   -99.0
e     3.0
dtype: float64

  如果我们想要用缺失值前面的有效值进行填充,可以令method=ffill(forward-fill);
  如果我们想要用缺失值后面的有效值进行填充,可以令method=bfill(back-fill);

In [23]:data.fillna(method="ffill")	# 向前填充
Out[23]:
a    1.0
b    1.0
c    2.0
d    2.0
e    3.0
dtype: float64

In [24]:data.fillna(method="bfill")	# 向后填充
Out[24]:
a    1.0
b    2.0
c    2.0
d    3.0
e    3.0
dtype: float64

  DataFrame也同样适用,只要加上axis参数:

In [25]:df.fillna(method='ffill', axis=1)	# axis=1,代表后一列向前一列看齐
Out[25]:
	 0	 1	 2	 3
0	1.0	1.0	2.0	2.0
1	2.0	3.0	5.0	5.0
2	NaN	4.0	6.0	6.0

  需要注意的是,假如在从前往后填充时,需要填充的缺失值前面没有值,那么它就仍然是缺失值。

你可能感兴趣的:(Python数据科学手册)