這一章節(jié)我們來看下如何使用Pandas處理缺失值。
什么是缺失值
在了解缺失值(也叫控制)如何處理之前,首先要知道的就是什么是缺失值?直觀上理解,缺失值表示的是“缺失的數(shù)據(jù)”。
可以思考一個問題:是什么原因造成的缺失值呢?其實有很多原因,實際生活中可能由于有的數(shù)據(jù)不全所以導致數(shù)據(jù)缺失,也有可能由于誤操作導致數(shù)據(jù)缺失,又或者人為地造成數(shù)據(jù)缺失。
來看下我們的示例吧。
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)為時間戳
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():判斷數(shù)據(jù)中的缺失值,若是確實值,則為True,否則為Flase,返回一個具體數(shù)據(jù)權(quán)威bool值的DataFrame對象
# isnull():判斷數(shù)據(jù)中的缺失值,若是確實值,則為True,否則為Flase,返回一個具體數(shù)據(jù)權(quán)威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為空的行后的數(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()
對于Series對象,相對簡單,直接刪除對應值為缺失值的行,返回刪除后的數(shù)據(jù)(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對象,相對復雜,因為刪除要謹慎操作,參數(shù)有:
- axis參數(shù)用于控制行或列,跟其他不一樣的是,axis=0 (默認)表示操作行,axis=1 表示操作列。
- how 參數(shù)可選的值為 any(默認) 或者 all。any 表示一行/列有任意元素為空時即丟棄,all 一行/列所有值都為空時才丟棄。
- subset 參數(shù)為列表,列表項為索引或列名,表示刪除時只根據(jù)給出索引或列名是否為缺失值來刪除對應的數(shù)據(jù)。
- thresh參數(shù)的類型為整數(shù),它的作用是某行缺失值數(shù)量是否刪除的標準,比如 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
# 刪除所有列都為空數(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個以上的行
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
除了可以使用標量來填充之外,還可以使用前一個或后一個有效值來填充。
設置參數(shù) 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
設置參數(shù) 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一樣,只是填充的數(shù)據(jù)多了而已
注意:在對Series進行修改或填充時,默認時不會對原數(shù)據(jù)對象修改的,可以在修改,填充,刪除的方法的參數(shù)中加上inplace=True,那么這時就不會有返回值了,再次打印原數(shù)據(jù)時,就會發(fā)生改變
實例:
對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 參數(shù)來改變方式。
# 使用線性差值進行填充
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
替換缺失值
大家有沒有想過一個問題:到底什么才是缺失值呢?你可能會奇怪說,前面不是已經(jīng)說過了么,None、np.nan、NaT 這些都是缺失值。但是我也說過了,這些在 Pandas 的眼中是缺失值,有時候在我們?nèi)祟惖难壑校承┊惓V滴覀円矔斪鋈笔е祦硖幚怼?/p>
例如,在我們的存儲的用戶信息中,假定我們限定用戶都是青年,出現(xiàn)了年齡為 40 的,我們就可以認為這是一個異常值。再比如,我們都知道性別分為男性(male)和女性(female),在記錄用戶性別的時候,對于未知的用戶性別都記為了 “unknown”,很明顯,我們也可以認為“unknown”是缺失值。此外,有的時候會出現(xiàn)空白字符串,這些也可以認為是缺失值。
replace()
對于上面的這種情況,我們可以使用 replace 方法來替換缺失值。
對于Series對象
# 將age列的np.nan數(shù)據(jù)項替換為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方法中加上參數(shù)inplace=True來做原地操作:可以對DataFrame,也可以針對某一列/行(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
可以指定每列要替換的值。設置為原地操作
# 替換多個值
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
使用對象填充
除了我們自己手動丟棄、填充已經(jīng)替換缺失值之外,我們還可以使用對象來填充。
例如有兩個關(guān)于用戶年齡的 Series,其中一個有缺失值,另一個沒有,我們可以將沒有的缺失值的 Series 中的元素傳給有缺失值的。
對于Series:
# 數(shù)據(jù)完整的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中的數(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
對于DataFrame,與Series一樣。