LoginSignup
2
3

More than 5 years have passed since last update.

[Python][pandas]データフレーム中の欠損値の確認/削除/補完方法まとめ

Last updated at Posted at 2019-02-01

これは何?

Python/pandasを使用したデータ分析で欠損値の扱いに困ったので、確認方法/対処方針とそのコードをまとめる。
私と同じデータ分析初心者の人の参考になればと思います。

データの準備

import pandas as pd
import numpy as np

# サンプルデータフレームのカラム名
list_column =  ['name', 'age', 'sex', 'height', 'weight']

# サンプルデータフレームの内容
temp_arr = np.array([
    ['ALice', 23, 0, 160.3, 52.6],
    ['Bob', 28,  1, None, 74.2],
    ['Carol', 31,  0, 154.4, None],
    ['Dave', 38, 1, None, 89.1],
    ['Ellen', 43, 0, 149.1, 51.4]
])

# サンプルデータフレームの作成
df = pd.DataFrame(data=temp_arr, columns=list_column)
実行結果
>>> df
    name age sex height weight
0  ALice  23   0  160.3   52.6
1    Bob  28   1   None   74.2
2  Carol  31   0  154.4   None
3   Dave  38   1   None   89.1
4  Ellen  43   0  149.1   51.4

欠損値の場所/数を特定する

欠損値を表示(欠損値の場所が"True"として表示される)

df.isnull()
実行結果
    name    age    sex  height  weight
0  False  False  False   False   False
1  False  False  False    True   False
2  False  False  False   False    True
3  False  False  False    True   False
4  False  False  False   False   False

Noneになっていた部分がTrueになってますね。

カラムごとに欠損値の数を表示

df.isnull().sum()
実行結果
name      0
age       0
sex       0
height    2
weight    1
dtype: int64

sum()関数で、isnull()関数を実行した結果から各列の合計値を表示している。
この時、「True = 1」「False = 0」と扱われるらしい。

欠損値への対処

前述のコードで欠損値(None)の場所と数を明らかにしたので、それらの欠損値に対処する。
ググってみた感じ、方針は大きく2つ。

  1. 欠損値を含む行/列を取り除く
  2. 欠損値を何らかの値で補完する
  3. 欠損値を無視する

「3.欠損値を無視する」と、色々と不都合が多いのでやめた方が良さそう。

方針①:欠損値を含む行/列を取り除く

欠損値を含むを削除する

df.dropna(axis=0) #引数は無くても同じ挙動をする
実行結果
    name age sex height weight
0  ALice  23   0  160.3   52.6
4  Ellen  43   0  149.1   51.4

欠損値を含むを削除する

df.dropna(axis=1)
実行結果
    name age sex
0  ALice  23   0
1    Bob  28   1
2  Carol  31   0
3   Dave  38   1
4  Ellen  43   0

欠損値の数が閾値以上のを削除する

# 閾値(欠損値の列を削除する基準)を計算する
# 今回は全データ数の4割以上がNAなら列を削除する
threshold = len(df)*0.4

# 欠損値の数が閾値以下の列名リストを取得
series_under_threshold = df.isna().sum() < threshold
list_isna_under_thresholt = series_under_threshold[series_under_threshold].index

# 取得したリストの列のみ抽出
df = df[list_isna_under_thresholt]
実行前
    name   age  sex  height  weight
0  ALice  23.0  0.0   160.3    52.6
1    Bob  28.0  1.0     NaN    74.2
2  Carol  31.0  0.0   154.4     NaN
3   Dave  38.0  1.0     NaN    89.1
4  Ellen  43.0  0.0   149.1    51.4
実行結果
    name   age  sex  weight
0  ALice  23.0  0.0    52.6
1    Bob  28.0  1.0    74.2
2  Carol  31.0  0.0     NaN
3   Dave  38.0  1.0    89.1
4  Ellen  43.0  0.0    51.4

方針②:欠損値を何らかの値で補完する

欠損値を同列の平均値で補完する

地道に列ごとの平均値を計算⇒欠損値へ代入しても補完できるが、scikit-learnにImputer(補完機)クラスという便利なクラスが用意されているのでこれを利用する。

scikit-learnのImputer(補完用クラス)を使って平均値補完を実行する
from sklearn.preprocessing import Imputer

# 欠損値を保管するためのImputerを生成して、dfの値に適合させる
imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)
imputer = imputer.fit(df.values)

# Imputerによる補完を実行する
arr_imputed= imputer.transform(df.values)
  • 4行目のImputer(missing_values='NaN', strategy='mean', axis=0)について
    missing_values:何の値を欠損値として扱うか。今回はNoneを保管対象にしたいので'NaN'を指定
    strategy:補完値の計算方法。{ 'mean' ⇒ 平均値 , 'median' ⇒ 中央値 , 'most_frequent' ⇒ 最頻値 }
    axis:補完値を求める方向。{ 0 ⇒ 列方向 , 1 ⇒ 行方向 }
    詳細は https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.Imputer.html で確認してください
実行結果
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\User\Anaconda3\lib\site-packages\sklearn\preprocessing\imputation.py", line 155, in fit
    force_all_finite=False)
  File "C:\Users\User\Anaconda3\lib\site-packages\sklearn\utils\validation.py", line 433, in check_array
    array = np.array(array, dtype=dtype, order=order, copy=copy)
ValueError: could not convert string to float: 'Ellen'

ダメだった理由:
 inputer.fit()メソッドには数値型の値しか渡せないっぽい。
 ⇒ "name"列の"Ellen"が数値型に変換できないからエラーとなってしまった。。
   (元のデータフレームに数値しか入っていない場合はエラーにならない)

文字列が混じったデータフレームでもエラーにならないように修正

エラーを回避するために
 ①:数値型の列名リスト(list_columns_include_number)を取得
 ②:元データフレーム(df)から、数値型の列のみの新データフレーム(df_include_number)を抽出
 ③:②で抽出した新データフレームに対し、Imputerクラスを利用して欠損値を補完
 ④:元データフレームの数値型列を新データフレームの値で上書き
の手順で補完する。

from sklearn.preprocessing import Imputer

# 数値型のみのデータフレームを作成する(数値型の列名リスト取得 ⇒ 数値型の列のみ抽出)
list_columns_include_number = df.select_dtypes(include='number').columns.values
df_include_number = df[list_columns_include_number]

# 欠損値を保管するためのImputerを生成して、dfの値に適合させる
imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)
imputer = imputer.fit(df_include_number.values)

# Imputerによる補完を実行する
arr_imputed = imputer.transform(df_include_number.values)

# 補完した結果をデータフレームに変換する
df_imputed = pd.DataFrame(data=arr_imputed, columns=list_columns_include_number)

# 元のデータフレームの値を更新する
df[list_columns_include_number] = df_imputed
実行前
>>> df
    name   age  sex  height  weight
0  ALice  23.0  0.0   160.3    52.6
1    Bob  28.0  1.0     NaN    74.2
2  Carol  31.0  0.0   154.4     NaN
3   Dave  38.0  1.0     NaN    89.1
4  Ellen  43.0  0.0   149.1    51.4
実行結果
>>> list_columns_include_number
array(['age', 'sex', 'height', 'weight'], dtype=object)

>>> df_imputed
    age  sex  height  weight
0  23.0  0.0   160.3  52.600
1  28.0  1.0   154.6  74.200
2  31.0  0.0   154.4  66.825
3  38.0  1.0   154.6  89.100
4  43.0  0.0   149.1  51.400

>>> df
    name   age  sex  height  weight
0  ALice  23.0  0.0   160.3  52.600
1    Bob  28.0  1.0   154.6  74.200
2  Carol  31.0  0.0   154.4  66.825
3   Dave  38.0  1.0   154.6  89.100
4  Ellen  43.0  0.0   149.1  51.400

方針③:欠損値を無視する

これはやめた方が良いかと。。。
どこかしらでエラーや見落としが発生しがちなので…

おわりに

  • 数値型以外の欠損値補完についてはよく分かってないので、調べた上でその内まとめたいなぁ
  • 「欠損値の数が閾値以上の列を削除する」の↓のコード、もっときれいに書けないかなぁ
    # 欠損値の数が閾値以下の列名リストを取得
    series_under_threshold = df.isna().sum() < threshold
    list_isna_under_thresholt = series_under_threshold[series_under_threshold].index
  • 初めてまともにMarkdown形式で記事書いたけど、慣れないせいで時間かかった...
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3