2020-08-05--Pandas-03--缺失值处理

这一章节我们来看下如何使用Pandas处理缺失值。

什么是缺失值

在了解缺失值(也叫控制)如何处理之前,首先要知道的就是什么是缺失值?直观上理解,缺失值表示的是“缺失的数据”。

可以思考一个问题:是什么原因造成的缺失值呢?其实有很多原因,实际生活中可能由于有的数据不全所以导致数据缺失,也有可能由于误操作导致数据缺失,又或者人为地造成数据缺失。

来看下我们的示例吧。

import pandas as pd
import numpy as np

index = pd.Index(data=["Tom", "Bob", "Mary", "James", "Andy", "Alice"], name="name")

data = {
    "age": [18, 30, np.nan, 40, np.nan, 30],
    "city": ["BeiJing", "ShangHai", "GuangZhou", "ShenZhen", np.nan, " "],
    "sex": [None, "male", "female", "male", np.nan, "unknown"],
    "birth": ["2000-02-10", "1988-10-17", None, "1978-08-08", np.nan, "1988-10-17"]
}

user_info = pd.DataFrame(data=data, index=index)

# 将出生日期转为时间戳
user_info["birth"] = pd.to_datetime(user_info.birth)

print(user_info)
#         age       city      sex      birth
# name
# Tom    18.0    BeiJing     None 2000-02-10
# Bob    30.0   ShangHai     male 1988-10-17
# Mary    NaN  GuangZhou   female        NaT
# James  40.0   ShenZhen     male 1978-08-08
# Andy    NaN        NaN      NaN        NaT
# Alice  30.0             unknown 1988-10-1

可以看到,用户 Tom 的性别为 None,用户 Mary 的年龄为 NAN,生日为 NaT。在 Pandas 的眼中,这些都属于缺失值,可以使用 isnull() 或 notnull() 方法来操作。

1.isnull() 和 notnull()

isnull针对DataFrame对象,notnull()针对Series对象

isnull():判断数据中的缺失值,若是确实值,则为True,否则为Flase,返回一个具体数据权威bool值的DataFrame对象

# isnull():判断数据中的缺失值,若是确实值,则为True,否则为Flase,返回一个具体数据权威bool值的DataFrame对象
isnull = user_info.isnull()
print(isnull)
#          age   city    sex  birth
# name
# Tom    False  False   True  False
# Bob    False  False  False  False
# Mary    True  False  False   True
# James  False  False  False  False
# Andy    True   True   True   True
# Alice  False  False  False  False

除了简单的可以识别出哪些是缺失值或非缺失值外,最常用的就是过滤掉一些缺失的行。比如,我想过滤掉用户年龄为空的用户,如何操作呢?

notnull():查看每行的该值是否为确实值,若是,则为True,否则为False,返回Series对象。

print(user_info.age.notnull())
# name
# Tom       True
# Bob       True
# Mary     False
# James     True
# Andy     False
# Alice     True
# Name: age, dtype: bool

# 过滤掉age为空的行后的数据
notnull = user_info[user_info.age.notnull()]
print(notnull)
#         age      city      sex      birth
# name                                     
# Tom    18.0   BeiJing     None 2000-02-10
# Bob    30.0  ShangHai     male 1988-10-17
# James  40.0  ShenZhen     male 1978-08-08
# Alice  30.0            unknown 1988-10-17

丢弃缺失值

dropna()

对于Series对象,相对简单,直接删除对应值为缺失值的行,返回删除后的数据(Series对象)

c = user_info.age.dropna()
print(c)
# name
# Tom      18.0
# Bob      30.0
# James    40.0
# Alice    30.0
# Name: age, dtype: float64

对于DataFrame对象,相对复杂,因为删除要谨慎操作,参数有:

  • axis参数用于控制行或列,跟其他不一样的是,axis=0 (默认)表示操作行,axis=1 表示操作列。
  • how 参数可选的值为 any(默认) 或者 all。any 表示一行/列有任意元素为空时即丢弃,all 一行/列所有值都为空时才丢弃。
  • subset 参数为列表,列表项为索引或列名,表示删除时只根据给出索引或列名是否为缺失值来删除对应的数据。
  • thresh参数的类型为整数,它的作用是某行缺失值数量是否删除的标准,比如 thresh=3,会在一行/列中至少有 3 个非空值时将其保留。
print(user_info)
#         age       city      sex      birth
# name
# Tom    18.0    BeiJing     None 2000-02-10
# Bob    30.0   ShangHai     male 1988-10-17
# Mary    NaN  GuangZhou   female        NaT
# James  40.0   ShenZhen     male 1978-08-08
# Andy    NaN        NaN      NaN        NaT
# Alice  30.0             unknown 1988-10-17

# 删除所有列都为空数据的那一行
c = user_info.dropna(axis=0,how='all')
print(c)
#         age       city      sex      birth
# name
# Tom    18.0    BeiJing     None 2000-02-10
# Bob    30.0   ShangHai     male 1988-10-17
# Mary    NaN  GuangZhou   female        NaT
# James  40.0   ShenZhen     male 1978-08-08
# Alice  30.0             unknown 1988-10-17

# 删除age或者city列为空的那行的整条数据
c = user_info.dropna(axis=0,subset=['age','city'])
print(c)
#         age      city      sex      birth
# name
# Tom    18.0   BeiJing     None 2000-02-10
# Bob    30.0  ShangHai     male 1988-10-17
# James  40.0  ShenZhen     male 1978-08-08
# Alice  30.0            unknown 1988-10-17

# 删除有空值的行
c = user_info.dropna(axis=0,how='any')
print(c)
#         age      city      sex      birth
# name
# Bob    30.0  ShangHai     male 1988-10-17
# James  40.0  ShenZhen     male 1978-08-08
# Alice  30.0            unknown 1988-10-17


# 删除空值在2个以上的行
c = user_info.dropna(axis=0,how='any',thresh=2)
print(c)
#         age       city      sex      birth
# name
# Tom    18.0    BeiJing     None 2000-02-10
# Bob    30.0   ShangHai     male 1988-10-17
# Mary    NaN  GuangZhou   female        NaT
# James  40.0   ShenZhen     male 1978-08-08
# Alice  30.0             unknown 1988-10-17

填充缺失值

除了可以丢弃缺失值外,也可以填充缺失值,最常见的是使用 fillna 完成填充。

fillna 这名字一看就是用来填充缺失值的。

填充缺失值时,常见的一种方式是使用一个标量来填充。例如,这里我样有缺失的年龄都填充为 0。

fillna()

# 对于Series对象来说的fillna()
c = user_info.age.fillna(0)
print(user_info)
print(c)
# name
# Tom      18.0
# Bob      30.0
# Mary      0.0
# James    40.0
# Andy      0.0
# Alice    30.0
# Name: age, dtype: float64

除了可以使用标量来填充之外,还可以使用前一个或后一个有效值来填充。

设置参数 method='pad' 或 method='ffill' 可以使用前一个有效值来填充。

# 用前一行的该列的值进行填充
c = user_info.age.fillna(method="ffill")
print(c)
# name
# Tom      18.0
# Bob      30.0
# Mary     30.0
# James    40.0
# Andy     40.0
# Alice    30.0
# Name: age, dtype: float64

设置参数 method='bfill' 或 method='backfill' 可以使用后一个有效值来填充。

# 用后一行的该列的值进行填充
c= user_info.age.fillna(method="backfill")
print(c)
# name
# Tom      18.0
# Bob      30.0
# Mary     40.0
# James    40.0
# Andy     30.0
# Alice    30.0
# Name: age, dtype: float64

对于DataFrame来说,该方法使用与Series一样,只是填充的数据多了而已

注意:在对Series进行修改或填充时,默认时不会对原数据对象修改的,可以在修改,填充,删除的方法的参数中加上inplace=True,那么这时就不会有返回值了,再次打印原数据时,就会发生改变
实例:
对DataFrame对象中的age列每一项都填充0,其他不变。

user_info.age.fillna(0,inplace=True)
print(user_info)
#         age       city      sex      birth
# name                                      
# Tom    18.0    BeiJing     None 2000-02-10
# Bob    30.0   ShangHai     male 1988-10-17
# Mary    0.0  GuangZhou   female        NaT
# James  40.0   ShenZhen     male 1978-08-08
# Andy    0.0        NaN      NaN        NaT
# Alice  30.0             unknown 1988-10-17

interpolate()

除了通过 fillna 方法来填充缺失值外,还可以通过 interpolate 方法来填充。默认情况下使用线性差值,可以是设置 method 参数来改变方式。

# 使用线性差值进行填充
c = user_info.age.interpolate()
print(c)
# name
# Tom      18.0
# Bob      30.0
# Mary     35.0
# James    40.0
# Andy     35.0
# Alice    30.0
# Name: age, dtype: float64 

替换缺失值

大家有没有想过一个问题:到底什么才是缺失值呢?你可能会奇怪说,前面不是已经说过了么,None、np.nan、NaT 这些都是缺失值。但是我也说过了,这些在 Pandas 的眼中是缺失值,有时候在我们人类的眼中,某些异常值我们也会当做缺失值来处理。

例如,在我们的存储的用户信息中,假定我们限定用户都是青年,出现了年龄为 40 的,我们就可以认为这是一个异常值。再比如,我们都知道性别分为男性(male)和女性(female),在记录用户性别的时候,对于未知的用户性别都记为了 “unknown”,很明显,我们也可以认为“unknown”是缺失值。此外,有的时候会出现空白字符串,这些也可以认为是缺失值。

replace()

对于上面的这种情况,我们可以使用 replace 方法来替换缺失值。
对于Series对象

# 将age列的np.nan数据项替换为50
c = user_info.age.replace(np.nan,50)
print(c)
# name
# Tom      18.0
# Bob      30.0
# Mary     50.0
# James    40.0
# Andy     50.0
# Alice    30.0
# Name: age, dtype: float64

也可以指定一个映射字典。

c = user_info.age.replace({np.nan:50})
print(c)
# name
# Tom      18.0
# Bob      30.0
# Mary     50.0
# James    40.0
# Andy     50.0
# Alice    30.0
# Name: age, dtype: float64

对于DataFrame来说,可以在replace方法中加上参数inplace=True来做原地操作:可以对DataFrame,也可以针对某一列/行(Series):

# 将数据中所有的NaN替换为50
user_info.replace(np.nan,50,inplace=True)
print(user_info)
#         age       city      sex         birth
# name
# Tom    18.0    BeiJing       50  9.501408e+17
# Bob    30.0   ShangHai     male  5.930496e+17
# Mary   50.0  GuangZhou   female  5.000000e+01
# James  40.0   ShenZhen     male  2.713824e+17
# Andy   50.0         50       50  5.000000e+01
# Alice  30.0             unknown  5.930496e+17

# 将数据中的age列的59替换为NaN
user_info.age.replace(50,np.nan,inplace=True)
print(user_info)
#         age       city      sex         birth
# name                                         
# Tom    18.0    BeiJing       50  9.501408e+17
# Bob    30.0   ShangHai     male  5.930496e+17
# Mary    NaN  GuangZhou   female  5.000000e+01
# James  40.0   ShenZhen     male  2.713824e+17
# Andy    NaN         50       50  5.000000e+01
# Alice  30.0             unknown  5.930496e+17

可以指定每列要替换的值。设置为原地操作

# 替换多个值
user_info.replace({"age": 40, "birth": pd.Timestamp("1978-08-08")}, np.nan,inplace=True)
print(user_info)
#         age       city      sex      birth
# name                                      
# Tom    18.0    BeiJing     None 2000-02-10
# Bob    30.0   ShangHai     male 1988-10-17
# Mary    NaN  GuangZhou   female        NaT
# James   NaN   ShenZhen     male        NaT
# Andy    NaN        NaN      NaN        NaT
# Alice  30.0             unknown 1988-10-17

类似地,我们可以将特定字符串进行替换.

除了可以替换特定的值之外,还可以使用正则表达式来替换,如:将空白字符串替换成空值。

user_info.city.replace(r'\s+', np.nan, regex=True,inplace=True)
print(user_info)
#         age       city      sex      birth
# name                                      
# Tom    18.0    BeiJing     None 2000-02-10
# Bob    30.0   ShangHai     male 1988-10-17
# Mary    NaN  GuangZhou   female        NaT
# James  40.0   ShenZhen     male 1978-08-08
# Andy    NaN        NaN      NaN        NaT
# Alice  30.0        NaN  unknown 1988-10-17

使用对象填充

除了我们自己手动丢弃、填充已经替换缺失值之外,我们还可以使用对象来填充。

例如有两个关于用户年龄的 Series,其中一个有缺失值,另一个没有,我们可以将没有的缺失值的 Series 中的元素传给有缺失值的。

对于Series:

# 数据完整的Series对象a
a = user_info.age
a.fillna(50,inplace=True)
print(a)
# name
# Tom      18.0
# Bob      30.0
# Mary     50.0
# James    40.0
# Andy     50.0
# Alice    30.0
# Name: age, dtype: float64

# 有缺失值的Series对象b
name = pd.Index(["Tom", "Bob", "Mary", "James"], name="name")
b = pd.Series(data=[18, 30, 25, np.nan], index=name, name="user_age_info")
print(b)
# name
# Tom      18.0
# Bob      30.0
# Mary     25.0
# James     NaN
# Name: user_age_info, dtype: float64

# 将a中的数据替换到b中
b = b.combine_first(a)
print(b)
# name
# Alice    30.0
# Andy     50.0
# Bob      30.0
# James    40.0
# Mary     25.0
# Tom      18.0
# Name: user_age_info, dtype: float64

对于DataFrame,与Series一样。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342