Kaggle(12) - 単変数のデータ分析

ヒストグラムを使って単変数のデータ分析を行います。

ヒストグラムは度数分布表をグラフ化したもので、1つのデータの分布・傾向などを分析するために使用します。

ヒストグラムの表示

タイタニックのデータを読み込みます。

1
2
3
4
import seaborn as sns
from matplotlib import pyplot as plt
sns.set(style='darkgrid')
titanic = sns.load_dataset('titanic')

distplot関数を使って、タイタニック乗船客の年齢ごとの乗客数(密度:density)をヒストグラムで表示します。

密度(density)は、ヒストグラムの総面積を1としたときの割合となります。

1
sns.distplot(titanic.age)

縦棒の数はbinsオプションで指定することができます。

binsオプションを変更することによって、データ分布の見え方を変えることができます。

binsオプションに10を設定すると、大まかな分布を確認できるようになります。

1
sns.distplot(titanic.age, bins=10)

binsオプションに30を設定すると、細かな分布を確認できるようになります。

1
sns.distplot(titanic.age, bins=30)

性別でスライシングするとまた異なった特徴をみることができます。

1
2
3
4
# 性別で重ねて比較
g = sns.FacetGrid(titanic, hue='sex', height=5)
g.map(sns.distplot, 'age', kde=False)
g.add_legend()

各年齢層ごとに男性客がやや多いことが分かります。

(実行環境としてGoogleさんのColaboratoryを使用ています。)

次回は、変数間のデータ分析を行います。

Kaggle(11) - 尺度変換

データを尺度変換すると分析しやすくなくことがあります。

例えば、25歳・38歳といった年齢の数値データから20代・30代という尺度へ変換することで各年代ごとの傾向や特徴を捉えやすくなります。

他にも価格データを「高い・普通・安い」と変換したり、レビューを「高い・普通・悪い」というような尺度へ変換することもよくあります。

ヒストグラムや散布図などで可視化した場合に、見やすくなるような粒度で変換するのがおすすめです。

尺度変換

チップのデータセットを使って尺度変換を試してみます。

まずはチップのデータセットを読み込みます。

1
2
3
4
5
import seaborn as sns
from matplotlib import pyplot as pyplot
sns.set(style='darkgrid')
tips = sns.load_dataset('tips')
tips

支払い総額(total_bill)をヒストグラムで表示します。

1
sns.distplot(tips.total_bill)

支払い総額(total_bill)を4段階の尺度に分割し、箱ひげ図で表示します。

  1. low (10未満)
  2. normal (10以上25未満)
  3. high (25以上40未満)
  4. very high (40以上)
1
2
3
4
5
6
7
8
9
10
11
12
13
def convert(x):
res = 0
if x < 10:
res = 'low'
elif x < 25:
res = 'normal'
elif x < 40:
res = 'high'
else:
res = 'veryhigh'
return res
tips['total_bill_level'] = tips.total_bill.apply(convert)
sns.boxplot(data=tips.sort_values('tip'), x='total_bill_level', y='tip')

総支払額に対するチップの金額ですが、支払い金額が多いほどチップ(の中央値)が増えていることが分かります。

総支払額が少ない場合や普通程度の場合に、チップの割合が多めになってしまうこともあるようです。(外れ値が多い)

(実行環境としてGoogleさんのColaboratoryを使用ています。)

Kaggle(10) - スライシング

データをさまざまな軸・水準で区切り、層別にすることによって意味づけされた層の特徴を理解することができるようになります。

タイタニックのデータセットの場合、性別と生存率の関係を分析する際、男女別→客室ランク別といった層に分けて分析することで女性のファーストクラス、セカンドクラスの乗客が生存しやすいといった特徴がわかるようになります。

スライシング

タイタニックデータを読み込みます。

1
2
3
4
5
6
import pandas as pd
import seaborn as sns
import numpy as np
from matplotlib import pyplot as plt

titanic = sns.load_dataset('titanic')

男女別にグループ化して、平均生存率を確認します。

1
titanic.groupby('sex').mean()

女性の生存率が74%、男性の生存率が19%と女性の生存率がかなり高いこと確認できます。

さらに客室ランク別にグループ化して、平均生存率を確認します。

1
titanic.groupby(['sex', 'class']).mean()

ファーストクラスとセカンドクラスの女性が生存率90%以上でとても高いことが確認できました。

(実行環境としてGoogleさんのColaboratoryを使用ています。)

Kaggle(9) - 要約統計量

大量の生データそのままですと特徴がつかみにくいですが、平均値・中央値・標準偏差という統計量をみることでデータの特徴が捉えやすくなります。

タイタニックのデータで言えば、乗船客の年齢では何歳くらいの人が多いのか、男性と女性で年齢層は違うのか、料金層で人数がどのように分布しているのか、ということを調べることで乗客の特徴を把握しやすくなります。

pandasではdescribe関数で統計量を出力できますが、データの種別(量的データ・カテゴリデータ・日付データ)によって表現できるものが異なります。

量的データの統計量表示

まずはタイタニックデータを読み込みます。

1
2
3
4
5
6
7
import pandas as pd
import seaborn as sns
import numpy as np
from matplotlib import pyplot as plt

titanic = sns.load_dataset('titanic')
titanic.head()

量的データの統計量を表示します。

量的データの統計量としては下記の項目が表示されます。

統計量内容
countデータの個数。欠損値はカウントされません。
mean平均値
std標準偏差
min,max最小値、最大値
パーセンタイルデフォルトで25%、50%、75%の位置を出力。50%が中央値。
1
2
# 量的(数値)データの統計量を表示
titanic.describe()

count行のageカラムを見ると、他のデータよりも数が少ないため、欠損値があることが分かります。


カテゴリーデータの統計量表示

カテゴリーデータの統計量を表示します。

カテゴリーデータの統計量を表示する場合は、describe関数にexclude=’number’を指定します。

カテゴリーデータの統計量としては下記の項目が表示されます。

統計量内容
countデータの個数。欠損値はカウントされません。
unique一意な要素の個数
top最頻値
freq最頻値の頻度
1
2
# カテゴリデータの統計量を表示
titanic.describe(exclude='number')

日付データの統計量表示

日付データの統計量を表示します。

日付データの統計量としては下記の項目が表示されます。

統計量内容
countデータの個数。欠損値はカウントされません。
unique一意な要素の個数
top最頻値
freq最頻値の頻度
first最初の日
last最後の日
1
2
3
4
5
6
# 日付データの統計量を表示
s = pd.Series([np.datetime64('2021-02-15'),
np.datetime64('2021-02-16'),
np.datetime64('2021-02-17'),
np.datetime64('2021-02-18')])
s.describe()

出力内容の変更

describe関数の返値はDataFrame形式なので、必要に応じて行や列を選択して出力することができます。

列count・meanを選択して表示します。

1
2
# count,meanのみ出力
titanic.describe().loc[['count', 'mean']]

次に性別はmaleで、列がcount・mean、行がage・fareのデータを表示します。

1
titanic[titanic.sex=='male'].describe().loc[['count', 'mean', 'std'],['age', 'fare']]

性別はfemaleで、列がcount・mean、行がage・fareのデータを表示します。

1
titanic[titanic.sex=='female'].describe().loc[['count', 'mean', 'std'],['age', 'fare']]

パーセンタイルの20%、40%、60%、80%を表示します。

1
titanic.describe(percentiles=[0.2, 0.4, 0.6, 0.8])

データの特徴や新たな仮説を得るためには、どんな切り口で分析を行うかといった視点がとても大切です。

知識や経験がものをいう分野でありますが、自分なりに統計量を表示してみて関連性を確認することが重要だと感じました。

(実行環境としてGoogleさんのColaboratoryを使用ています。)

次回は、スライシングを行います。

Kaggle(8) - データの型変換

データセットの中に分析に向かないデータがある場合、分析に適した形式に変更する必要があります。

例えば、「男」「女」のようなテキストデータの場合、seabornのヒートマップでは可視化できないため「男」→0、「女」→1のように数値へ変換します。

データセットに対してのいろいろな変換パターンがありますが、今回はデータ型の変換を実行してみます。

テキストデータから数値データへの変換

テキストデータを数値データに変換する例を次の一覧に示します。

変換前(テキスト)変換後(数値)
男/女0/1
良い/普通/悪い0/1/2
神奈川支店/千葉支店100/200

タイタニックのデータセットには性別データ(male/female)がありますので、これを数値データに変換します。

まずはデータセットを読み込み、性別のデータを表示します。

1
2
3
import seaborn as sns
titanic = sns.load_dataset('titanic')
titanic['sex'].head()

「male」を0に、「female」を1に変換するコードは次の通りです。

apply関数を使うと各データに関数を適用することができます。

1
2
df = titanic['sex'].apply(lambda x: 0 if x == 'male' else 1)
df.head()

想定通りにテキストデータを数値データに変換することができました。


数値データからテキストデータへの変換

視点や切り口を変えてデータを分析するために、数値データからテキストデータへ変換を行うことがあります。

例えば、年齢のような数値データを「幼年/少年/成人/熟年」のように変換しデータの尺度を変えると、元の数値データには見えない特徴が見えることがあります。

タイタニックの年齢データを次の一覧のようにテキストデータに変換します。

変換前(数値)変換後(テキスト)
0歳から6歳幼年
7歳から15歳少年
16歳から30歳青年
31歳から45歳壮年
46歳から60歳中年
61歳以上熟年

まずは元の年齢データを表示して数値を確認します。

1
titanic['age'].head()

年齢をテキストデータに変換するコードは下記の通りです。

再びapply関数を使いますが、条件分岐が多いためlambdaを使わず事前に関数定義を行っています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 年齢をテキストデータに変換
def trans(age):
if age < 7:
return '幼年'
elif age < 16:
return '少年'
elif age < 31:
return '青年'
elif age < 46:
return '壮年'
elif age < 61:
return '中年'
else:
return '熟年'

df = titanic['age'].apply(trans)
df.head()

年齢に応じて青年、壮年などのテキストデータが表示されます。

(実行環境としてGoogleさんのColaboratoryを使用ています。)

次回は、要約統計量の表示を行います。

Kaggle(7) - 重複データの削除

データセットには同じレコードが複数存在することがあります。(行データの重複)

またデータ名が違っているのにデータの値が全て同じという事もあります。(列データの重複)

行または列に重複するデータあるかどうかを確認し、重複がある場合は重複行/重複列を削除します。

重複行の削除

重複行のあるデータを定義します。

1
2
3
4
5
6
7
8
9
import pandas as pd
df = pd.DataFrame([['Sato', 100],
['Sato', 100],
['Suzuki', 200],
['Suzuki', 200],
['Sato', 100],
['Suzuki', 200]],
columns=['name', 'count'])
df

SatoさんとSuzukiさんの同じレコードが3件ずつありますが、同じデータが複数あっても意味がありませんので、それぞれ1レコード分に修正します。

重複行の削除はdrop_duplicates関数を使って簡単に行うことができます。

1
2
df = df.drop_duplicates()
df

3レコード分あった重複データが1レコード分に修正されました。


重複列の削除

重複列があるデータを定義します。

1
2
3
4
5
6
7
df = pd.DataFrame([['Kanagawa', 100, 987, 4, 100],
['Saitama', 200, 654, 3, 200],
['Ibaraki', 300, 321, 1, 300],
['Chiba', 400, 123, 2, 400],
['Ehime', 500, 456, 5, 500]],
columns=['area', 'salary', 'population', 'rank', 'money'])
df

salaryとmoneyが全く同じことが分かりますが、データがたくさんあると重複列があるかとうかを確認するのは大変です。

そこでヒートマップを使うと相関を可視化することができ、重複行を簡単に見つけることができます。

1
2
import seaborn as sns
sns.heatmap(df.corr(), annot=True)

ヒートマップの一番左下が相関係数1.0であり、この2つの変数が重複の可能性があることを確認できます。

ただし、相関係数が1.0であってもかならず重複列であるとは限らないので、かならず実際のデータを確認して重複列であるかどうかの最終判断を行ってください。

重複列であることを確認できたらdrop関数を使って重複列の1つを削除します。

今回はsalary列を残して、money列を削除することにします。

1
2
df = df.drop('money', axis=1)
df

money列が削除されたことが確認できました。

(実行環境としてGoogleさんのColaboratoryを使用ています。)

次回は、データの型変換を行います。

Kaggle(6) - 表記の揺れを統一する

表記の揺れとは、ある言語の文字表記において、2通り以上の書き方をされることにより、表記にばらつきが生じることです。

表記の揺れのデータ残っているとデータを正しく分類することができません。同じデータでも異なるものとして分類されてしまうからです。

そのためデータ分析前には表記の揺れを除去する必要があります。

表記の揺れの代表的なパターンは次の一覧の通りです。

表記ゆれのパターン表記ゆれの例
半角・全角カナガワ/カナガワ
漢字・カナ猫/ネコ/ねこ
送り仮名切り替え/切替
語尾ユーザー/ユーザ
数字1年/一年
英単語スピード/speed
固有名詞Macintosh/mac
大文字・小文字Kanagawa/kanagawa/KANAGAWA
省略形I would like to/I'd like to

大文字・小文字の表記の揺れを統一

大文字と小文字の表記の揺れがあるデータを定義しデータをカウントしてみます。

1
2
3
4
5
import pandas as pd
df = pd.DataFrame(['Kanagawa', 'kanagawa', 'Kanagawa',
'SAITAMA', 'saitama', 'Saitama',
'Ibaraki', 'ibaraki', 'IBARAKI'], columns=['area'])
df['area'].value_counts()

同じ地域でも別データとしてカウントされていることが分かります。

そこで、小文字表記に統一して再度データをカウントします。

1
2
df['area'] = df.area.apply(lambda x:x.lower())
df['area'].value_counts()

地域ごとに正しくカウントされるようになりました。

(実行環境としてGoogleさんのColaboratoryを使用ています。)

次回は、重複データの削除を行います。

Kaggle(5) - 上位n%・下位n%によるクリッピング

クリッピングとはデータの一部を抽出することです。

クリッピングすることで、分析に悪影響を及ぼすと考えられる外れ値を対象外とします。

(実行環境としてGoogleさんのColaboratoryを使用します。)

タイタニック データセットの読み込み

まずタイタニックのデータセットを読み込みます。

1
2
3
4
import seaborn as sns
from matplotlib import pyplot as plt
sns.set(style='darkgrid')
titanic = sns.load_dataset('titanic')

外れ値の判定

料金(fare)の統計量を確認してみます。

1
titanic.fare.describe()

平均が32、標準偏差が49、最大値が512と分布に偏りがあるようです。

次にヒストグラムを表示して、分布を可視化してみます。

1
2
plt.figure(figsize=(10,5))
sns.distplot(titanic.fare)

100以上のデータは度数が少なく偏りが大きいと見受けられますので、100未満のデータでクリッピングすることにします。

クリッピング

100未満のデータをクリッピングし、再度ヒストグラムを表示します。

1
2
3
titanic = titanic[titanic['fare'] < 100]
plt.figure(figsize=(10,5))
sns.distplot(titanic.fare)

クリッピング前よりだいぶ偏りが少なくなったようです。

最後に、クリッピング後の統計量を確認します。

1
titanic.fare.describe()

平均が22、標準偏差が20、最大値が93となり、統計量からも偏りが減少したことを確認できました。

次回は、表記の揺れの対応を行います。

Kaggle(4) - 四分位範囲による外れ値の判定

四分位範囲を使って外れ値を判断してみます。

四分位範囲とはデータを昇順にソートした状態で、25%~75%に位置にある値のことです。

この四分位範囲を1.5倍した値を、上限値・下限値として外れ値の判断基準とします。

(実行環境としてGoogleさんのColaboratoryを使用します。)

タイタニック データセットの読み込み

まずタイタニックのデータセットを読み込みます。

1
2
3
4
import seaborn as sns
from matplotlib import pyplot as plt
sns.set(style='darkgrid')
titanic = sns.load_dataset('titanic')

箱ひげ図の表示

四分位範囲による外れ値の判定は、seabornの箱ひげ図を使って確認することができます。

1
2
plt.figure(figsize=(10, 8))
sns.boxplot(data=titanic, x='pclass', y='age')

箱ひげ図は2変数の間の関係を見るので、変数によって外れ値の判断が異なります。

上の箱ひげ図では、客室ランクが2の場合は約55歳以上が外れ値となりますが、客室ランクが3の場合は約50歳以上が外れ値となります。

客室ランク以外の変数との関連をみていくとまた違った外れ値の判断基準がでてくることになります。

そのため四分位範囲による判断は目安程度と考えた方がよいかもしれません。

次回は、上位・下位n%によるクリッピングを行います。

Kaggle(3) - 外れ値の影響を抑える

データに外れ値がある場合、データ分析の結果に大きく影響を与えてしまいます。

特に平均値などの抵抗性が弱い統計量や相関係数にも影響を及ぼすので、データ分析を行う前に外れ値が存在しているかどうか確認し、必要であれば外れ値を除外する必要があります。

(実行環境としてGoogleさんのColaboratoryを使用します。)

データを作成する

例として年齢と年収のデータを作成します。

1
2
3
4
5
import pandas as pd
df = pd.DataFrame([[22,330], [26,390], [23,270], [28,820], [30,450], [31,410],
[33,1000], [32,560], [34,460], [33,510], [37,540]],
columns=['age','salary'])
df

散布図で外れ値を確認する

散布図を表示し、外れ値のあたりをつけてみます。

1
2
3
import seaborn as sns
sns.set(style='darkgrid')
sns.scatterplot(data=df, x='age', y='salary')

どうやら800万円以上の年収が外れ値のようです。

相関関係を確認する

相関係数を使って、年齢と年収の関係性の強さを調べます。

相関係数は一般的に、+1 に近ければ近いほど「強い正の相関がある」、−1 に近ければ近いほど「強い負の相関がある」、0 に近ければ近いほど「ほとんど相関がない」と評価されます。)

まずは外れ値を含んだままで相関関係を確認します。相関係数の算出にはcorr関数を使います。

1
df.corr()['age']['salary']

相関係数は0.44と相関が高くありません。

次に年収800万円を外れ値として除外し、そのデータで相関係数をとってみます。

1
2
df=df[df['salary']>800]
df.corr()['age']['salary']

相関係数が1.0となり、外れ値を除外した後は強い相関関係があることがわかります。

次回は、外れ値の判断基準として四分位範囲(しぶんいはんい)による外れ値の判定を行います。