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

Pandas 是一個強大的分析結構化數據的工具集,它的使用基礎是Numpy(提供高性能的矩陣運算),用於數據挖掘和數據分析,同時也提供數據清洗功能。git

在往期文章中,已經詳細講解了Pandas作分析數據的四種基本操做:索引分組變形合併。如今,開始正式介紹Pandas的數據結構類型:缺失數據、文本數據、分類數據和時序數據。算法

在接下來的兩章中,會接觸到數據預處理中比較麻煩的類型,即缺失數據和文本數據(尤爲是混雜型文本)。今天,咱們首先對缺失數據進行系統地梳理。

圖片

 本文目錄
數據結構

              1. 基礎概念app

                  1.1. 缺失值分類dom

                  1.2. 缺失值處理方法ide

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)。

數據值的處理方法

主要就是兩種方法:

  • 刪除存在缺失值的個案;

  • 缺失值插補。

【注意】缺失值的插補只能用於客觀數據。因爲主觀數據受人的影響,其所涉及的真實值不能保證。 一、刪除含有缺失值的個案(2種方法) (1)簡單刪除法

簡單刪除法是對缺失值進行處理的最原始方法。它將存在缺失值的個案刪除。若是數據缺失問題能夠經過簡單的刪除小部分樣原本達到目標,那麼這個方法是最有效的。

(2)權重法 當缺失值的類型爲非徹底隨機缺失的時候,能夠經過對完整的數據加權來減少誤差。 把數據不徹底的個案標記後,將完整的數據個案賦予不一樣的權重,個案的權重能夠經過logistic或probit迴歸求得。 若是解釋變量中存在對權重估計起決定行因素的變量,那麼這種方法能夠有效減少誤差。 若是解釋變量和權重並不相關,它並不能減少誤差。

對於存在多個屬性缺失的狀況,就須要對不一樣屬性的缺失組合賦不一樣的權重,這將大大增長計算的難度,下降預測的準確性,這時權重法並不理想。

二、可能值插補缺失值

【思想來源】:以最可能的值來插補缺失值比所有刪除不徹底樣本所產生的信息丟失要少。

(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()

圖片

瞭解缺失信息

一、isna和notna方法

對Series使用會返回布爾列表


df['Physics'].isna().head()

圖片


df['Physics'].notna().head()

圖片

對DataFrame使用會返回布爾表


df.isna().head()

圖片

但對於DataFrame咱們更關心到底每列有多少缺失值

df.isna().sum()

此外,能夠經過第1章中介紹的info函數查看缺失信息


df.info()

二、查看缺失值的因此在行

以最後一列爲例,挑出該列缺失值的行

 
 

df[df['Physics'].isna()]

三、挑選出全部非缺失值列

使用all就是所有非缺失值,若是是any就是至少有一個不是缺失值


df[df.notna().all(1)]

三種缺失符號

一、np.nan

np.nan是一個麻煩的東西,首先它不等與任何東西,甚至不等於本身。


np.nan == np.nan
False

np.nan == 0
False

np.nan == None
False

在用equals函數比較時,自動略過兩側全是np.nan的單元格,所以結果不會影響。


df.equals(df)
True

其次,它在numpy中的類型爲浮點,由此致使數據集讀入時,即便原來是整數的列,只要有缺失值就會變爲浮點型。


type(np.nan)
float

pd.Series([1,2,3]).dtype
dtype('int64')

pd.Series([1,np.nan,3]).dtype
dtype('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'].dtype
dtype('float64')

df['Math'].dtype
dtype('float64')

df['Class'].dtype
dtype('O')

二、None

None比前者稍微好些,至少它會等於自身


None == None
True

它的布爾值爲False


pd.Series([None],dtype='bool')
0    False
dtype: bool

修改布爾列表不會改變數據類型




s = pd.Series([True,False],dtype='bool')s[0]=Nones
0    False
1    False
dtype: bool



s = pd.Series([1,0],dtype='bool')s[0]=Nones
0    False
1    False
dtype: bool

在傳入數值類型後,會自動變爲np.nan


type(pd.Series([1,None])[1])
numpy.float64

只有當傳入object類型是保持不動,幾乎能夠認爲,除非人工命名None,它基本不會自動出如今Pandas中


type(pd.Series([1,None],dtype='O')[1])
NoneType

在使用equals函數時不會被略過,所以下面的狀況下返回False


pd.Series([None]).equals(pd.Series([np.nan]))
False

三、NaT

NaT是針對時間序列的缺失值,是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.NaTType

s_time[2] == s_time[2]
False

s_time.equals(s_time)
True



s = 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.NA

一、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

圖片

二、Nullable布爾

對於該種類型而言,做用與上面的相似,記號爲boolean



s_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]

 
 

三、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的特性

一、邏輯運算

只需看該邏輯運算的結果是否依賴pd.NA的取值,若是依賴,則結果仍是NA,若是不依賴,則直接計算結果。


True | pd.NA
True

pd.NA | True
True

False | pd.NA
<NA>

False & pd.NA
False

True & pd.NA
<NA>

取值不明直接報錯


#bool(pd.NA)

二、算術運算和比較運算

這裏只需記住除了下面兩類狀況,其餘結果都是NA便可


pd.NA ** 0
1

1 ** pd.NA
1

其餘狀況:


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

缺失數據的運算與分組

加號與乘號規則

使用加法時,缺失值爲0



s = pd.Series([2,3,np.nan,4])s.sum()

9.0

使用乘法時,缺失值爲1


s.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方法

一、值填充與先後向填充(分別與ffill方法和bfill方法等價)


df['Physics'].fillna('missing').head()


df['Physics'].fillna(method='ffill').head()


df['Physics'].fillna(method='backfill').head()

二、填充中的對齊特性



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方法

一、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)

二、how參數 (能夠選all或者any,表示全爲缺失去除和存在缺失去除)

df_d.dropna(axis=1,how='all')

三、subset參數 (即在某一組列範圍中搜索缺失值)¶

df_d.dropna(axis=0,subset=['B','C'])

插值

線性插值

一、索引無關的線性插值

默認狀態下,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>

二、與索引有關的插值

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中的限制參數

一、limit表示最多插入多少個



s = pd.Series([1,np.nan,np.nan,np.nan,5])s.interpolate(limit=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')

圖片

三、limit_area表示插值區域,可選inside,outside,默認None



s = 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.0

1.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.0

2.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()
相關文章
相關標籤/搜索