【Python】pandasで欠損値を上手に扱う

python-logo Python

pandasを使って読み込んだデータを処理する時に、
必ずと言っても良いほど向き合わなければいけないのが欠損値(NaN)の処理。

今回は備忘録としてよく利用する機能を簡易的にまとめてみた。

今回扱うデータ

今回読み込むデータはこちら。

idnamecolorpricecountry
1applered200
2strawberryred300
3bananayellow214
4merongreen3000
5orange1280
6grapepurple
$ cat sample.csv
id,name,color,price,country
1,apple,red,200,
2,strawberry,red,300,
3,banana,yellow,214,
4,meron,green,3000,
5,orange,,1280,
6,grape,purple,,

欠損値がいくつか入っている状態のcsvファイルを用意した。

準備

まずはデータの読み込み。

>>> import pandas as pd
>>> df = pd.read_csv('sample.csv')
>>> df
   id        name   color   price  country
0   1       apple     red   200.0      NaN
1   2  strawberry     red   300.0      NaN
2   3      banana  yellow   214.0      NaN
3   4       meron   green  3000.0      NaN
4   5      orange     NaN  1280.0      NaN
5   6       grape  purple     NaN      NaN
>>>

欠損値の状態確認

実務では上記のような少ないデータ数を扱うことはない。
全部目視で確認するのが大変なほど大量なデータを扱うはずである。

そのため、データの中身を確認し、どこにどれくらい欠損値が入っているのかを確認する必要がある。

欠損値をTrue/Falseで確認する

欠損値をTrue/False表記で確認したい場合、isnull()メソッドを利用する。

>>> df.isnull()
      id   name  color  price  country
0  False  False  False  False     True
1  False  False  False  False     True
2  False  False  False  False     True
3  False  False  False  False     True
4  False  False   True  False     True
5  False  False  False   True     True
>>>
  • 欠損値:True
  • 欠損値ではない:False

で表現されていることが分かる。

欠損値の数を数える

上記のisnull()メソッドを利用すると、集計も可能である。 

>>> df.isnull().sum()
id         0
name       0
color      1
price      1
country    6
dtype: int64
>>> df.shape
(6, 5)
>>>
  1. isnull()でTrue/False形式に変換
  2. sum()で集計

という順序で欠損値の数を確認できる。

この場合、「shape()メソッドの結果と一致している=全て欠損値の列」となる。
countryに関して、shapeの結果とisnull().sum()の結果が一致していることが確認できる。

欠損値ではない数を数える

欠損値ではない数を数える場合は、count()メソッドを利用する。

>>> df.count()
id         6
name       6
color      5
price      5
country    0
dtype: int64
>>> df.shape
(6, 5)
>>>

この場合、「0と表示されている=全て欠損値の列」となる。
countryが0となっていて、全てが欠損値であることが確認できる。

特定列にある欠損値の出現頻度を一覧で見る

ユニークな要素の出現頻度を数えるvalue_counts()メソッドを使うと
特定列のNaNを含めた出現頻度を見ることができる。

欠損値を含めて数えるためには、引数に「dropna=False」を指定する必要がある。

>>> df
   id        name   color   price  country
0   1       apple     red   200.0      NaN
1   2  strawberry     red   300.0      NaN
2   3      banana  yellow   214.0      NaN
3   4       meron   green  3000.0      NaN
4   5      orange     NaN  1280.0      NaN
5   6       grape  purple     NaN      NaN
>>> df['color'].value_counts()
red       2
yellow    1
green     1
purple    1
Name: color, dtype: int64
>>> df['color'].value_counts(dropna=False)
red       2
NaN       1
yellow    1
green     1
purple    1
Name: color, dtype: int64
>>>

このメソッドはDataFrameのような2次元の状態では利用できない点に注意する。

データの基本情報から判断する

データの詳しい情報を見るためのinfo()メソッドを利用すると、
データの概要とともに欠損値についても確認ができる。

>>> df
   id        name   color   price  country
0   1       apple     red   200.0      NaN
1   2  strawberry     red   300.0      NaN
2   3      banana  yellow   214.0      NaN
3   4       meron   green  3000.0      NaN
4   5      orange     NaN  1280.0      NaN
5   6       grape  purple     NaN      NaN
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 5 columns):
 #   Column   Non-Null Count  Dtype
---  ------   --------------  -----
 0   id       6 non-null      int64
 1   name     6 non-null      object
 2   color    5 non-null      object
 3   price    5 non-null      float64
 4   country  0 non-null      float64
dtypes: float64(2), int64(1), object(2)
memory usage: 368.0+ bytes
>>>

RangeIndexでデータサイズが分かるので、non-nullの数と見比べて、
どれくらい欠損値があるのかを確認できる。

欠損値への操作

欠損値の状態が確認できたところで、欠損値への操作を行う。

大抵の場合、以下のどちらかの対応になる。

  • 欠損値を除外する
  • 欠損値を何か別の値で埋める

全てが欠損値である列を削除する

今回のデータはcountryの列が全て欠損値となっている。
この列は何の意味も持たない列なので、削除したい。

欠損値の削除を行う場合はdropna()メソッドを利用する。
このメソッドに下記の引数を組み合わせて実現する。

  • 列方向に削除したい:axis=1を指定
  • 全ての値が欠損値であるものを削除したい:how='all'を指定
>>> df
   id        name   color   price  country
0   1       apple     red   200.0      NaN
1   2  strawberry     red   300.0      NaN
2   3      banana  yellow   214.0      NaN
3   4       meron   green  3000.0      NaN
4   5      orange     NaN  1280.0      NaN
5   6       grape  purple     NaN      NaN
>>> df.dropna(axis=1, how='all')
   id        name   color   price
0   1       apple     red   200.0
1   2  strawberry     red   300.0
2   3      banana  yellow   214.0
3   4       meron   green  3000.0
4   5      orange     NaN  1280.0
5   6       grape  purple     NaN
>>>

ところどころにNaNが含まれているcolorやpriceは削除されなかったが、
全てNaNで埋め尽くされているcountryは削除されたことが分かる。

特定の列の欠損値を補完する

今回の場合、全てのcountry=Japanとしたい場合、下記のようにfillna()メソッドを使って補完することができる。

>>> df
   id        name   color   price  country
0   1       apple     red   200.0      NaN
1   2  strawberry     red   300.0      NaN
2   3      banana  yellow   214.0      NaN
3   4       meron   green  3000.0      NaN
4   5      orange     NaN  1280.0      NaN
5   6       grape  purple     NaN      NaN

# countryの列だけ抽出して、fillna()メソッドを適用する
>>> df['country'].fillna('Japan')
0    Japan
1    Japan
2    Japan
3    Japan
4    Japan
5    Japan
Name: country, dtype: object

# 適用した結果で再代入する
>>> df['country'] = df['country'].fillna('Japan')
>>> df
   id        name   color   price country
0   1       apple     red   200.0   Japan
1   2  strawberry     red   300.0   Japan
2   3      banana  yellow   214.0   Japan
3   4       meron   green  3000.0   Japan
4   5      orange     NaN  1280.0   Japan
5   6       grape  purple     NaN   Japan
>>>

2次元のDataFrameそのものに対して適用してしまうと、
特定の行だけではなく全ての欠損値が補完されてしまうため注意。

>>> df
   id        name   color   price  country
0   1       apple     red   200.0      NaN
1   2  strawberry     red   300.0      NaN
2   3      banana  yellow   214.0      NaN
3   4       meron   green  3000.0      NaN
4   5      orange     NaN  1280.0      NaN
5   6       grape  purple     NaN      NaN
>>> df.fillna('HOGE')
   id        name   color   price country
0   1       apple     red   200.0    HOGE
1   2  strawberry     red   300.0    HOGE
2   3      banana  yellow   214.0    HOGE
3   4       meron   green  3000.0    HOGE
4   5      orange    HOGE  1280.0    HOGE
5   6       grape  purple    HOGE    HOGE
>>>

まとめ

  • 欠損値の数を数える:isnull().sum(), count(), value_counts()など
  • 欠損値を削除する:dropna()
  • 欠損値を補完する:fillna()
タイトルとURLをコピーしました