これは何?
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つ。
- 欠損値を含む行/列を取り除く
- 欠損値を何らかの値で補完する
- 欠損値を無視する
「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形式で記事書いたけど、慣れないせいで時間かかった...