実際のデータで分析を行うとデータが不完全で欠損値が含まれていることがあります。
欠損値の扱い方が変わるだけで分析の結果が変化する場合もあります。
そこで本記事では欠損値の処理をすることができるように
- 簡単な欠損値の確かめ方
- 欠損値を削除する方法
- 欠損値を穴埋めする方法
の3つについて解説していきます。
簡単な欠損値の確かめ方
とりあえず各列に欠損値があるかどうかを知りたい、というときは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
基本的な使い方
では先ほど作成したSeriesにdropna
関数を適用させます。すると、NaN値を含むところが切り落とされる結果になります。
In [13]: df.dropna()
Out[13]:
A 1.0
B 2.0
C 3.0
E 0.0
dtype: float64
DataFrame
だとデフォルトの設定でdropna
を適用すると欠損値を1つでも含む行は削除されます。
In [14]: df_2 = pd.DataFrame({'A':[0, 1, np.nan, 2],'B':[np.nan,2, 3, 4]})
In [15]: df_2
Out[15]:
A B
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]:
A B
1 1.0 2.0
3 2.0 4.0
全てが欠損値の行を削除する
how='all'
にすると全ての値が欠損値になっている行のみを削除します。
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]:
A B
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]:
A B
0 1.0 NaN
2 2.0 3.0
3 NaN 4.0
4 2.0 5.0
5 NaN 6.0
削除したい列を指定する
subset=[カラム名のリスト]
とすることで欠損値を削除したい列を指定することができます。
In [22]: df
Out[22]:
A B
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 [23]: df.dropna(subset=['A']) # A列に含まれる欠損値だけを削除する
Out[23]:
A B
0 1.0 NaN
2 2.0 3.0
4 2.0 5.0
変更を元のデータに反映させる
inplace=True
にすれば変更を元のデータに反映させることが可能です。このとき、返り値はありません。
In [24]: df
Out[24]:
A B
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 [25]: df_copy = df.copy()
In [26]: df_copy.dropna(inplace=True)
In [27]: df_copy
Out[27]:
A B
2 2.0 3.0
4 2.0 5.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
削除する方向を指定
axis
で欠損値を含む行を削除するか、列を削除するかを指定できます。axis=1
もしくはaxis='columns'
で列を削除できます。
In [33]: df_t = df.T
In [34]: df_t
Out[34]:
0 1 2 3 4 5
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]:
3 4
A 2.0 3.0
B 3.0 5.0
C 3.0 5.0
欠損値を穴埋めする方法
欠損値を穴埋めするにはfillna
関数を使います。
dropna
関数と同様、元のデータに変更を反映させたいときはinplace=True
を使います。
基本的な使い方
最初の引数で穴埋めする値を指定して使います。0
で埋めたかったらdropna(0)
と指定します。
In [37]: df
Out[37]:
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 [38]: df.fillna(0)
Out[38]:
A B C
0 0.0 0.0 1.0
1 0.0 0.0 0.0
2 0.0 2.0 0.0
3 2.0 3.0 3.0
4 3.0 5.0 5.0
5 4.0 6.0 0.0
変則的ですが、文字列で埋めることも可能です。
In [39]: df.fillna('missing')
Out[39]:
A B C
0 0 missing 1
1 missing missing missing
2 missing 2 missing
3 2 3 3
4 3 5 5
5 4 6 missing
列ごとに埋める値を変える
列ごとに穴埋めする値を変更するときは、辞書形式で指定します。
In [40]: df
Out[40]:
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 [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 [51]: df
Out[51]:
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 [52]: df.fillna(df.mean()) # 平均値で穴埋め
Out[52]:
A B C
0 0.00 4.0 1.0
1 2.25 4.0 3.0
2 2.25 2.0 3.0
3 2.00 3.0 3.0
4 3.00 5.0 5.0
5 4.00 6.0 3.0
In [53]: df.fillna(df.median()) # 中央値
Out[53]:
A B C
0 0.0 4.0 1.0
1 2.5 4.0 3.0
2 2.5 2.0 3.0
3 2.0 3.0 3.0
4 3.0 5.0 5.0
5 4.0 6.0 3.0
In [54]: df.fillna(df.mode()) # 最頻値
Out[54]:
A B C
0 0.0 2.0 1.0
1 2.0 3.0 3.0
2 3.0 2.0 5.0
3 2.0 3.0 3.0
4 3.0 5.0 5.0
5 4.0 6.0 3.0
個別に穴埋めする値を指定する
DataFrameを指定することで個別に穴埋めする値を指定することができます。
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