Pandas(3)

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

处理缺失值

缺失值主要有三种形式: null、 NaN 或 NA。

选择处理缺失值的方法

在数据表或 DataFrame 中有很多识别缺失值的方法。 一般情况下可以
分为两种: 一种方法是通过一个覆盖全局的掩码表示缺失值, 另一种方
法是用一个标签值(sentinel value) 表示缺失值。
在掩码方法中, 掩码可能是一个与原数组维度相同的完整布尔类型数
组, 也可能是用一个比特(0 或 1) 表示有缺失值的局部状态。
在标签方法中, 标签值可能是具体的数据(例如用 -9999 表示缺失的整
数) , 也可能是些极少出现的形式。 另外, 标签值还可能是更全局的
值, 比如用 NaN(不是一个数) 表示缺失的浮点数, 它是 IEEE 浮点数
规范中指定的特殊字符。

使用这两种方法之前都需要先综合考量: 使用单独的掩码数组会额外出
现一个布尔类型数组, 从而增加存储与计算的负担; 而标签值方法缩小
了可以被表示为有效值的范围, 可能需要在 CPU 或 GPU 算术逻辑单元
中增加额外的(往往也不是最优的) 计算逻辑。 通常使用的 NaN 也不能
表示所有数据类型。

Pandas的缺失值

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

  1. None: Python对象类型的缺失值

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

import numpy as np
import pandas as pd
vals1 = np.array([1, None, 3, 4])
vals1
array([1, None, 3, 4], dtype=object)

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

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

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

使用 Python 对象构成的数组就意味着如果你对一个包含 None 的数
组进行累计操作, 如 sum() 或者 min(), 那么通常会出现类型错
误:

这就是说, 在 Python 中没有定义整数与 None 之间的加法运算。

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

另一种缺失值的标签是 NaN( 全称 Not a Number, 不是一个数
字)

vals2 = np.array([1, np.nan, 3, 4])
vals2.dtype
dtype('float64')

请注意, NumPy 会为这个数组选择一个原生浮点类型, 这意味着
和之前的 object 类型数组不同, 这个数组会被编译成 C 代码从而
实现快速操作。

无论和 NaN 进行何种操作, 最终结果都是
NaN:

 1 + np.nan
nan
0 * np.nan
nan

累计操作的结果定义不会抛出异常

vals2.sum(), vals2.min(), vals2.max()
C:\ProgramData\Anaconda3\lib\site-packages\numpy\core\_methods.py:32: RuntimeWarning: invalid value encountered in reduce
  return umr_minimum(a, axis, None, out, keepdims, initial)
C:\ProgramData\Anaconda3\lib\site-packages\numpy\core\_methods.py:28: RuntimeWarning: invalid value encountered in reduce
  return umr_maximum(a, axis, None, out, keepdims, initial)





(nan, nan, nan)

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

 np.nansum(vals2), np.nanmin(vals2), np.nanmax(vals2)
(8.0, 1.0, 4.0)

谨记, NaN 是一种特殊的浮点数, 不是整数、 字符串以及其他数据
类型。

  1. Pandas中NaN与None的差异

虽然 NaN 与 None 各有各的用处, 但是 Pandas 把它们看成是可以等
价交换的, 在适当的时候会将两者进行替换:

pd.Series([1, np.nan, 2, None])
0    1.0
1    NaN
2    2.0
3    NaN
dtype: float64

Pandas 会将没有标签值的数据类型自动转换为 NA。 例如, 当我们
将整型数组中的一个值设置为 np.nan 时, 这个值就会强制转换成
浮点数缺失值 NA。

x = pd.Series(range(2), dtype=int)
x
0    0
1    1
dtype: int32
x[0] = None
x
0    NaN
1    1.0
dtype: float64

除了将整型数组的缺失值强制转换为浮点数, Pandas 还会
自动将 None 转换为 NaN。

Pandas对不同类型缺失值的转换规则

类型 缺失值转换规则 NA标签值
floating 浮点型 无变化 np.nan
object 对象类型 无变化 None 或 np.nan
integer 整数类型 强制转换为 float64 np.nan
floating 浮点型 无变化 np.nan
boolean 布尔类型 强制转换为 object None 或 np.nan

需要注意的是, Pandas 中字符串类型的数据通常是用 object 类型
存储的。

处理缺失值

Pandas 基本上把 None 和 NaN 看成是可以等价交换的缺
失值形式。 为了完成这种交换过程, Pandas 提供了一些方法来发现、 剔
除、 替换数据结构中的缺失值, 主要包括以下几种。

  • isnull()
    创建一个布尔类型的掩码标签缺失值。

  • notnull()
    与 isnull() 操作相反。

  • dropna()
    返回一个剔除缺失值的数据。

  • fillna()
    返回一个填充了缺失值的数据副本。

  1. 发现缺失值

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

data = pd.Series([1, np.nan, 'hello', None])
 data.isnull()
0    False
1     True
2    False
3     True
dtype: bool

布尔类型掩码数组可以直接作为 Series
或 DataFrame 的索引使用:

data[data.notnull()] 
0        1
2    hello
dtype: object

在 Series 里使用的 isnull() 和 notnull() 同样适用于
DataFrame, 产生的结果同样是布尔类型。

  1. 剔除缺失值

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

data.dropna()
0        1
2    hello
dtype: object

而在 DataFrame 上使用它们时需要设置一些参数, 例如下面的
DataFrame:

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

没法从 DataFrame 中单独剔除一个值, 要么是剔除缺失值所
在的整行, 要么是整列。 根据实际需求, 有时你需要剔除整行, 有
时可能是整列。

默认情况下, dropna() 会剔除任何包含缺失值的整行数据:

df.dropna()
0 1 2
1 2.0 3.0 5

可以设置按不同的坐标轴剔除缺失值, 比如 axis=1(或
axis=‘columns’) 会剔除任何包含缺失值的整列数据:

df.dropna(axis='columns')
2
0 2
1 5
2 6

但是这么做也会把非缺失值一并剔除, 因为可能有时候只需要剔除
全部是缺失值的行或列, 或者绝大多数是缺失值的行或列。 这些需
求可以通过设置 how 或 thresh 参数来满足, 它们可以设置剔除行
或列缺失值的数量阈值。

默认设置是 how=‘any’, 也就是说只要有缺失值就剔除整行或整列
(通过 axis 设置坐标轴) 。 你还可以设置 how=‘all’, 这样就只
会剔除全部是缺失值的行或列了:

df[3] = np.nan
df
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='columns', how='all')
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)
0 1 2 3
1 2.0 3.0 5 NaN

第 1 行与第 3 行被剔除了, 因为它们只包含两个非缺失值。

  1. 填充缺失值

有时候可能并不想移除缺失值, 而是想把它们替换成有效的数
值。 有效的值可能是像 0、 1、 2 那样单独的值, 也可能是经过填充
或转换得到的。 虽然你可以通过isnull() 方法建立掩码来填充缺失值,Pandas 为此专门提供
了一个 fillna() 方法, 它将返回填充了缺失值后的数组副本。

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

将用一个单独的值来填充缺失值, 例如用 0:

data.fillna(0)
a    1.0
b    0.0
c    2.0
d    0.0
e    3.0
dtype: float64

可以用缺失值前面的有效值来从前往后填充(forward-fill) :

# 从前往后填充
data.fillna(method='ffill')
a    1.0
b    1.0
c    2.0
d    2.0
e    3.0
dtype: float64

也可以用缺失值后面的有效值来从后往前填充(back-fill) :

# 从后往前填充
data.fillna(method='bfill')
a    1.0
b    2.0
c    2.0
d    3.0
e    3.0
dtype: float64

DataFrame 的操作方法与 Series 类似, 只是在填充时需要设置坐
标轴参数 axis:

df
0 1 2 3
0 1.0 NaN 2 NaN
1 2.0 3.0 5 NaN
2 NaN 4.0 6 NaN
df.fillna(method='ffill', axis=1)

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

发布了57 篇原创文章 · 获赞 63 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/weixin_41503009/article/details/104185117
今日推荐