手把手教你用pandas處理缺失值

語言: CN / TW / HK

作者:韋斯·麥金尼(Wes McKinney)

譯者:徐敬一

來源:大數據DT(ID:hzdashuju)

 

導讀:在進行數據分析和建模的過程中,大量的時間花在數據準備上:加載、清理、轉換和重新排列。本文將討論用於缺失值處理的工具。缺失數據會在很多數據分析應用中出現。pandas的目標之一就是儘可能無痛地處理缺失值。

pandas對象的所有描述性統計信息默認情況下是排除缺失值的。   pandas對象中表現缺失值的方式並不完美,但是它對大部分用户來説是有用的。對於數值型數據,pandas使用浮點值NaN(Not a Number來表示缺失值)。我們稱NaN為容易檢測到的標識值:

  In :   

string_data = pd.Series(['aardvark''artichoke', np.nan, 'avocado'])
string_data
  Out:  
0      aardvark
1     artichoke
2            NaN
3       avocado
dtype: object
    In:  
string_data.isnull()
  Out:  
0     False
1     False
2      True
3     False
dtype: bool

在pandas中,我們採用了R語言中的編程慣例,將缺失值成為NA,意思是not available(不可用)。在統計學應用中,NA數據可以是不存在的數據或者是存在但不可觀察的數據(例如在數據收集過程中出現了問題)。當清洗數據用於分析時,對缺失數據本身進行分析以確定數據收集問題或數據丟失導致的數據偏差通常很重要。   Python內建的None值在對象數組中也被當作NA處理:  

In:    

string_data[0] = None
string_data.isnull()
  Out:  
0      True
1     False
2      True
3     False
dtype: bool
  pandas項目持續改善處理缺失值的內部細節,但是用户API函數,比如pandas. isnull,抽象掉了很多令人厭煩的細節。處理缺失值的相關函數列表如下:  
  • dropna:根據每個標籤的值是否是缺失數據來篩選軸標籤,並根據允許丟失的數據量來確定閾值
  • fillna:用某些值填充缺失的數據或使用插值方法(如“ffill”或“bfill”)。
  • isnull:返回表明哪些值是缺失值的布爾值
  • notnull:isnull的反作用函數

    01 過濾缺失值  

有多種過濾缺失值的方法。雖然你可以使用pandas.isnull和布爾值索引手動地過濾缺失值,但dropna在過濾缺失值時是非常有用的。在Series上使用dropna,它會返回Series中所有的非空數據及其索引值:  

In:  

from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
data.dropna()
  Out:  
0     1.0
2     3.5
4     7.0
dtype: float64

  上面的例子與下面的代碼是等價的:

  In:  

data[data.notnull()]
  Out:  
0     1.0
2     3.5
4     7.0
dtype: float64

  當處理DataFrame對象時,事情會稍微更復雜一點。你可能想要刪除全部為NA或包含有NA的行或列。dropna默認情況下會刪除包含缺失值的行:  

In:  

data = pd.DataFrame([[1.6.53.], [1., NA, NA]
                     [NA, NA, NA], [NA, 6.53.]])
cleaned = data.dropna()
 
data
  Out:  
   0     1     2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6.5  3.0
    In:  
cleaned
  Out:  
   0     1     2
0  1.0  6.5  3.0

  傳入how='all’時,將刪除所有值均為NA的行:  

In:  

data.dropna(how='all')
  Out:  
     0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
3  NaN  6.5  3.0

  如果要用同樣的方式去刪除列,傳入參數axis=1:  

In:  

data[4] = NA
data
  Out:  
     0    1    2   4
0  1.0  6.5  3.0 NaN
1  1.0  NaN  NaN NaN
2  NaN  NaN  NaN NaN
3  NaN  6.5  3.0 NaN
  In:  
data.dropna(axis=1, how='all')
  Out:  
     0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6.5  3.0

  過濾DataFrame的行的相關方法往往涉及時間序列數據。假設你只想保留包含一定數量的觀察值的行。你可以用thresh參數來表示:

  In:  

df = pd.DataFrame(np.random.randn(73))
df.iloc[:41] = NA
df.iloc[:22] = NA
df
  Out:  
          0         1         2
0 -0.204708       NaN       NaN
1 -0.555730       NaN       NaN
2  0.092908       NaN  0.769023
3  1.246435       NaN -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741
  In:  
df.dropna()
  Out:  
         0         1         2
4 0.274992  0.228913  1.352917
5 0.886429 -2.001637 -0.371843
6 1.669025 -0.438570 -0.539741
  In:  
df.dropna(thresh=2)
  Out:  
         0         1         2
2 0.092908       NaN  0.769023
3 1.246435       NaN -1.296221
4 0.274992  0.228913  1.352917
5 0.886429 -2.001637 -0.371843
6 1.669025 -0.438570 -0.539741

    02 補全缺失值  

你有時可能需要以多種方式補全“漏洞”,而不是過濾缺失值(也可能丟棄其他數據)。   大多數情況下,主要使用fillna方法來補全缺失值。調用fillna時,可以使用一個常數來替代缺失值:  

In:  

df.fillna(0)
  Out:  
          0         1         2
0 -0.204708  0.000000  0.000000
1 -0.555730  0.000000  0.000000
2  0.092908  0.000000  0.769023
3  1.246435  0.000000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

  在調用fillna時使用字典,你可以為不同列設定不同的填充值:

  In:  

df.fillna({10.520})
  Out:  
          0         1         2
0 -0.204708  0.500000  0.000000
1 -0.555730  0.500000  0.000000
2  0.092908  0.500000  0.769023
3  1.246435  0.500000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

  fillna返回的是一個新的對象,但你也可以修改已經存在的對象:

  In:  

_ = df.fillna(0, inplace=True)
df
  Out:  
          0         1         2
0 -0.204708  0.000000  0.000000
1 -0.555730  0.000000  0.000000
2  0.092908  0.000000  0.769023
3  1.246435  0.000000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

  用於重建索引的相同的插值方法也可以用於fillna:

  In:  

df = pd.DataFrame(np.random.randn(63))
df.iloc[2:, 1] = NA
df.iloc[4:, 2] = NA
df
  Out:  
          0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772       NaN  1.343810
3 -0.713544       NaN -2.370232
4 -1.860761       NaN       NaN
5 -1.265934       NaN       NaN
  In:  
df.fillna(method='ffill')
  Out:  
          0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761  0.124121 -2.370232
5 -1.265934  0.124121 -2.370232
  In:  
df.fillna(method='ffill', limit=2)
  Out:  
          0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761       NaN -2.370232
5 -1.265934       NaN -2.370232

  使用fillna你可以完成很多帶有一點創造性的工作。例如,你可以將Series的平均值或中位數用於填充缺失值:

  In:  

data = pd.Series([1., NA, 3.5, NA, 7])
data.fillna(data.mean())
  Out:  
0     1.000000
1     3.833333
2     3.500000
3     3.833333
4     7.000000
dtype: float64
  以下是fillna的函數參數。  
  • value:標量值或字典型對象用於填充缺失值
  • method:插值方法,如果沒有其他參數,默認是'ffill'
  • axis:需要填充的軸,默認axis=0
  • inplace:修改被調用的對象,而不是生成一個備份
  • limit:用於前向或後向填充時最大的填充範圍
 

關於作者:韋斯·麥金尼(Wes McKinney)是流行的Python開源數據分析庫pandas的創始人。他是一名活躍的演講者,也是Python數據社區和Apache軟件基金會的Python/C++開源開發者。目前他在紐約從事軟件架構師工作。

 

本文摘編自利用Python進行數據分析》(原書第2版),經出版方授權發佈。