Aidemy 2020/10/30
はじめに
こんにちは、んがょぺです!バリバリの文系ですが、AIの可能性に興味を持ったのがきっかけで、AI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます!
今回は、「データ分析 タイタニック号」の2つ目の投稿になります。どうぞよろしくお願いします。
*本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。
今回学ぶこと
・④パターンの分析,データの分析(前回の続き)
・③データの整形、作成、クレンジング(戻ります)→新しい特徴量の作成
④パターンの分析,データの分析
前回のピボットテーブル(相関)の分析
・Pclass=1のとき、Survivedの平均が0.62であり、0.5を超えた有意な(正の)相関があると言えるので、Pclassはモデルの特徴量として使うこととする。
・同様に、Sex=Femaleのとき、Survivedの平均が0.74なので、これもモデルの特徴量として使う。
・SibSpとParchについては有意な相関がなかったので、この2つを組み合わせた新しい特徴量を作成する。
データを可視化
Ageの範囲を指定してヒストグラム化
・「子供は生存率が高い」という仮説を確かめるため、Ageのデータを範囲指定して分割する。これを使用して、データの分布をヒストグラムを使って作成する。
・このヒストグラムについて、横軸はAgeであるが、縦軸はデータの個数、つまり乗客数であるので注意が必要。
・ヒストグラムは「df.hist()」で作れる。階級を何個作るか(データを何分割するか)は引数に「bins=」で指定すればよく、Surviveが0と1のそれぞれのデータ数を見たいときは引数に「by=」でそのパラメータを指定すれば良い。
・また、Chapter1で見たとおりAgeには欠損値が含まれているので、dropnaで最初に欠損値を削除しておく。
・結果を見ると、「0~5才」の生存率が高いことがわかる。
・また、データの総数を見ると「15~35才」の数が多いこともわかる。
「カテゴリ値と数値」を持つ特徴量の相関
・次は、前項で作ったAgeごとのヒストグラムについて、さらにPclassごとにわけて可視化する。
・コードはSurvivedとAgeについてヒストグラム化し、それを「by=train_df['Pclass']」で分割するように記述する。
・このグラフから、「Pclass=3の死亡者が圧倒的に多い」「Pclass=1の生存者が多い」「Pclass=2,3のAgeが0~5才の生存者が多い」ということがわかる。いずれも仮説の通りであると言える。
「カテゴリ値」を持つ特徴量の相関
・「Embarked」が['C','Q','S']のときの「Survived」と「Pclass/Sex」の関係をピボットテーブルで作成し、「plt.plot()」で図示する。
・ピボットテーブルの作成はChapter1と同様に行う。今回はEmbarkedの中身を条件式で指定するのも忘れないようにする。また、グループ化(集計)するのはPclassとSexの二つなので、リストで渡す。
・作成したピボットテーブルを、Sexがmaleとfemaleで分割し、Pclassの順でソートする。
・これを、「plt.plot()」で図示する。x軸は['1','2','3']で、y軸はデータのSurvivedの数にする。
・コード(Embarked='C'のとき。他2つは同様に作成したものとする)(切れているところは前回と同じ)
・グラフ(Embarked='C'のときのみ、他二つも同じようなグラフ)
・このグラフから「女性の生存率が圧倒的に高い」ということができる。これも仮説の通りである。また、ここでは図示していないが、「Embarked='Q'の男性の生存率がとても低い」ということもわかった。
「カテゴリ値」を持つ特徴量と「数値」を持つ特徴量間の相関
・今度は、前項の「Pclass」の部分を「Survived」に、「Survived」の部分を「Fare」に置き換えて、全く同じように図示する。
・「Fare」と「Survived/Sex」の関係をピボットテーブルで作成したら、次はそれをSurvivedが0か1かで分割する。今回は、それをサブプロットで表す。「plt.subplot()」でできる。横軸には「Sex」縦軸には「Fare」の棒グラフ(plt.bar())をセットし、左が「Survived==0」、右が「Survived==1」となるようにセットする。
③データの整形、作成、クレンジング
・ここからは、特徴量の変換・作成・補完を行う。
データの削除
・まずはChapter1の方針で決めたように、欠損や重複の多い「Ticket」と「Cabin」を削除する。
・列の削除なので「drop(axis=1)」を使う。
新しい特徴量の作成
・同様に、Survivedと明らかに相関のない「Name」と「PassengerId」も削除していくが、「Name」に含まれる敬称(Mr,Mrs,Drなど)とSurvivedとの相関がある可能性があるので、確認する。
・敬称をNameから抽出するには、正規表現を使う。str型のデータから正規表現にマッチする部分を抽出するには、「str.extract('正規表現')」を使う。今回の正規表現は'([A-Za-z]+).'となる。これは「Dr.」など、敬称は「.」の前に大文字小文字が複数あるため、このように表現される。また、この第二引数には「expand=False」を指定するが、これは抽出したものをDataFrameで返すことを示している。
・この抽出したものは、'Title'という新しい列(特徴量)に格納する。
・この'Title'と'Sex'の関係を「クロス集計」する。これは、今回の例で言えばDr.やMrs.などのTitleのカテゴリごとにSexの要素がどれぐらい出現したかを集計するものである。
・クロス集計を行うには「pd.crosstab()」関数を使う。第一引数には結果の行にあたるデータ(今回は'Title')、第二引数には結果の列にあたるデータ('Sex')を渡す。
・ここで出てきた敬称のうち、頻度の少ないものを「'Rare'」というその他枠にまとめる。また、同様の意味を持つ'Mile'は'Miss'に、'Mme'は'Mrs'に置き換える。これらの置き換えは「replace()」で行えば良い。
・ここまで行ったらピボットテーブル化し、相関を調べる。
・このTitleについて、各要素を数値として扱いたいので、{"Mr":1,"Miss":2,"Mrs":3,"Master":4,"Rare":5}に変換する。
・方法は、上記対応表(辞書)を準備しておき、dataset['Title']に対して「map()」関数を適用させることで変換できる。
・以下の実行が終わったら、当初の想定どおり「Name」と「PassengerId」を削除する。
多値データを二値データに変換
・前項で使用したmap()関数を使って、次は多値データを二値データに変換する。
・ここでは'Sex'がmale:0,female:1となるように変換する。
・astype()メソッドの引数にデータ型を指定すると、全ての列の指定したデータ型が全て変更された、新しいDataFrameが返される。今回は全てint型にしたいので、引数にはintと指定する。
特徴量の補完 Age
・データの削除、変換、作成を行ったら、次に補完を行う。補完はNullまたはNaNに対して値を推測して代入することである。
・まずは連続的な数値データである「Age」を補完する。補完の方法は以下の3つがある。
1:平均を参考にして乱数を生成する
2:相関のある他の特徴量を参考にする
3:1と2を組み合わせる、具体的には、平均と標準偏差を参考にして乱数を生成する
・今回は「2」の手法を用いる。具体的に「Age」と相関のある特徴量とは「Sex」と「Pclass」の二つが挙げられる。この二つの特徴量を参考にして、Ageの推測値(中間年齢)を取得する。
・まずはAgeの値を格納する2行3列の空の配列を用意する。このように具体的な行列の大きさがわかっているからの配列を作るときは「np.zeros()」を使うと良い。引数に行列の形を指定することで、値が全て0の(実質的に空の)配列を作成することができる。
・次に、Ageの推測値(中間年齢)を算出する。まずは'Sex'と'Pclass'の全ての組み合わせの場合の'Age'の値を(NaNを除外して)抽出する。組み合わせは「2*3通り」あるので、行列の大きさを(2,3)と指定した。これに対して「median()」を使って中央値を取得すると、これが中間年齢となる。
・このそれぞれの場合における中央値を(2,3)の配列に格納して終了。
連続値を離散値に変換
AgeBand
・Ageの補完を行ったら、連続値であるAge全体について、離散値に変換する。これはChapter1の指針「作成」の項で示した通り、範囲を指定して分割する(=離散データに変換する)ことで予測しやすくするために行う。
・離散値に変換することを「ビニング処理」あるいは「ビン分割」という。行う方法は、「pd.cut()」を使う。第一引数にはデータを渡し、第二引数にはデータを何分割するかを指定する。今回は5分割とし、離散データとして「AgeBand」という新しい特徴量に格納する。
・また、AgeBandとSurvivedとの相関を確認したいので、ピボットテーブルも作成する。
・次に、離散値に変換したAgeを順序データに変換する。具体的には、AgeBandを参考にして、Ageが「0~16」なら0、「16~32」なら1、「32~48」なら2、「48~64」なら3に変換する。
・変換方法は、loc[条件式,変換する列]で'Age'の範囲を抽出してそれを上記の数値に置き換えれば良い。
・ここまで終えたらAgeBandはdrop()で削除する。
新しい特徴量の作成
FamilySize
・方針の「作成」で示した通り、同系統の特徴量「Parch」「Sibsp」を組み合わせて「FamilySize」という新しい特徴量を作成する。この特徴量は「家族の数」を表す。
・方法は、普通に二つの列を抽出して足し合わせれば良い。ただし、自分も「家族の数」に入るので、その分の「+1」も忘れないようにする。
・新しい特徴量を作成したときは、これまで通りSurvivedとの平均をとって相関を調べる。
IsAlone
・上記FamilySizeについて、より抽象化するために「独身か家族もちか」という分類を行う。すなわち、FamilySizeが1なら「IsAlone=1」、それ以外は全て「IsAlone=0」というように変換する。
・作り方としては、まず全ての値を「0」としてIsAloneという特徴量を作成し、FamilySize=1の時のみ「1」に変換するように行う(loc()を使う)。
・ここまで行ったら、Parch,SibSp,FamilySizeを削除する。
Age*Class
・ここでは「年齢」に「船室のグレード」を掛けて重み付けした「Age*Class」という人工的な特徴を作成する。作り方はそのままAgeとPclassを抽出してかければ良い。
最頻値で補完 Embarked
・Chapter1で見た通り、訓練データのEmbarkedには2つ欠損値がある。この欠損値について、まずは削除し、その部分を最頻値で置き換える。ちなみに最頻値は「S」である。
・最頻値は「mode()」で取得できる。また、最頻値のみを取得したい(indexは必要ない)ので[0]でcolumnのみ取得する。
・取得した最頻値を変数freq_portに格納し、「fillna(freq_port)」で補完する。
・また、Sexなどと同様に、Embarkedはカテゴリ値であるので、{'S':0,'C':1,'Q':2}のように数値に変換する。map()関数を使う。また、astype(int)とする。
数値データの補完 Fare
・テストデータのFareには1つだけ欠損値が存在する。この欠損値には中央値(median)を代入する。
・さらに、Ageと同様に連続値であるFareを離散値に変換する。変換したものをFareBandという新しい特徴量に格納する。離散値はFareを4分割にして算出する。
・Ageのときは「cut()」で範囲を分けたが、これは「範囲が均等になるように分割する」ときに使うものである。一方今回のFareでは「範囲に含まれる要素の数が均等になるように分割する」ときに使用する「qcut()」を使う。
・このFareBandを参照して、Fareを離散値に置き換える。今回は「~7.91」が0、「7.91~14.454」が1、「14.454~31」が2、「31~」が3というように変換する。
・変換方法はAgeの時と同じく「loc[条件式,変換する列]」で行う。
まとめ
・予測の目的となる変数「Survived」との相関を調べ、有意な相関があるものについてはそのまま使用し、有意な相関が見られないものについては、Nameから敬称Tirleを抽出したように、その変数の一部分を取り出して新しい特徴量を作成したり、ParchとSibSpを組み合わせてFamilySizeにしたように、新しい特徴量を作成したりする。
・また、AgeやFareなどの連続値は離散値に変換して特徴量を変換する。欠損値を含む場合は中央値や最頻値を使って値を補完する。
・SexやEmbarkedは[male,female][S,C,Q]のようなデータになっているので、[0,1][0,1,2]のように数値データに置き換える。
今回は以上です。最後まで読んでいただき、ありがとうございました。