"BOKU"のITな日常

62歳・文系システムエンジニアの”BOKU”は日々勉強を楽しんでます

Pandas:データの加工(置き換え、欠損値修正、one-hot変換、不要列の削除など)

pandasで頻繁に行うデータの加工方法について整理してみます。

f:id:arakan_no_boku:20200329000112p:plain

 

 はじめに

 

 

今回はDataFrameに読み込んだデータの加工です。

pandasはすごく機能が豊富なので、いろんなことができるのですが、あまり手を広げても混乱するので、とりあえず自分がよく使うパターンだけまとめておきます。

pandas.pydata.org

 

なお。

コード例は、以下がインポートされている前提とします。

import pandas as pd

以下の要領でCSVファイルを読み込んで「t」というDataFrameがある想定です。

tdf = pd.read_csv('./csvdata/train.csv')

例では kaggleのtitanicデータを一部使わせていただいてます。

 

単純置き換え

 

機械学習に使えない文字列データを、数字に置き換えるときとかに使います。

対象の文字列がわかっている場合は、DataFrameの組み込みメソッド(replace)を使っても簡単にできます。

f:id:arakan_no_boku:20200504184719p:plain

例えば、上記「sex」列の「male」「famale」を「0」「1」にするには。

tdf['Sex'].replace(['male','female'],[0,1], inplace=True)

です。

結果は、以下のようになります。

f:id:arakan_no_boku:20200504185236p:plain

 

正規表現置き換え

 

DataFrameの組み込みメソッド(replace)は「regex=True」にすると、置き換え対象の指定に正規表現が使えます。

例えば。

f:id:arakan_no_boku:20200504221821p:plain

上記の[Cabin」列のように、「C35」「D」「C33 E45」などのように、大文字の英数字で始まり、単独で終わる場合もあれば、後ろに英数字空白が続く場合もあるようなものをすべて「1」に置き換えるとすると。

tdf['Cabin'].replace('[A-Z].*',1, regex=True, inplace=True)

のように正規表現を使うと一発でできます。

f:id:arakan_no_boku:20200504222208p:plain

 

カテゴリデータを数値に置き換え

 

変換対象の文字列がすべてわからない場合は、replaceで条件を書くのではなく、「factorise」を使います。

例えば、以下の「Embarked」列は「S・C」以外に文字があります。

f:id:arakan_no_boku:20200506105754p:plain

そういう時は

tdf['Embarked'] = pd.factorize(t'Embarked'])[0]

のようにすると、きっちり数値返還してくれます。

f:id:arakan_no_boku:20200506110303p:plain

 

欠損値の補完

 

データには欠損値(NaN)がありえます。

欠損値を扱う方針としては

  • 欠損値を修正せずそのまま使う
  • 代表的な値または他の特徴量から予測した値で欠損値を補完する
  • 欠損値か否かの情報(0or1)で新しい特徴量を作る

 の3パターンが考えられます。

 

代表的な値または他の特徴量から予測した値で欠損値を補完する

 

DataFrameの組み込みメソッドの「fillna」を使います。

f:id:arakan_no_boku:20200504203744p:plain

 

上記の「Cabin」列にある、NaN(欠損値)を「0」に置き換えます。

tdf['Cabin'].fillna(0, inplace=True)

 で、以下のようになります。

f:id:arakan_no_boku:20200504204119p:plain

 上記の「0」のところを、

np.mean(t['Fare'])のような式に置き換えると、平均値や最小値などいろいろな値に置き換えることもできます。

 

欠損値か否かの情報(0or1)で新しい特徴量を作る

 

例えば。

f:id:arakan_no_boku:20200504224511p:plain

のようにNaN(欠損値)を含む「Age」列で欠損値を置き換えてしまう前に、ここが欠損値であったという情報を「AgeIsNull」みたいな列を追加して残しておきたいような場合には、以下のよう組み込みメソッド「loc」を使います。

tdf.loc[~(tdf['Age'].isnull()), 'AgeIsNull']= 0
tdf.loc[(tdf['Age'].isnull()), 'AgeIsNull']= 1

~は否定演算子です。

Age列が「NaN」でない行は、「AgeIsNull」列に「0」をセットし、そのあとでAge列が「NaN」である行の「AgeIsNull」列に「1」をセットしてます。

f:id:arakan_no_boku:20200504225232p:plain

 

欠損値を0、欠損値以外を1にする

 

titanicにはないのですが、たまに欠損値と、それ以外がバラバラのデータ(なんかのIDとか)が混在しています的なデータがあります。

それを欠損値を「0」、それ以外を「1」に置き換えたい場合があります。

それを「uid」という列でやると、こんな感じです。

tdf.loc[(tdf['uid'].notnull()), 'uid'] = 1
tdf['uid'].fillna(0, inplace=True)

  

カテゴライズデータをダミー列に変換

 

以下のEmbarked列は、「S,・C・Q」の3つのカテゴリに分かれているデータです。

f:id:arakan_no_boku:20200506105644p:plain

これを単純に数字にする変換(上でfactorizeしたみたいな)は、その数字の大小関係まで特徴ととらえられてしまって、よくない場合があります。

そういう時は、0,1の2値でカテゴリの違いを表現できるようなダミー列を作るというのが良いといわれてます。

こういうときに便利なのが「get_dummies」です。

t_emb = pd.get_dummies(t['Embarked'], prefix='Emb')

のように実行すると「t_emb」に以下のようにできます。

f:id:arakan_no_boku:20200506141047p:plain

これを元のDataFrameにくっつけるには「merge」を使います。

train = pd.merge(t, t_emp, right_index=True, left_index=True)

上記でもとの「t」の後ろに、「t_emp」がくっつきます。 

 

条件式で新しい「one-hot」列を作るケース

 

機械学習用データで、1つの列に「1,2,3,4,5」のように3つ以上の選択肢があるとき、そのまま学習すると単なるコードなのに、数値の差に意味があるように学習してしまって精度があがらないことがあります。

そんなとき、IsOne、isTwo・・のように、0,1の二値(one-hot)に変換した特徴量の列を追加する場合にも、上記の新しい特徴量を作る手段は使えます。

例えば。

f:id:arakan_no_boku:20200504230950p:plain

上記の「Pclass」列のように「1 = 1st, 2 = 2nd, 3 = 3rd」という3つの選択肢があるものを、Is1st、Is2nd、Is3rdのような0と1の2値の列を追加するような場合、以下のようにします。

t.loc[(train_org['Pclass']==1), 'Is1st'] = 1
t.loc[~(train_org['Pclass']==1), 'Is1st'] = 0
t.loc[(train_org['Pclass']==2), 'Is2nd'] = 1
t.loc[~(train_org['Pclass']==2), 'Is2nd'] = 0
t.loc[(train_org['Pclass']==3), 'Is3rd'] = 1
t.loc[~(train_org['Pclass']==3), 'Is3rd'] = 0

すると、以下のようにより特徴量が明確になります。

f:id:arakan_no_boku:20200504231402p:plain


不要な列の削除

 

機械学習で使用しない列を切り落とすときに使います。

例えば、「Name」と「PassengerId」列を切り落として、学習用データを作るときは以下のようにします。

delete_cols = ['Name','PassengerId']
train = train_org.drop(delete_cols,axis=1) 

 axis=1は必須です。

 

おまけ

 

kaggleのsubmit用フォーマットに、評価結果を更新するパターンです。

y_pred = clf.predict(x_test)

で評価結果が「y_pred」にある想定で、更新先はこんなPandasのDataFrameです。

f:id:arakan_no_boku:20200505203521p:plain

この「Survived」列を、上記の評価結果で置き換えるのは。

sub['Survived'] = list(map(int, y_pred))

 です。

mapで、y_predの要素をすべて「int()」で数値に変換して、listにして渡します。

今回はこんなところで。

ではでは。