前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >用Pandas处理缺失值

用Pandas处理缺失值

作者头像
用户3577892
发布2020-06-10 19:35:15
发布2020-06-10 19:35:15
2.8K00
代码可运行
举报
文章被收录于专栏:数据科学CLUB数据科学CLUB
运行总次数:0
代码可运行

处理缺失值选择处理缺失值的方法Pandas的缺失值处理缺失值

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

处理缺失值

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

选择处理缺失值的方法

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

Pandas的缺失值

Pandas 用标签方法表示缺失值,包括两种 Python 原有的缺失值:

  • 浮点数据类型的 NaN 值
  • Python的 None 对象。
  1. None:Python对象类型的缺失值

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

代码语言:javascript
代码运行次数:0
运行
复制
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings("ignore")
代码语言:javascript
代码运行次数:0
运行
复制
vals1 = np.array([1, None, 3, 4])
vals1
代码语言:javascript
代码运行次数:0
运行
复制
array([1, None, 3, 4], dtype=object)

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

代码语言:javascript
代码运行次数:0
运行
复制
for dtype in ['object', 'int']:
    print("dtype =", dtype)
    %timeit np.arange(1E6, dtype=dtype).sum()
    print()
代码语言:javascript
代码运行次数:0
运行
复制
dtype = object
182 ms ± 26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

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

使用 Python 对象构成的数组就意味着如果你对一个包含 None 的数组进行累计操作, 如 sum() 或者 min(), 那么通常会出现类型错误。这就是说, 在 Python 中没有定义整数与 None 之间的加法运算。

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

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

代码语言:javascript
代码运行次数:0
运行
复制
vals2 = np.array([1, np.nan, 3, 4])
vals2.dtype
代码语言:javascript
代码运行次数:0
运行
复制
dtype('float64')

请注意, NumPy 会为这个数组选择一个原生浮点类型, 这意味着和之前的 object 类型数组不同, 这个数组会被编译成 C 代码从而实现快速操作。 无论和 NaN 进行何种操作, 最终结果都是NaN:

代码语言:javascript
代码运行次数:0
运行
复制
 1 + np.nan
代码语言:javascript
代码运行次数:0
运行
复制
nan
代码语言:javascript
代码运行次数:0
运行
复制
0 * np.nan
代码语言:javascript
代码运行次数:0
运行
复制
nan

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

代码语言:javascript
代码运行次数:0
运行
复制
vals2.sum(), vals2.min(), vals2.max()
代码语言:javascript
代码运行次数:0
运行
复制
(nan, nan, nan)

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

代码语言:javascript
代码运行次数:0
运行
复制
 np.nansum(vals2), np.nanmin(vals2), np.nanmax(vals2)
代码语言:javascript
代码运行次数:0
运行
复制
(8.0, 1.0, 4.0)

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

  1. Pandas中NaN与None的差异

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

代码语言:javascript
代码运行次数:0
运行
复制
pd.Series([1, np.nan, 2, None])
代码语言:javascript
代码运行次数:0
运行
复制
0    1.0
1    NaN
2    2.0
3    NaN
dtype: float64

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

代码语言:javascript
代码运行次数:0
运行
复制
x = pd.Series(range(2), dtype=int)
x
代码语言:javascript
代码运行次数:0
运行
复制
0    0
1    1
dtype: int32
代码语言:javascript
代码运行次数:0
运行
复制
x[0] = None
x
代码语言:javascript
代码运行次数:0
运行
复制
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()。每种方法都返回布尔类型的掩码数据

代码语言:javascript
代码运行次数:0
运行
复制
data = pd.Series([1, np.nan, 'hello', None])
代码语言:javascript
代码运行次数:0
运行
复制
 data.isnull()
代码语言:javascript
代码运行次数:0
运行
复制
0    False
1     True
2    False
3     True
dtype: bool

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

代码语言:javascript
代码运行次数:0
运行
复制
data[data.notnull()] 
代码语言:javascript
代码运行次数:0
运行
复制
0        1
2    hello
dtype: object

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

  1. 剔除缺失值

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

代码语言:javascript
代码运行次数:0
运行
复制
data.dropna()
代码语言:javascript
代码运行次数:0
运行
复制
0        1
2    hello
dtype: object

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

代码语言:javascript
代码运行次数:0
运行
复制
df = pd.DataFrame([[1, np.nan, 2],
                    [2, 3, 5],
                    [np.nan, 4, 6]])
print(df)
代码语言:javascript
代码运行次数:0
运行
复制
     0    1  2
0  1.0  NaN  2
1  2.0  3.0  5
2  NaN  4.0  6

没法从 DataFrame 中单独剔除一个值, 要么是剔除缺失值所在的整行, 要么是整列。根据实际需求, 有时你需要剔除整行, 有时可能是整列。默认情况下, dropna() 会剔除任何包含缺失值的整行数据:

代码语言:javascript
代码运行次数:0
运行
复制
print(df.dropna())
代码语言:javascript
代码运行次数:0
运行
复制
     0    1  2
1  2.0  3.0  5

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

代码语言:javascript
代码运行次数:0
运行
复制
print(df.dropna(axis='columns'))
代码语言:javascript
代码运行次数:0
运行
复制
   2
0  2
1  5
2  6

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

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

代码语言:javascript
代码运行次数:0
运行
复制
df[3] = np.nan
print(df)
代码语言:javascript
代码运行次数:0
运行
复制
     0    1  2   3
0  1.0  NaN  2 NaN
1  2.0  3.0  5 NaN
2  NaN  4.0  6 NaN
代码语言:javascript
代码运行次数:0
运行
复制
print(df.dropna(axis='columns', how='all'))
代码语言:javascript
代码运行次数:0
运行
复制
     0    1  2
0  1.0  NaN  2
1  2.0  3.0  5
2  NaN  4.0  6

还可以通过 thresh 参数设置行或列中非缺失值的最小数量, 从而实现更加个性化的配置:

代码语言:javascript
代码运行次数:0
运行
复制
print(df.dropna(axis='rows', thresh=3))
代码语言:javascript
代码运行次数:0
运行
复制
     0    1  2   3
1  2.0  3.0  5 NaN

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

  1. 填充缺失值

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

代码语言:javascript
代码运行次数:0
运行
复制
data = pd.Series([1, np.nan, 2, None, 3], index=list('abcde'))
data
代码语言:javascript
代码运行次数:0
运行
复制
a    1.0
b    NaN
c    2.0
d    NaN
e    3.0
dtype: float64

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

代码语言:javascript
代码运行次数:0
运行
复制
data.fillna(0)
代码语言:javascript
代码运行次数:0
运行
复制
a    1.0
b    0.0
c    2.0
d    0.0
e    3.0
dtype: float64

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

代码语言:javascript
代码运行次数:0
运行
复制
# 从前往后填充
data.fillna(method='ffill')
代码语言:javascript
代码运行次数:0
运行
复制
a    1.0
b    1.0
c    2.0
d    2.0
e    3.0
dtype: float64

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

代码语言:javascript
代码运行次数:0
运行
复制
# 从后往前填充
data.fillna(method='bfill')
代码语言:javascript
代码运行次数:0
运行
复制
a    1.0
b    2.0
c    2.0
d    3.0
e    3.0
dtype: float64

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

代码语言:javascript
代码运行次数:0
运行
复制
print(df)
代码语言:javascript
代码运行次数:0
运行
复制
     0    1  2   3
0  1.0  NaN  2 NaN
1  2.0  3.0  5 NaN
2  NaN  4.0  6 NaN
代码语言:javascript
代码运行次数:0
运行
复制
print(df.fillna(method='ffill', axis=1))
代码语言:javascript
代码运行次数:0
运行
复制
     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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据科学CLUB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 处理缺失值
    • 选择处理缺失值的方法
    • Pandas的缺失值
    • 处理缺失值
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档