數據分析之Pandas缺失數據處理

↑↑↑關注後"星標"Datawhale

每日干貨 & 每月組隊學習,不錯過

 Datawhale乾貨 
作者:耿遠昊,Datawhale成員,華東師範大學

Pandas 是一個強大的分析結構化數據的工具集,它的使用基礎是Numpy(提供高性能的矩陣運算),用於數據挖掘和數據分析,同時也提供數據清洗功能。
在往期文章中,已經詳細講解了Pandas做分析數據的四種基本操作:索引、分組、變形及合併。現在,開始正式介紹Pandas的數據結構類型:缺失數據、文本數據、分類數據和時序數據。
在接下來的兩章中,會接觸到數據預處理中比較麻煩的類型,即缺失數據和文本數據(尤其是混雜型文本)。今天,我們首先對缺失數據進行系統地梳理。
 本文目錄

              1. 基礎概念
                  1.1. 缺失值分類
                  1.2. 缺失值處理方法
2. 缺失觀測及其類型
    2.1. 瞭解缺失信息
    2.2. 三種缺失符號
    2.3. Nullable類型與NA符號

    2.4. NA的特性

    2.5. convert_dtypes方法

              3. 缺失數據的運算與分組 
                3.1. 加號與乘號規則
                  3.2. groupby方法中的缺失值
               4. 填充與剔除
                4.1. fillna方法
                  4.2. dropna方法
               5. 插值

    5.1. 線性插值
    5.2. 高級插值方法      5.3. interpolate中的限制參數 6. 問題及練習    6.1. 問題
    6.2. 練習
基礎概念首先,對缺失值分類和缺失值處理方法進行講解。

缺失值的分類
按照數據缺失機制可分爲:
可忽略的缺失

完全隨機缺失(missing completely at random, MCAR),所缺失的數據發生的概率既與已觀察到的數據無關,也與未觀察到的數據無關
隨機缺失(missing at random, MAR),假設缺失數據發生的概率與所觀察到的變量是有關的,而與未觀察到的數據的特徵是無關的。
不可忽略的缺失(non-ignorable missing ,NIM) 或非隨機缺失(not missing at random, NMAR, or, missing not at random, MNAR),如果不完全變量中數據的缺失既依賴於完全變量又依賴於不完全變量本身,這種缺失即爲不可忽略的缺失。
【注意】:Panda讀取的數值型數據,缺失數據顯示“NaN”(not a number)。數據值的處理方法
主要就是兩種方法:
刪除存在缺失值的個案;
缺失值插補。
【注意】缺失值的插補只能用於客觀數據。由於主觀數據受人的影響,其所涉及的真實值不能保證。1、刪除含有缺失值的個案(2種方法)(1)簡單刪除法簡單刪除法是對缺失值進行處理的最原始方法。它將存在缺失值的個案刪除。如果數據缺失問題可以通過簡單的刪除小部分樣本來達到目標,那麼這個方法是最有效的。
(2)權重法當缺失值的類型爲非完全隨機缺失的時候,可以通過對完整的數據加權來減小偏差。把數據不完全的個案標記後,將完整的數據個案賦予不同的權重,個案的權重可以通過logistic或probit迴歸求得。如果解釋變量中存在對權重估計起決定行因素的變量,那麼這種方法可以有效減小偏差。如果解釋變量和權重並不相關,它並不能減小偏差。對於存在多個屬性缺失的情況,就需要對不同屬性的缺失組合賦不同的權重,這將大大增加計算的難度,降低預測的準確性,這時權重法並不理想。
2、可能值插補缺失值
【思想來源】:以最可能的值來插補缺失值比全部刪除不完全樣本所產生的信息丟失要少。
(1)均值插補屬於單值插補。數據的屬性分爲定距型和非定距型。如果缺失值是定距型的,就以該屬性存在值的平均值來插補缺失的值;如果缺失值是非定距型的,就用該屬性的衆數來補齊缺失的值。(2)利用同類均值插補
屬於單值插補。用層次聚類模型預測缺失變量的類型,再以該類型的均值插補。假設爲信息完全的變量,爲存在缺失值的變量,那麼首先對或其子集行聚類,然後按缺失個案所屬類來插補不同類的均值。如果在以後統計分析中還需以引入的解釋變量和做分析,那麼這種插補方法將在模型中引入自相關,給分析造成障礙。(3)極大似然估計(Max Likelihood ,ML)
在缺失類型爲隨機缺失的條件下,假設模型對於完整的樣本是正確的,那麼通過觀測數據的邊際分佈可以對未知參數進行極大似然估計(Little and Rubin)。這種方法也被稱爲忽略缺失值的極大似然估計,對於極大似然的參數估計實際中常採用的計算方法是期望值最大化(Expectation Maximization,EM)。該方法比刪除個案和單值插補更有吸引力,前提是適用於大樣本,有效樣本的數量足夠以保證ML估計值是漸近無偏的並服從正態分佈。這種方法可能會陷入局部極值,收斂速度也不是很快,並且計算很複雜。(4)多重插補(Multiple Imputation,MI)
多值插補的思想來源於貝葉斯估計,認爲待插補的值是隨機的,它的值來自於已觀測到的值。具體實踐上通常是估計出待插補的值,然後再加上不同的噪聲,形成多組可選插補值。根據某種選擇依據,選取最合適的插補值。多重插補方法的三個步驟:
爲每個空值產生一套可能的插補值,這些值反映了無響應模型的不確定性;每個值都可以被用來插補數據集中的缺失值,產生若干個完整數據集合。
每個插補數據集合都用針對完整數據集的統計方法進行統計分析。
對來自各個插補數據集的結果,根據評分函數進行選擇,產生最終的插補值。多重插補方法舉例:
假設一組數據,包括三個變量,它們的聯合分佈爲正態分佈,將這組數據處理成三組,A組保持原始數據,B組僅缺失,C組缺失和。在多值插補時,對A組將不進行任何處理,對B組產生的一組估計值(作關於的迴歸),對C組作產生和的一組成對估計值(作關於的迴歸)。當用多值插補時,對A組將不進行處理,對B、C組將完整的樣本隨機抽取形成爲組(爲可選擇的組插補值),每組個案數只要能夠有效估計參數就可以了。對存在缺失值的屬性的分佈作出估計,然後基於這組觀測值,對於這組樣本分別產生關於參數的組估計值,給出相應的預測即,這時採用的估計方法爲極大似然法,在計算機中具體的實現算法爲期望最大化法(EM)。對B組估計出一組的值,對C將利用 它們的聯合分佈爲正態分佈這一前提,估計出一組()。上例中假定了的聯合分佈爲正態分佈。這個假設是人爲的,但是已經通過驗證(Graham和Schafer於1999),非正態聯合分佈的變量,在這個假定下仍然可以估計到很接近真實值的結果。多重插補彌補貝葉斯估計的不足之處:
貝葉斯估計以極大似然的方法估計,極大似然的方法要求模型的形式必須準確,如果參數形式不正確,將得到錯誤得結論,即先驗分佈將影響後驗分佈的準確性。而多重插補所依據的是大樣本漸近完整的數據的理論,在數據挖掘中的數據量都很大,先驗分佈將極小的影響結果,所以先驗分佈的對結果的影響不大。
貝葉斯估計僅要求知道未知參數的先驗分佈,沒有利用與參數的關係。而多重插補對參數的聯合分佈作出了估計,利用了參數間的相互關係。
缺失觀測及其類型首先導入數據:

import pandas as pdimport numpy as npdf = pd.read_csv('data/table_missing.csv')df.head()
瞭解缺失信息1、isna和notna方法對Series使用會返回布爾列表df['Physics'].isna().head()
df['Physics'].notna().head()
對DataFrame使用會返回布爾表df.isna().head()
但對於DataFrame我們更關心到底每列有多少缺失值
df.isna().sum()
此外,可以通過第1章中介紹的info函數查看缺失信息
df.info()
2、查看缺失值的所以在行以最後一列爲例,挑出該列缺失值的行df[df['Physics'].isna()]
3、挑選出所有非缺失值列

使用all就是全部非缺失值,如果是any就是至少有一個不是缺失值df[df.notna().all(1)]
三種缺失符號

1、np.nannp.nan是一個麻煩的東西,首先它不等與任何東西,甚至不等於自己。np.nan == np.nanFalsenp.nan == 0Falsenp.nan == NoneFalse在用equals函數比較時,自動略過兩側全是np.nan的單元格,因此結果不會影響。df.equals(df)True其次,它在numpy中的類型爲浮點,由此導致數據集讀入時,即使原來是整數的列,只要有缺失值就會變爲浮點型。type(np.nan)floatpd.Series([1,2,3]).dtypedtype('int64')pd.Series([1,np.nan,3]).dtypedtype('float64')此外,對於布爾類型的列表,如果是np.nan填充,那麼它的值會自動變爲True而不是False。pd.Series([1,np.nan,3],dtype='bool')
但當修改一個布爾列表時,會改變列表類型,而不是賦值爲True。s = pd.Series([True,False],dtype='bool')s[1]=np.nans
在所有的表格讀取後,無論列是存放什麼類型的數據,默認的缺失值全爲np.nan類型。
因此整型列轉爲浮點;而字符由於無法轉化爲浮點,因此只能歸併爲object類型('O'),原來是浮點型的則類型不變df['ID'].dtypedtype('float64')df['Math'].dtypedtype('float64')df['Class'].dtypedtype('O')2、NoneNone比前者稍微好些,至少它會等於自身None == NoneTrue它的布爾值爲Falsepd.Series([None],dtype='bool')0    False
dtype: bool修改布爾列表不會改變數據類型s = pd.Series([True,False],dtype='bool')s[0]=Nones0    False
1    False
dtype: bools = pd.Series([1,0],dtype='bool')s[0]=Nones0    False
1    False
dtype: bool在傳入數值類型後,會自動變爲np.nantype(pd.Series([1,None])[1])numpy.float64只有當傳入object類型是保持不動,幾乎可以認爲,除非人工命名None,它基本不會自動出現在Pandas中type(pd.Series([1,None],dtype='O')[1])NoneType在使用equals函數時不會被略過,因此下面的情況下返回Falsepd.Series([None]).equals(pd.Series([np.nan]))False3、NaTNaT是針對時間序列的缺失值,是Pandas的內置類型,可以完全看做時序版本的np.nan,與自己不等,且使用equals是也會被跳過s_time = pd.Series([pd.Timestamp('20120101')]*5)s_time
s_time[2] = Nones_time
s_time[2] = np.nans_time
s_time[2] = pd.NaTs_time
type(s_time[2])pandas._libs.tslibs.nattype.NaTTypes_time[2] == s_time[2]Falses_time.equals(s_time)Trues = pd.Series([True,False],dtype='bool')s[1]=pd.NaTs
Nullable類型與NA符號
這是Pandas在1.0新版本中引入的重大改變,其目的就是爲了(在若干版本後)解決之前出現的混亂局面,統一缺失值處理方法。"The goal of pd.NA is provide a “missing” indicator that can be used consistently across data types (instead of np.nan, None or pd.NaT depending on the data type)."——User Guide for Pandas v-1.0官方鼓勵用戶使用新的數據類型和缺失類型pd.NA1、Nullable整形對於該種類型而言,它與原來標記int上的符號區別在於首字母大寫:'Int's_original = pd.Series([1, 2], dtype="int64")s_original
s_new = pd.Series([1, 2], dtype="Int64")s_new
它的好處就在於,其中前面提到的三種缺失值都會被替換爲統一的NA符號,且不改變數據類型。
s_original[1] = np.nans_original
s_new[1] = np.nans_new
s_new[1] = Nones_new
s_new[1] = pd.NaTs_new
2、Nullable布爾對於該種類型而言,作用與上面的類似,記號爲booleans_original = pd.Series([1, 0], dtype="bool")s_original
s_new = pd.Series([0, 1], dtype="boolean")s_new
s_original[0] = np.nans_original
s_original = pd.Series([1, 0], dtype="bool") #此處重新加一句是因爲前面賦值改變了bool類型s_original[0] = Nones_original
s_new[0] = np.nans_new
s_new[0] = Nones_new
s_new[0] = pd.NaTs_new
需要注意的是,含有pd.NA的布爾列表在1.0.2之前的版本作爲索引時會報錯,這是一個之前的bug,現已經修復。s = pd.Series(['dog','cat'])s[s_new]
3、string類型

該類型是1.0的一大創新,目的之一就是爲了區分開原本含糊不清的object類型,這裏將簡要地提及string,因爲它是第7章的主題內容。它本質上也屬於Nullable類型,因爲並不會因爲含有缺失而改變類型。s = pd.Series(['dog','cat'],dtype='string')s
s[0] = np.nans
s[0] = Nones
s[0] = pd.NaTs
此外,和object類型的一點重要區別就在於,在調用字符方法後,string類型返回的是Nullable類型,object則會根據缺失類型和數據類型而改變。
s = pd.Series(["a", None, "b"], dtype="string")s.str.count('a')
s2 = pd.Series(["a", None, "b"], dtype="object")s2.str.count("a")
s.str.isdigit()s2.str.isdigit()
NA的特性
1、邏輯運算只需看該邏輯運算的結果是否依賴pd.NA的取值,如果依賴,則結果還是NA,如果不依賴,則直接計算結果。True | pd.NATruepd.NA | TrueTrueFalse | pd.NA<NA>False & pd.NAFalseTrue & pd.NA<NA>取值不明直接報錯#bool(pd.NA)2、算術運算和比較運算這裏只需記住除了下面兩類情況,其他結果都是NA即可pd.NA ** 011 ** pd.NA1其他情況:pd.NA + 1<NA>"a" * pd.NA<NA>pd.NA == pd.NA<NA>pd.NA < 2.5<NA>np.log(pd.NA)<NA>np.add(pd.NA, 1)<NA>convert_dtypes方法這個函數的功能往往就是在讀取數據時,就把數據列轉爲Nullable類型,是1.0的新函數。pd.read_csv('data/table_missing.csv').dtypes
pd.read_csv('data/table_missing.csv').convert_dtypes().dtypes
缺失數據的運算與分組加號與乘號規則使用加法時,缺失值爲0s = pd.Series([2,3,np.nan,4])s.sum()9.0

使用乘法時,缺失值爲1s.prod()24.0使用累計函數時,缺失值自動略過s.cumsum()
s.cumprod()
s.pct_change()
groupby方法中的缺失值
自動忽略爲缺失值的組df_g = pd.DataFrame({'one':['A','B','C','D',np.nan],'two':np.random.randn(5)})df_g
df_g.groupby('one').groups
填充與剔除
fillna方法1、值填充與前後向填充(分別與ffill方法和bfill方法等價)df['Physics'].fillna('missing').head()
df['Physics'].fillna(method='ffill').head()
df['Physics'].fillna(method='backfill').head()
2、填充中的對齊特性
df_f = pd.DataFrame({'A':[1,3,np.nan],'B':[2,4,np.nan],'C':[3,5,np.nan]})df_f.fillna(df_f.mean())
返回的結果中沒有C,根據對齊特點不會被填充
df_f.fillna(df_f.mean()[['A','B']])
dropna方法
1、axis參數df_d = pd.DataFrame({'A':[np.nan,np.nan,np.nan],'B':[np.nan,3,2],'C':[3,2,1]})df_d
df_d.dropna(axis=0)
df_d.dropna(axis=1)
2、how參數(可以選all或者any,表示全爲缺失去除和存在缺失去除)df_d.dropna(axis=1,how='all')
3、subset參數(即在某一組列範圍中搜索缺失值)¶df_d.dropna(axis=0,subset=['B','C'])
插值
線性插值1、索引無關的線性插值默認狀態下,interpolate會對缺失的值進行線性插值s = pd.Series([1,10,15,-5,-2,np.nan,np.nan,28])s
s.interpolate()
s.interpolate().plot()<matplotlib.axes._subplots.AxesSubplot at 0x7fe7df20af50>
此時的插值與索引無關s.index = np.sort(np.random.randint(50,300,8))s.interpolate()#值不變
s.interpolate().plot()#後面三個點不是線性的(如果幾乎爲線性函數,請重新運行上面的一個代碼塊,這是隨機性導致的)<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dfc69890>
2、與索引有關的插值method中的index和time選項可以使插值線性地依賴索引,即插值爲索引的線性函數s.interpolate(method='index').plot()#可以看到與上面的區別<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dca0c4d0>
如果索引是時間,那麼可以按照時間長短插值,對於時間序列將在第9章詳細介紹s_t = pd.Series([0,np.nan,10]        ,index=[pd.Timestamp('2012-05-01'),pd.Timestamp('2012-05-07'),pd.Timestamp('2012-06-03')])s_t
s_t.interpolate().plot()<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dc964850>
s_t.interpolate(method='time').plot()<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dc8eda10>
高級插值方法此處的高級指的是與線性插值相比較,例如樣條插值、多項式插值、阿基瑪插值等(需要安裝Scipy)。關於這部分僅給出一個官方的例子,因爲插值方法是數值分析的內容,而不是Pandas中的基本知識:ser = pd.Series(np.arange(1, 10.1, .25) ** 2 + np.random.randn(37))missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])ser[missing] = np.nanmethods = ['linear', 'quadratic', 'cubic']df = pd.DataFrame({m: ser.interpolate(method=m) for m in methods})df.plot()<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dc86f810>
interpolate中的限制參數1、limit表示最多插入多少個s = pd.Series([1,np.nan,np.nan,np.nan,5])s.interpolate(limit=2)
2、limit_direction表示插值方向,可選forward,backward,both,默認前向。s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])s.interpolate(limit_direction='backward')
3、limit_area表示插值區域,可選inside,outside,默認Nones = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])s.interpolate(limit_area='inside')
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])s.interpolate(limit_area='outside')
問題與練習問題
【問題一】 如何刪除缺失值佔比超過25%的列?第一步,計算單列缺失值的數量,計算單列總樣本數
第二步,算出比例,得到一個列的布爾列表
第三步,利用這個布爾列表進行列索引或列刪除
df.loc[:,(df.isna().sum()/df.isna().count()<0.25).values]【問題二】 什麼是Nullable類型?請談談爲什麼要引入這個設計?
Nullable類型是一種爲了統一NaN,Null,NaT三類缺失值而誕生的新的類型。是在原來的數值、布爾、字符等類型的基礎上進行小改,優化了當出現缺失值情況時的應對。引入這個設計時爲了更好的處理缺失值,統一缺失值處理方法
【問題三】 對於一份有缺失值的數據,可以採取哪些策略或方法深化對它的瞭解?可以查看缺失值出現的比例;
查看缺失值之間的關聯性;
查看總體的缺失信息;
根據缺失信息判斷是否爲有效數據;
根據缺失信息清洗數據等等。
練習

【練習一】現有一份虛擬數據集,列類型分別爲string/浮點/整型,請解決如下問題。
q1 = pd.read_csv('data/Missing_data_one.csv')q1.head()      A       B     C0  not_NaN  0.922  4.01  not_NaN  0.700  NaN2  not_NaN  0.503  8.03  not_NaN  0.938  4.04  not_NaN  0.952  10.01.1 請以列類型讀入數據,並選出C爲缺失值的行。
q1[q1['C'].isna()]1.2 現需要將A中的部分單元轉爲缺失值,單元格中的最小轉換概率爲25%,且概率大小與所在行B列單元的值成正比
q1['A'] = pd.Series(list(zip(q1['A'].values,q1['B'].values))).apply(lambda x:x[0] if np.random.rand()>0.25*x[1]/q1['B'].min() else np.nan)【練習二】 現有一份缺失的數據集,記錄了36個人來自的地區、身高、體重、年齡和工資,解決如下問題:pd.read_csv('data/Missing_data_two.csv').head()  編號 地區 身高    體重   年齡   工資0  1   A   157.50  NaN    47.0  15905.01  2   B   202.00  91.80  25.0  NaN2  3   C   169.09  62.18  NaN   NaN3  4   A   166.61  59.95  77.0  5434.04  5   B   185.19  NaN    62.0  4242.02.1 統計各列缺失的比例並選出在後三列中至少有兩個非缺失值的行
q2.isna().sum()/q2.shape[0]q2[q2.iloc[:,-3:].isna().sum(1)<=1].head()2.2 請結合身高列和地區列中的數據,對體重進行合理插值
q2_new = q2.copy()for area,group in q2.groupby('地區'):    q2_new.loc[group.index,'體重'] = group[['身高','體重']].sort_values(by='身高').interpolate()['體重'] q2_new = q2_new.round(decimals=2)q2_new.head()本文電子版 後臺回覆 缺失數據 獲取


“感謝你的在看,點贊,分享三連↓

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章