5.8 KiB
3.1.7:数值运算与缺失值处理
选择处理缺失值的方法
一般情况下可以分为两种:一种方法是通过一个覆盖全局的掩码表示缺失值,另一种方法是用一个**标签值(sentinel value)
**表示缺失值。
-
掩码方法中掩码可能是一个与原数组维度相同的完整布尔类型数组,也可能是用一个比特(
0
或1
)表示有缺失值的局部状态。 -
标签方法中,标签值可能是具体的数据(例如用
-9999
表示缺失的整数),也可能是些极少出现的形式。
Pandas缺失值
综合考虑各种方法的优缺点,Pandas
最终选择用标签方法表示缺失值,包括两种Python
原有的缺失值:浮点数据类型的NaN
值,以及 Python
的None
对象。
None
:Python
对象类型的缺失值Pandas
可以使用的第一种缺失值标签是None
,它是一个Python
单体对象,由于None
是一个Python
对象,所以不能作为任何NumPy / Pandas
数组类型的缺失值,只能用于'object'
数组类型(即由Python
对象构成的数组)
np.array([1, None, 3, 4])
Out: array([1, None, 3, 4], dtype=object)
NaN
:数值类型的缺失值 另一种缺失值的标签是NaN
(全称Not a Number
),是一种按照IEEE
浮点数标准设计、在任何系统中都兼容的特殊浮点数:
vals2 = np.array([1, np.nan, 3, 4])
vals2.dtype
Out: dtype('float64')
注意:NumPy
会为这个数组选择一个原生浮点类型,这意味着和之前的 object
类型数组不同,这个数组会被编译成C
代码从而实现快速操作。你可以把NaN
看作是一个数据类病毒——它会将与它接触过的数据同化。无论和NaN
进行何种操作,最终结果都是NaN
:
1 + np.nan
0 * np.nan #这两个的结果都为nan
虽然这些累计操作的结果定义是合理的(即不会抛出异常),但是并非总是有效的:
vals2 = np.array([1, np.nan, 3, 4])
vals2.sum(), vals2.min(), vals2.max()
Out:(nan, nan, nan)
NumPy
也提供了一些特殊的累计函数,它们可以忽略缺失值的影响:
np.nansum(vals2), np.nanmin(vals2), np.nanmax(vals2)
Out: (8.0, 1.0, 4.0)
谨记,NaN
是一种特殊的浮点数,不是整数、字符串以及其他数据类型。
Pandas
中NaN
与None
的差异 虽然NaN
与None
各有各的用处,但是Pandas
把它们看成是可以等价交换的:
pd.Series([1, np.nan, 2, None])
Out:
0 1.0
1 NaN
2 2.0
3 NaN
dtype: float64
Pandas
会将没有标签值的数据类型自动转换为NA
。例如我们将整形数组中的一个值设置为np.nan
时,这个值就会强制转换成浮点数缺失值NA
,下表表示Pandas
对不同类型缺失值的转换规则:
类型 | 缺失值转换规则 | NA标签值 |
---|---|---|
floating 浮点型 | 无变化 | np.nan |
object 对象类型 | 无变化 | np.nan或None |
integer 整数类型 | 强制转换为 float64 | np.nan |
boolean 布尔类型 | 强制转换为 object | np.nan或None |
发现缺失值
Pandas
有两种方法可以发现缺失值:isnull()
和notnull()
,这俩个中方法皆可用于Series
和DataFrame
。每种方法都返回布尔类型的掩码数据。
data=pd.Series([1,np.nan,'hello',None])
data.isnull()
Out:
0 False
1 True
2 False
3 True
dtype: bool
布尔类型掩码数组可以直接作为Series
或DataFrame
的索引使用。
data[data.notnull()]
Out:
0 1
2 hello
dtype: object
处理缺失值
- dropna()删除缺失值
作用在
Series
对象上时,它的作用和data[data.notnull()]
一样,而在DataFrame
上使用它们时需要设置一些参数:
df = pd.DataFrame([[1, np.nan, 2],
[2, 3, 5],
[np.nan, 4, 6]])
# 如果不传任何参数时,dropna会删除有缺失值的所有行
df.dropna()
Out:
0 1 2
1 2.0 3.0 5
# 传入axis=1(或者axis='columns')时会删除所有包含缺失值的列
df.dropna(axis=1)
Out:
2
0 2
1 5
2 6
但是这么做也会把非缺失值一并剔除,因为可能有时候只需要剔除全部是缺失值的行或列,或者绝大多数是缺失值的行或列。这些需求可以通过设置how
或thresh
参数来满足,它们可以设置剔除行或列缺失值的数量阈值。
df[3] = np.nan
Out:
0 1 2 3
0 1.0 NaN 2 NaN
1 2.0 3.0 5 NaN
2 NaN 4.0 6 NaN
# 删除值全部为缺失值的列
df.dropna(axis=1,how="all")
Out:
0 1 2
0 1.0 NaN 2
1 2.0 3.0 5
2 NaN 4.0 6
#通过 thresh 参数设置行或列中非缺失值的最小数量
df.dropna(axis='rows', thresh=3) #非缺失值至少有3个
Out:
0 1 2 3
1 2.0 3.0 5 NaN
- fillna()填充缺失值
有时候你可能并不想移除缺失值,而是想把它们替换成有效的数值。虽然你可以通过
isnull()
方法建立掩码来填充缺失值,但是Pandas
为此专门提供了一个fillna()
方法,它将返回填充了缺失值后的数组副本。
data=pd.Series([1, np.nan, 2, None, 3],index=list('abcd')
data.fillna(0) # 将缺失值填充为0
Out:
a 1.0
b 0.0
c 2.0
d 0.0
e 3.0
dtype: float64
可以用缺失值前面的有效值来从前往后填充(forward-fill)
,也可以用缺失值后面的有效值来从后往前填充(back-fill)
:
data.fillna(method="ffill")
Out:
a 1.0
b 1.0
c 2.0
d 2.0
e 3.0
dtype: float64
data.fillna(method='bfill')
Out:
a 1.0
b 2.0
c 2.0
d 3.0
e 3.0
dtype: float64
DataFrame
的操作方法与Series
类似,只是在填充时需要设置坐标轴参数axis
。
df.fillna(method='ffill', axis=1) # bfill同样适用
Out:
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