簡単な欠損値の確かめ方

とりあえず各列に欠損値があるかどうかを知りたい、というときは isnull 関数と any 関数の組み合わせと notnull 関数と all 関数の組み合わせがあります。

前者の組み合わせのときは欠損値のある列に True が返され、後者の組み合わせのときは欠損値のある列に False が返されます。

以下のように確かめることができます。

In [1]: import pandas as pd
In [2]: import numpy as np
In [3]: data = np.random.randn(5,5)
In [4]: data[1,3] = np.nan # 欠損値を入れる
In [5]: data[2,0] = np.nan # 欠損値2つ目
In [6]: data
Out[6]:
array([[ 2.10851627,  1.07282648, -0.97971334,  0.02266813, -0.38525149],
       [-0.48737955,  0.11157585, -0.76305474,         nan, -0.07192401],
       [        nan, -0.97189693, -0.31556478,  0.33463695,  0.70125536],
       [-0.07861912, -0.19900086, -0.01112946,  0.56708296,  0.46685101],
       [-0.63178651,  1.65860916, -0.36124422, -0.00691114, -0.73076744]])
In [7]: df = pd.DataFrame(data, columns=['A','B','C','D','E']) # DataFrameの作成
   ...:
In [8]: df
Out[8]:
          A         B         C         D         E
0  2.108516  1.072826 -0.979713  0.022668 -0.385251
1 -0.487380  0.111576 -0.763055       NaN -0.071924
2       NaN -0.971897 -0.315565  0.334637  0.701255
3 -0.078619 -0.199001 -0.011129  0.567083  0.466851
4 -0.631787  1.658609 -0.361244 -0.006911 -0.730767
In [9]: df.isnull().any()
Out[9]:
A     True
B    False
C    False
D     True
E    False
dtype: bool
In [10]: df.notnull().all()
Out[10]:
A    False
B     True
C     True
D    False
E     True
dtype: bool
欠損値を削除する方法

では、欠損値の削除の仕方を確認してみましょう。

欠損値を削除するにはdropna関数を使います。読んで字のごとく、NaN値(欠損値)をドロップ(除外)します。

1つ注意ですが、inplace=Trueにしないと、元のデータに変更は反映されないので注意しましょう。

以下のデータを使います。

In [11]: df = pd.Series([1, 2, 3, np.nan, 0, None], index=['A','B','C','D','E','F']) # 欠損値を含むデータを作成
In [12]: df
Out[12]:
A    1.0
B    2.0
C    3.0
D    NaN
E    0.0
F    NaN
dtype: float64
In [14]: df_2 = pd.DataFrame({'A':[0, 1, np.nan, 2],'B':[np.nan,2, 3, 4]})
In [15]: df_2
Out[15]:
0  0.0  NaN
1  1.0  2.0
2  NaN  3.0
3  2.0  4.0
In [16]: df_2.dropna() # 0,2行目が削除される
Out[16]:
1  1.0  2.0
3  2.0  4.0
In [17]: df = pd.DataFrame({'A': [1, np.nan, 2, np.nan, 2, np.nan],
    ...:                    'B': [np.nan, np.nan, 3, 4, 5, 6]})
    ...:                    
In [18]: df
Out[18]:
0  1.0  NaN
1  NaN  NaN
2  2.0  3.0
3  NaN  4.0
4  2.0  5.0
5  NaN  6.0
In [19]: df.dropna(how='all') # 両方とも欠損値の1行目だけが消去される
Out[19]:
0  1.0  NaN
2  2.0  3.0
3  NaN  4.0
4  2.0  5.0
5  NaN  6.0

threshで行あたりにいくつ以上のデータが非欠損値であれば削除しないかを指定できます。

例えば、2個以上非欠損値があればその行を残したいときはthresh=2のように設定します。

In [28]: df = pd.DataFrame({'A':[0, np.nan, np.nan, 2, 3, 4],
    ...:                    'B':[np.nan, np.nan, 2, 3, 5, 6],
    ...:                    'C':[1, np.nan, np.nan, 3,5,np.nan]})
    ...:                    
In [29]: df
Out[29]:
     A    B    C
0  0.0  NaN  1.0
1  NaN  NaN  NaN
2  NaN  2.0  NaN
3  2.0  3.0  3.0
4  3.0  5.0  5.0
5  4.0  6.0  NaN
In [30]: df.dropna(thresh=2) # 欠損値でないところが2つ以上残っていれば削除しない
Out[30]:
     A    B    C
0  0.0  NaN  1.0
3  2.0  3.0  3.0
4  3.0  5.0  5.0
5  4.0  6.0  NaN
A  0.0 NaN  NaN  2.0  3.0  4.0
B  NaN NaN  2.0  3.0  5.0  6.0
C  1.0 NaN  NaN  3.0  5.0  NaN
In [35]: df_t.dropna(axis='columns')
Out[35]:
A  2.0  3.0
B  3.0  5.0
C  3.0  5.0
5  4.0  6.0  NaN
In [41]: df.fillna({'A':-10, 'B': 0, 'C': 999}) # 列ごとに穴埋めする値を変える
Out[41]:
      A    B      C
0   0.0  0.0    1.0
1 -10.0  0.0  999.0
2 -10.0  2.0  999.0
3   2.0  3.0    3.0
4   3.0  5.0    5.0
5   4.0  6.0  999.0

欠損値の前後を使って穴埋めをすることができます。method='ffill'で直前の値が穴埋めの値に使われます。

ffillはforward fillの略称なので前方方向に向けて穴埋めするというイメージを持つとわかりやすいと思います。 一方、method='bfill'はbackward fillの略称となり後方方向に向けて穴埋めするということになります。

In [42]: df_method = df.copy()
In [43]: df_method.iloc[3:,1] = np.nan
In [44]: df_method
Out[44]:
     A    B    C
0  0.0  NaN  1.0
1  NaN  NaN  NaN
2  NaN  2.0  NaN
3  2.0  NaN  3.0
4  3.0  NaN  5.0
5  4.0  NaN  NaN
In [45]: df_method.fillna(method='ffill') # 直前の値を使って埋めていく
Out[45]:
     A    B    C
0  0.0  NaN  1.0
1  0.0  NaN  1.0
2  0.0  2.0  1.0
3  2.0  2.0  3.0
4  3.0  2.0  5.0
5  4.0  2.0  5.0
In [46]: df_method.fillna(method='bfill') # 直後の値を使って穴埋めをする
Out[46]:
     A    B    C
0  0.0  2.0  1.0
1  2.0  2.0  3.0
2  2.0  2.0  3.0
3  2.0  NaN  3.0
4  3.0  NaN  5.0
5  4.0  NaN  NaN

limit引数で値を指定することで連続した欠損値で何個まで穴埋めするかを指定できます。limit引数はmethodが指定されているときのみ有効となります。

ではmethod='ffill'limitを変更して見ます。

In [49]: df_method.fillna(method='ffill', limit=2) # 2個連続まで
Out[49]:
     A    B    C
0  0.0  NaN  1.0
1  0.0  NaN  1.0
2  0.0  2.0  1.0
3  2.0  2.0  3.0
4  3.0  2.0  5.0
5  4.0  NaN  5.0
In [50]: df_method.fillna(method='ffill', limit=1) # 1個連続まで
Out[50]:
     A    B    C
0  0.0  NaN  1.0
1  0.0  NaN  1.0
2  NaN  2.0  NaN
3  2.0  2.0  3.0
4  3.0  NaN  5.0
5  4.0  NaN  5.0
In [70]: fill_df = pd.DataFrame(np.arange(18).reshape(6,3),
    ...:                        columns=['A','B','C'])
    ...:                        
In [71]: df.fillna(fill_df)
Out[71]:
     A    B     C
0  0.0  1.0   1.0
1  3.0  4.0   5.0
2  6.0  2.0   8.0
3  2.0  3.0   3.0
4  3.0  5.0   5.0
5  4.0  6.0  17.0

今回は欠損値を削除する方法と穴埋めする方法について解説しました。

欠損値を削除する場合はdropna、穴埋めする場合はfillna関数を使えば大抵の欠損値処理を行うことができます。

欠損値処理を確実にこなしておくと、次のデータ処理の段階でエラーが起きにくくなるのでしっかり把握しておくと良いでしょう。

  • Python for Data Analysis 2nd edition –Wes McKinney(書籍)
  • pandas.DataFrame.dropna - pandas 0.23.4 documentation
  • pandas.DataFrame.fillna - pandas 0.23.4 documentation
  •