目標3はデータの特徴を可視化することです。データの分布や項目同士の関係を可視化するために、以下のようなグラフを描いてみましょう。
箱ひげ図はデータの範囲や外れ値を見るため、ヒストグラムは度数の分布を見るため、散布図は関係を見るために使われます。
グラフを描く方法については、すでにこの連載の第5回でmatplotlib.pyplotモジュールの関数を使う例を紹介しています。同じことをやっても仕方がないので、ここでは、pandasデータフレームのグラフ描画メソッドを使うことと、どのように結果を見るかということに重点を置くことにします。では、1つずつ見ていきましょう。
箱ひげ図を作る方法は幾つかありますが、ここでは、以下の2つの方法でやってみます*3。
*3 pandas.plottingモジュールのboxplot関数にデータフレームまたはSeriesを指定しても箱ひげ図の描画ができます。
まず、データフレームのboxplotメソッドを使って表示してみます。ただし、築年数と世帯年収をまとめて指定すると、単位が異なるにも関わらず同じ描画領域の中にグラフが表示されてしまいます(リスト17、図11)。
import pandas as pd
df = pd.read_csv('./sample_data/california_housing_train.csv')
bp = df[['housing_median_age', 'median_income']].boxplot()
見やすいグラフにするために、築年数と箱ひげ図と世帯年収の箱ひげ図を個別に描いて並べるようにしておきましょう。データフレームのboxplotメソッドやSeriesのplot.boxメソッドは、matplotlib.pyplotモジュールのboxplot関数のラッパー(複雑な部分を隠して使いやすくしたもの)となっているので、お手軽にグラフを描くのに便利です。しかし、複数のグラフを並べるなど、細かな設定を行うためにはmatplotlib.pyplotモジュールの関数が必要になることもあります(リスト18、図12)。
# リスト17の続きに入力する
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 6))
# 築年数の箱ひげ図
plt.subplot(1, 2, 1) # 1行2列の1つ目
bp1 = df.housing_median_age.plot.box(grid=True)
# 世帯収入の箱ひげ図
plt.subplot(1, 2, 2) # 1行2列の2つ目
bp2 = df.median_income.plot.box(grid=True)
図12はリスト18を実行して描画された箱ひげ図と、その見方を合わせて示したものです。
図12 リスト18の実行結果と箱ひげ図の見方(築年数と世帯年収の箱ひげ図を並べて描画)箱ひげ図を見ると、築年数の分布にはあまり偏りがないように見えます。ただし、度数(値の個数)が反映されていないので、中央に山がある正規分布のような形になっているかどうかはこれだけでは分かりません(これについては後述します)。一方、世帯年収の方は、小さい方に値が集中していることが分かります。○マークが幾つも重なって表示されているので、極端に離れた外れ値がポツンと存在するのではなく、小さい値の「山」の「裾」を右に引きずるような形で大きな値が並んでいることが分かります。
箱ひげ図だけでは、分布の形がよく分からないので、ヒストグラムを作ってみましょう。ここでは世帯年収のヒストグラムだけを描きます(築年数のヒストグラムの描画と考察については練習問題で取り上げることとします)。ヒストグラムを作るにはデータフレームやSeriesのplot.histメソッドなどを使う方法がありますが、ここでは、単一の列のヒストグラムを作成するので、Seriesのplot.histメソッドを使ってやってみます(リスト19、図13)。
# リスト18の続きに入力する
hist = df.median_income.plot.hist()
基本統計量を求めたところで平均値と中央値の関係を紹介しましたが、世帯年収については平均値が3.88万ドル、中央値が3.54万ドルとなっており、平均値>中央値なので、左に山のある分布だろうと推測されました。図13を見れば確かにそうなっていることが分かります。
機械学習(回帰や分類)を行う場合には、分布が偏っていると予測の精度が上がらないことがあります。図13のように左に山のある分布の場合、各データの対数を取ると正規分布に近い形になります。機械学習では、そのように変換した値を使うこともよくあります*4。plot.histメソッドではX軸やY軸を対数変換したグラフが描けるので、その形を見てみましょう(リスト20、図14)。
*4 機械学習のためのライブラリscikit-learnでは、分布の歪みを正規分布に近い形に変換するためのツールとして、sklearn.preprocessing.PowerTransformerクラスが用意されており、yeo-johnson変換やbox-cox変換と呼ばれる方法が使えます。また、値が一定の範囲に収まるように変換する正規化や標準化も行われます。例えば、最小値が0、最大値が1になるように変換するにはMinMaxScalingクラスを使い、平均が0、分散が1の正規分布になるように変換するにはStandardScalingクラスを使います(後述します)。
# リスト19の続きに入力する
hist = df.median_income.plot.hist(logx=True, bins =30)
階級の個数によっては、ヒストグラムの形が違って見えることがありますが、大まかな分布を把握することはできます。なお、階級の個数を幾つにするかは、スタージェスの公式(log2n+1)によって求めた値が目安とされることがあります。ここでの例であれば、n=17000なので、log217000+1 ≈ 15となります。
これまでのお話は、個別の列(変数)について特徴を見るものでした。列同士の関係についてはそれだけでは分かりません。すでに求めた相関行列は列同士の関係を表すものですが、その特徴がよく分かるように散布図を作成して列同士の関係を可視化してみましょう。散布図は、データフレームのplot.scatterメソッドを使えば簡単に描画できますが、全ての変数同士の散布図を一度に描画するにはpandasのplotting.scatter_matrix関数が便利です(リスト21)。
# リスト20の続きに入力する
sc = pd.plotting.scatter_matrix(df, figsize=(12, 12))
plotting.scatter_matrix関数では、データフレームの列数やデータの件数が多くなると処理にかなりの時間がかかります(図15の結果を得るのに、筆者の環境では15秒かかりました)。同じ列同士については、散布図ではなくヒストグラムが描かれるので、分布も合わせて全体像を見るのにも便利です。
ちなみに、個々の列同士の散布図を描画するには、データフレームのplot.scatterメソッドを使って、
sc = df.plot.scatter('median_income', 'median_house_value')
のように書きます。
この記事では、個々のヒストグラムを描画する例を先に紹介しましたが、実際には、最初に図15のような全てのグラフを作成しておき、気になる箇所について詳細に見ていく、といった順序で分析を進めていったほうがいいでしょう。例えば、全体を眺めてみると、住宅価格のヒストグラム(図15の右下)がどうも不自然です。右端(500,000ドルあたり)の値がやけに多いですね。これはおそらく、値の大きなデータをひとまとめにしてしまったからだと思われます。築年数についても、箱ひげ図では偏りがなかったように見えましたが、山が2つあるようにも見えます。これらの項目は詳細に見ていく必要がありそうです(目標4で重回帰分析を行うときに見ることにします)。
図15を見ると、経度と緯度に山が2つあることが分かります。これは、住宅が集中して存在する場所(=大都市)があるということです。大都市ほど人口や世帯収入が大きいと考えられ、それらと住宅価格には正の相関があるので、地域によって住宅価格の差があることも想像できます。そこで、地図上で住宅データの分布と住宅価格を色分けして表示してみましょう(リスト22、図16)。
データを地図上にマッピングするにはGeoPandasライブラリが便利です。詳細はこちらのサイトを参照してください。リスト22を実行するには、あらかじめ以下のコードを実行しておき、geopandasモジュールをインストールしておく必要があります。
!pip install geopandas
地図データはU.S. Geological Surveyのサイトの「Type: Download」の下のリンクなどから入手できるので、その地図に住宅データをマッピングしていきます。上記のサイトからダウンロードした地図データのファイルはzipファイルとなっているので、それを展開して得られる「GU_CountyOrEquivalent」というファイル(名前が同じで拡張子が異なる.dbf、.prj、.shp、.shxとなっている4つのファイル)をGoogle Colabで利用できるようにしておきましょう。そのためには以下のコードを実行しておくのが簡単です(実行にはやや時間がかかります。複数回実行すると、同じファイルが何度もダウンロードされるので、実行は1回だけで構いません)。
!wget https://prd-tnm.s3.amazonaws.com/StagedProducts/GovtUnit/Shape/GOVTUNIT_California_State_Shape.zip
!unzip GOVTUNIT_California_State_Shape.zip
準備ができたらリスト22のコードを実行してみましょう。図17のような地図が表示されるはずです。
import pandas as pd
import geopandas as gpd
from mpl_toolkits.axes_grid1 import make_axes_locatable
# データを読み込む
df = pd.read_csv('./sample_data/california_housing_train.csv')
# 地図データを読み込む
map = gpd.read_file('./Shape/GU_CountyOrEquivalent.shx') # データはあらかじめGoogle Colabで利用できるようにしておく
# 経度と緯度の列を位置データに変換し、データフレームの末尾に追加する
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.longitude, df.latitude))
# 地図を描く
ax = map.plot(color='aliceblue', edgecolor='black', figsize=(8, 8))
# 凡例(カラーバー)を描く領域を用意する
divider = make_axes_locatable(ax) # 描画領域を追加できるようにする
cax = divider.append_axes('right', size='5%', pad='2%') # 右に、5%の大きさ、2%の空き
# 住宅データをプロットする(axが地図、caxがカラーバー)
gax = gdf.plot(column='median_house_value', ax=ax, cax=cax, legend=True, marker='.', markersize=10)
図16 リスト22の実行結果(住宅を地図上にブロット)カラーバーで示されているように、黄色で表示されている地域の住宅価格が高くなっています。住宅価格の高い地域は、サンフランシスコ、サンノゼあたり(左上の黄色い部分)とロサンゼルス、サンディエゴあたり(右下の黄色い部分)です。ちなみに、これらの都市はカリフォルニア州の人口の多い都市の上位4つになっています。
Copyright© Digital Advantage Corp. All Rights Reserved.