前回の記事からの続きです。
今回はタイタニックのデータの中身を俯瞰しようと思います。
単純にクロス集計をして表を眺めるだけでもいいのですが、ヒストグラムや散布図などで可視化してあげるとより理解しやすくなると思います。
タイタニックのデータぐらいのカラム数なら問題ないのですが、実業務で変数の数が多くなると見ないといけない数字やグラフの数も増えるので大変になります。
変数選択など経て変数の数を減らしてからデータの中身を見た方が実は効率がいいのかも知れません。
データ確認の結果が少し長くなってしまったので3つの記事に分割して公開します。
(その2-2)はこちら
② データの理解
カテゴリ型の変数と数値型の変数
前回変数の一覧をまとめてみましたが、どの変数がカテゴリ型なのか数値型なのかまとめてみました。
色々呼び方はあるかと思います。質的変数・量的変数とか、名義型・離散型・連続型などいずれかは統一した呼び方をするためにまとめたいと思います。
カテゴリ型の変数
- Survived (0:非生存、1:生存)
- Pclass (1:1st(上級クラス)、2:2nd(中級クラス)、3:3rd(下級クラス))
- Sex (1:男性、2:女性)
- embarked (C:Cherbourg、Q:Queenstown、S:Southampton)
数値型の変数
- Age (年齢)
- Fare (運賃)
- SibSp (タイタニックに乗船した兄弟・姉妹・配偶者の数)
- Parch (タイタニックに乗船した両親・子供の数)
※ ほとんどのケースでツール側でデータの中身をみてどのデータ型か判別してくれることが多いですが、分析の目的に合わせて文字列ではなく数値で読み込んだりする場合もあります。今回は上記のようにまとめてみました。名前は名義型でしょうか?PassengerIdは数値型に入るのでしょうか?とりあえず分析に使いそうな項目だけ分類しました。
データ俯瞰
データの読み込み
import pandas as pd
# タイタニックデータセットの訓練データを読み込み
df = pd.read_csv("/Users/hinomaruc/Desktop/notebooks/titanic/train.csv")
df
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked 0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S 1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C 2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S 3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S 4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S ... ... ... ... ... ... ... ... ... ... ... ... ... 886 887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.0000 NaN S 887 888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.0000 B42 S 888 889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.4500 NaN S 889 890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.0000 C148 C 890 891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.7500 NaN Q 891 rows × 12 columns
# カラム名を確認
df.columns
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'], dtype='object')
データ件数の確認
# 最大レコード数を確認。indexが0から始まるので、+1している。
df.index.max() + 1
891
タイタニックの乗客が約2224人なので、その内891人のデータが学習用データのようです。
891/2224 = 0.40なので全体の約40%のサンプルになるようです。
各カラムのレコード数の確認 (NULLはカウントしない)
# 各カラムのレコード数の確認 (NULLはカウントされない)
df.count()
PassengerId 891 Survived 891 Pclass 891 Name 891 Sex 891 Age 714 SibSp 891 Parch 891 Ticket 891 Fare 891 Cabin 204 Embarked 889 dtype: int64
Age,Cabin,EmbarkedでNULLが存在するようです。
欠損値処理で平均、中央値、最頻値などに置換する必要がありそうです。
Cabinは欠損値が多いのでそもそも利用しないという手もあります。
infoメソッドの利用
全体や各変数のレコード数を確認したい場合はdf.info()でも確認できるようです。
# infoメソッドでも大体のことが分かる
df.info()
RangeIndex: 891 entries, 0 to 890 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 PassengerId 891 non-null int64 1 Survived 891 non-null int64 2 Pclass 891 non-null int64 3 Name 891 non-null object 4 Sex 891 non-null object 5 Age 714 non-null float64 6 SibSp 891 non-null int64 7 Parch 891 non-null int64 8 Ticket 891 non-null object 9 Fare 891 non-null float64 10 Cabin 204 non-null object 11 Embarked 889 non-null object dtypes: float64(2), int64(5), object(5) memory usage: 83.7+ KB
欠損値の確認
次に重要な欠損値の確認です。
# 欠損値の確認
chk_null = df.isnull().sum()
chk_null[chk_null > 0]
Age 177 Cabin 687 Embarked 2 dtype: int64
欠損があるレコード数が表示されました。
数値だけだと規模感が分かりづらいので、割合も出してみます。
# 欠損値割合の確認
chk_null_pct = chk_null / (df.index.max() + 1)
chk_null_pct[chk_null_pct > 0]
Age 0.198653 Cabin 0.771044 Embarked 0.002245 dtype: float64
Ageが約20%、Cabinが約77%欠損していることが分かりました。
モデル作成時にCabinは欠損割合が高いのでやはり利用は躊躇すると思います。
Ageは悩みます。とても重要なファクターな気もするので、上手く他の変数から年齢を推定できないかどうか検討するかもしれません。
データ重複の確認
重複データがあるかどうか確認します。
キーだと思われる変数に重複があったりすると結合処理などが上手く出来ない場合があるので確認してみます。
# 最大重複数を出力
print("最大重複レコード数 (1=重複なし)")
chk_duplicates = df.dtypes[df.dtypes == 'object']
for idx in chk_duplicates.index: # indexでloopする
# for val in chk_duplicates: # valueでloopする
# for idx,val in chk_duplicates.items(): # index,valueでloopする
print(idx,"->",df.groupby(idx).PassengerId.nunique().max())
最大重複レコード数 (1=重複なし) Name -> 1 Sex -> 577 Ticket -> 7 Cabin -> 4 Embarked -> 644
Nameは重複なしのようです。
TicketやCabinは若干重複しているデータがあるようです。
同室に泊まっている家族のデータは同じTicketやCabinの番号になっているのかも知れません。
SexやEmbarkedはカラム定義から推測するにカテゴリ値なので重複データになっていても問題ありません。
ちなみに、重複データの確認はpandasのdescribeメソッドでも確認することが出来ます。
わざわざfor loopを使って確認しなくても良いかもしれません。
記述統計の確認 (数値型)
pandasには記述統計をお手軽に確認できるdescribeメソッドが用意されています。
デフォルトだと数値型以外の変数の統計量(最頻値など)は出てきませんが、df.describe(include=['O'])のようにオプションを設定することによって質的データの統計も確認することが可能になります。
またパーセンタイル値もデフォルトだと[0,0.25,0.5,0.75,1]しか表示されませんが、こちらもpercentiles=[0.1,0.2,0.3]のように設定が可能なようです。
# describeメソッドで数値データの概要を確認
df.describe()
PassengerId Survived Pclass Age SibSp Parch Fare count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000 mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208 std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429 min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000 25% 223.500000 0.000000 2.000000 20.125000 0.000000 0.000000 7.910400 50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200 75% 668.500000 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000 max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
記述統計の確認 (名義型)
質的変数の件数、ユニーク数、最頻出、重複数が確認出来ます。
df.describe(include=['O'])
Name Sex Ticket Cabin Embarked count 891 891 891 204 889 unique 891 2 681 147 3 top Braund, Mr. Owen Harris male 347082 B96 B98 S freq 1 577 7 4 644
最頻値や重複数がさくっと確認できるのはとても便利ですね。
パーセンタイルの確認
describeメソッドでもオプションに追加すれば確認出来ますが、パーセンタイルだけ確認することもquantileメソッドを使えば可能になります。
# パーセンタイルで数値データの詳細を確認
df.quantile([0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.99,1])
PassengerId Survived Pclass Age SibSp Parch Fare 0.00 1.0 0.0 1.0 0.42 0.0 0.0 0.00000 0.10 90.0 0.0 1.0 14.00 0.0 0.0 7.55000 0.20 179.0 0.0 1.0 19.00 0.0 0.0 7.85420 0.30 268.0 0.0 2.0 22.00 0.0 0.0 8.05000 0.40 357.0 0.0 2.0 25.00 0.0 0.0 10.50000 0.50 446.0 0.0 3.0 28.00 0.0 0.0 14.45420 0.60 535.0 0.0 3.0 31.80 0.0 0.0 21.67920 0.70 624.0 1.0 3.0 36.00 1.0 0.0 27.00000 0.80 713.0 1.0 3.0 41.00 1.0 1.0 39.68750 0.90 802.0 1.0 3.0 50.00 1.0 2.0 77.95830 0.95 846.5 1.0 3.0 56.00 3.0 2.0 112.07915 0.99 882.1 1.0 3.0 65.87 5.0 4.0 249.00622 1.00 891.0 1.0 3.0 80.00 8.0 6.0 512.32920
パーセンタイルからは数多くの情報やインサイトを得ることが出来ます。
- PassengerIdは連番なのであまり利用の意味が無いかもしれません。
- Survivedを確認すると70パーセンタイルで1になっているので、訓練データの乗客のうち約3割が生存者ということが分かります。
- Pclassを確認すると、30%が1st、20%が2nd、そして残りの50%が3rdということが分かります。
- Ageを確認すると、20%が19歳以下、70%が19歳〜50歳、残りの10%が50歳以上のようです。昔の平均年齢によりますが、高齢な乗客は訓練データにはあまり存在しないようです。
- SibSpは約70%が0人、Parchも約80%が0人なようです。SibSpとParchを組み合わせた複合変数を作ってみたほうがいいかも知れませんが、もしかしたら1人で乗ってる人が多いのでしょうか?友達と一緒という可能性もありますね。もう少し時代背景などを勉強したら1人が多い理由がわかるかもしれません。例えばNewYorkに仕事を探しに行く人が多かったからなど (注:ただの仮説です。)
- Fareにはかなりばらつきがあるようです。0~512のようです。(単位はドルかな?) 0ドルでの乗船は色々憶測を立てることが出来そうです。乳幼児は無料なのか、関係者は無料なのか、それともゴミデータなのかなど名前や年齢などと組み合わせて見る必要がありそうです。最高額で乗船した人が生存状況も気になります。Fareが高い部屋の近くはもしかしたら生存確率に関係あるかも知れませんね。(脱出用ボートがある非常階段から近い遠いなどはあるかも知れません。)
重複データの中身の確認
describeメソッドで最頻出の文字列を確認することが出来ますが、念のためもう少し詳細を確認しておきます。
# Sexの重複レコード件数の降順
df.groupby(["Sex"]).count().sort_values(by="PassengerId",ascending=False).head()
PassengerId Survived Pclass Name Age SibSp Parch Ticket Fare Cabin Embarked Sex male 577 577 577 577 453 577 577 577 577 107 577 female 314 314 314 314 261 314 314 314 314 97 312
# Ticketの重複レコード件数の降順
df.groupby(["Ticket"]).count().sort_values(by="PassengerId",ascending=False).head()
PassengerId Survived Pclass Name Sex Age SibSp Parch Fare Cabin Embarked Ticket 1601 7 7 7 7 7 4 7 7 7 0 7 CA. 2343 7 7 7 7 7 0 7 7 7 0 7 347082 7 7 7 7 7 7 7 7 7 0 7 CA 2144 6 6 6 6 6 6 6 6 6 0 6 347088 6 6 6 6 6 6 6 6 6 0 6
# Cabinの重複レコード件数の降順
df.groupby(["Cabin"]).count().sort_values(by="PassengerId",ascending=False).head()
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Embarked Cabin C23 C25 C27 4 4 4 4 4 4 4 4 4 4 4 G6 4 4 4 4 4 4 4 4 4 4 4 B96 B98 4 4 4 4 4 4 4 4 4 4 4 F2 3 3 3 3 3 3 3 3 3 3 3 C22 C26 3 3 3 3 3 3 3 3 3 3 3
# Embarkedの重複レコード件数の降順
df.groupby(["Embarked"]).count().sort_values(by="PassengerId",ascending=False).head()
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked S 644 644 644 644 644 554 644 644 644 644 129 C 168 168 168 168 168 130 168 168 168 168 69 Q 77 77 77 77 77 28 77 77 77 77 4
まとめ
まだデータ確認の前半部分ですが、かなりボリュームが多くなってしまいました。
実業務でもデータ確認や加工に大部分の時間をかけることが多いと思います。
後半も近いうちに投稿する予定です。