這一章節(jié)我們來(lái)看下如何使用Pandas處理缺失值趋惨。
什么是缺失值
在了解缺失值(也叫控制)如何處理之前,首先要知道的就是什么是缺失值酷宵?直觀上理解亥贸,缺失值表示的是“缺失的數(shù)據(jù)”。
可以思考一個(gè)問(wèn)題:是什么原因造成的缺失值呢浇垦?其實(shí)有很多原因炕置,實(shí)際生活中可能由于有的數(shù)據(jù)不全所以導(dǎo)致數(shù)據(jù)缺失,也有可能由于誤操作導(dǎo)致數(shù)據(jù)缺失男韧,又或者人為地造成數(shù)據(jù)缺失朴摊。
來(lái)看下我們的示例吧。
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)
# 將出生日期轉(zhuǎn)為時(shí)間戳
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() 方法來(lái)操作。
1.isnull() 和 notnull()
isnull針對(duì)DataFrame對(duì)象春哨,notnull()針對(duì)Series對(duì)象
isnull():判斷數(shù)據(jù)中的缺失值,若是確實(shí)值荆隘,則為True,否則為Flase悲靴,返回一個(gè)具體數(shù)據(jù)權(quán)威bool值的DataFrame對(duì)象
# isnull():判斷數(shù)據(jù)中的缺失值,若是確實(shí)值臭胜,則為True,否則為Flase癞尚,返回一個(gè)具體數(shù)據(jù)權(quán)威bool值的DataFrame對(duì)象
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
除了簡(jiǎn)單的可以識(shí)別出哪些是缺失值或非缺失值外耸三,最常用的就是過(guò)濾掉一些缺失的行。比如浇揩,我想過(guò)濾掉用戶年齡為空的用戶仪壮,如何操作呢?
notnull():查看每行的該值是否為確實(shí)值胳徽,若是积锅,則為True,否則為False养盗,返回Series對(duì)象缚陷。
print(user_info.age.notnull())
# name
# Tom True
# Bob True
# Mary False
# James True
# Andy False
# Alice True
# Name: age, dtype: bool
# 過(guò)濾掉age為空的行后的數(shù)據(jù)
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()
對(duì)于Series對(duì)象,相對(duì)簡(jiǎn)單往核,直接刪除對(duì)應(yīng)值為缺失值的行,返回刪除后的數(shù)據(jù)(Series對(duì)象)
c = user_info.age.dropna()
print(c)
# name
# Tom 18.0
# Bob 30.0
# James 40.0
# Alice 30.0
# Name: age, dtype: float64
對(duì)于DataFrame對(duì)象箫爷,相對(duì)復(fù)雜,因?yàn)閯h除要謹(jǐn)慎操作,參數(shù)有:
- axis參數(shù)用于控制行或列聂儒,跟其他不一樣的是虎锚,axis=0 (默認(rèn))表示操作行生兆,axis=1 表示操作列屠凶。
- how 參數(shù)可選的值為 any(默認(rèn)) 或者 all。any 表示一行/列有任意元素為空時(shí)即丟棄买羞,all 一行/列所有值都為空時(shí)才丟棄非春。
- subset 參數(shù)為列表柱徙,列表項(xiàng)為索引或列名,表示刪除時(shí)只根據(jù)給出索引或列名是否為缺失值來(lái)刪除對(duì)應(yīng)的數(shù)據(jù)奇昙。
- thresh參數(shù)的類型為整數(shù)坐搔,它的作用是某行缺失值數(shù)量是否刪除的標(biāo)準(zhǔn),比如 thresh=3敬矩,會(huì)在一行/列中至少有 3 個(gè)非空值時(shí)將其保留概行。
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
# 刪除所有列都為空數(shù)據(jù)的那一行
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列為空的那行的整條數(shù)據(jù)
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個(gè)以上的行
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 這名字一看就是用來(lái)填充缺失值的业踏。
填充缺失值時(shí),常見的一種方式是使用一個(gè)標(biāo)量來(lái)填充涧卵。例如勤家,這里我樣有缺失的年齡都填充為 0。
fillna()
# 對(duì)于Series對(duì)象來(lái)說(shuō)的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
除了可以使用標(biāo)量來(lái)填充之外柳恐,還可以使用前一個(gè)或后一個(gè)有效值來(lái)填充讼庇。
設(shè)置參數(shù) method='pad' 或 method='ffill' 可以使用前一個(gè)有效值來(lái)填充近尚。
# 用前一行的該列的值進(jìn)行填充
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
設(shè)置參數(shù) method='bfill' 或 method='backfill' 可以使用后一個(gè)有效值來(lái)填充歼跟。
# 用后一行的該列的值進(jìn)行填充
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
對(duì)于DataFrame來(lái)說(shuō),該方法使用與Series一樣骚秦,只是填充的數(shù)據(jù)多了而已
注意:在對(duì)Series進(jìn)行修改或填充時(shí)往毡,默認(rèn)時(shí)不會(huì)對(duì)原數(shù)據(jù)對(duì)象修改的,可以在修改嗤详,填充,刪除的方法的參數(shù)中加上inplace=True办龄,那么這時(shí)就不會(huì)有返回值了,再次打印原數(shù)據(jù)時(shí)英融,就會(huì)發(fā)生改變
實(shí)例:
對(duì)DataFrame對(duì)象中的age列每一項(xiàng)都填充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()
除了通過(guò) fillna 方法來(lái)填充缺失值外境肾,還可以通過(guò) interpolate 方法來(lái)填充。默認(rèn)情況下使用線性差值,可以是設(shè)置 method 參數(shù)來(lái)改變方式冷离。
# 使用線性差值進(jìn)行填充
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
替換缺失值
大家有沒(méi)有想過(guò)一個(gè)問(wèn)題:到底什么才是缺失值呢瞭空?你可能會(huì)奇怪說(shuō),前面不是已經(jīng)說(shuō)過(guò)了么黑毅,None愿卒、np.nan、NaT 這些都是缺失值。但是我也說(shuō)過(guò)了渣刷,這些在 Pandas 的眼中是缺失值鹦肿,有時(shí)候在我們?nèi)祟惖难壑校承┊惓V滴覀円矔?huì)當(dāng)做缺失值來(lái)處理辅柴。
例如碌嘀,在我們的存儲(chǔ)的用戶信息中股冗,假定我們限定用戶都是青年,出現(xiàn)了年齡為 40 的,我們就可以認(rèn)為這是一個(gè)異常值。再比如第焰,我們都知道性別分為男性(male)和女性(female),在記錄用戶性別的時(shí)候妨马,對(duì)于未知的用戶性別都記為了 “unknown”,很明顯湘纵,我們也可以認(rèn)為“unknown”是缺失值。此外滤淳,有的時(shí)候會(huì)出現(xiàn)空白字符串梧喷,這些也可以認(rèn)為是缺失值。
replace()
對(duì)于上面的這種情況脖咐,我們可以使用 replace 方法來(lái)替換缺失值铺敌。
對(duì)于Series對(duì)象
# 將age列的np.nan數(shù)據(jù)項(xiàng)替換為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
也可以指定一個(gè)映射字典。
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
對(duì)于DataFrame來(lái)說(shuō)屁擅,可以在replace方法中加上參數(shù)inplace=True來(lái)做原地操作:可以對(duì)DataFrame适刀,也可以針對(duì)某一列/行(Series):
# 將數(shù)據(jù)中所有的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
# 將數(shù)據(jù)中的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
可以指定每列要替換的值。設(shè)置為原地操作
# 替換多個(gè)值
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
類似地煤蹭,我們可以將特定字符串進(jìn)行替換.
除了可以替換特定的值之外笔喉,還可以使用正則表達(dá)式來(lái)替換,如:將空白字符串替換成空值硝皂。
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
使用對(duì)象填充
除了我們自己手動(dòng)丟棄常挚、填充已經(jīng)替換缺失值之外,我們還可以使用對(duì)象來(lái)填充稽物。
例如有兩個(gè)關(guān)于用戶年齡的 Series奄毡,其中一個(gè)有缺失值,另一個(gè)沒(méi)有贝或,我們可以將沒(méi)有的缺失值的 Series 中的元素傳給有缺失值的吼过。
對(duì)于Series:
# 數(shù)據(jù)完整的Series對(duì)象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對(duì)象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中的數(shù)據(jù)替換到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
對(duì)于DataFrame,與Series一樣。