大阪の不動産価格マップを作る
ここでは不動産価格と物件所在地の関係に注目し、コロプレス図等を用いて地域ごとの価格傾向を視化可してみます。

価格vs所在地

前回までに収集された不動産価格の分布を改めて以下に示します。

price_log.png

この価格はすべての年代における、大阪全土の物件が含まれています。 全体の傾向を知るにはこれで構いませんが、もっと詳細に、特に今回は物件価格と物件所在地の関係を見ていきます。

とにかくはじめます🍛

物件を地図上にプロット

まずは取引が活発に行われている場所を知りたいので、各物件を地図上にプロットしてみましょう。

とは言ったのものの、少なくとも以下の手順が必要です。

  1. 大阪の地図を描画する
  2. 物件の緯度・経度を求める
  3. 物件の位置を地図にプロットする

大阪の地図の描画

地図の描画方法にもいろいろ方法がありますが、今回はGeoPandasを使用してみます。

GeoPandasはPythonで地理データを扱いやすくするために開発されたpandasの拡張パッケージです。 GeoPandasに大阪の地理データを放り込めば、簡単に地図を描画できるようなります。

地図データ

地図情報は e-Stat から取得しました。

政府統計の総合窓口(e-Stat)は、各府省が公表する統計データを一つにまとめ、統計データを検索したり、地図上に表示できるなどの、たくさんの便利な機能を備えた政府統計のポータルサイトです。

e-Statは他にも面白そうなデータがありそうですが、今回使用したのはこちらのShapefileです。

Shapefileとは地理情報を扱うデータタイプの一種です。 例えば mapshaper というWebサービスを利用すれば、簡単に地形情報を確認することができます。

mapshaper.png

GeoPandasで描画

# 必要なモジュールを事前にインストールしておく
pip install geopandas descartes mapclassify

さっそくダウンロードしたShapefileGeoPandasに放り込みましょう。 と思ったのですが、Shapefileは評判が悪いようなので1、私は上記サイトで GeoJSON に変換してから読み込みを行いました。

import geopandas as gpd
osaka = gpd.read_file("osaka.geojson")
osaka[osaka.HCODE != 8154].plot()
osaka.png

いいですね。 (色や座標系を変えているのでmapshaperの結果と少し違っていますが)

なおHCODE8154の部分は水面調査区と定義されている場所のため、除外してプロットするようにしています。 各カラムや値の意味はこちらの 定義書 に記載されている通りで、面積や人口なんかも記録されていますね。

大阪市だけ描画

大阪市だけを描画したい場合は以下のようになります。

city = osaka[osaka.GST_NAME == '大阪市']
city.plot(linewidth=0.1)
# アノテーション例
pos = (city.centroid.x.mean(), city.centroid.y.mean())
ax.annotate('大阪市', pos, horizontalalignment='center')
osaka_city.png

データには町や丁目まで含まれているため、そのすべてが細かく描画されていることが確認できます。 ただし細かすぎるので、もっとざっくり、例えば大阪府を市単位で集約したい場合はどうすれば良いのでしょうか?

通常のpandasであればgroupbyを使用しますが、GeoPandasではdissolveを使って、市単位で集約してやるようです。

osaka_diss = osaka.dissolve(by='GST_NAME', aggfunc='sum')

面積や人口といった情報が集約(加算)されています。

GST_NAMEAREAJINKO
(都市名)(面積)(人口)
大阪市220,137,8382,691,185
堺市149,490,353839,310
東大阪市61,668,858502,784
枚方市65,164,856404,152
豊中市36,222,932395,479
吹田市36,166,799374,468
:::

それだけでなく、地理情報も集計されているので、以下のようにすれば大阪市だけをシンプルに描画できます。

osaka_diss.loc[['大阪市']].plot()
osaka_city_diss.png

物件の緯度・経度を求める

先ほどの地図上に各不動産の位置をプロットするためには、物件所在地に対応する緯度・経度が必要です。

しかしながら、前回収集したデータには緯度・経度は含まれていないので、物件所在地に対応する緯度・経度を自分で求める必要があります。

緯度・経度を求める

緯度・経度の取得には Google Maps PlatformGeocoding API を使用します。

このサービスは住所をインプットとして、それに対応する緯度・経度を返してくれるGoogleのWEBサービスです。

利用料金は以下が分かりやすいとかと思いますが、1,000リクエストで5ドルとなっています😕

料金表 | Google Maps Platform | Google Cloud

しかし毎月$200分は無料で利用できるので、月40,000リクエスト以内であれば無料で利用可能です😄

なお物件は60,000件近くあるため無料枠を超えそうですが、ユニークな住所は高々2,000件ほどしかありません。 というのも取引価格情報提供制度により収集されたデータは、物件を特定できないように詳細な番地レベルまでは記載されていないためです。

なお実際の取得ロジックは公式のサンプルを適当に修正すればすぐに使えるかと思います。

def geocoding(address:str):
    '''Geocoding API を呼び出す'''
    maps_key = 'APIキーを取得して貼り付けてください'
    base_url = 'https://maps.googleapis.com/maps/api/geocode/json'

    # This joins the parts of the URL together into one string.
    url = base_url + '?' + urllib.parse.urlencode({
        'address': address,
        'key': maps_key,
    })

    # Get the API response.
    response = urllib.request.urlopen(url).read()
    # If we didn't get an IOError then parse the result.
    result = json.loads(response)
    if result['status'] == 'OK':
        return result
    elif result['status'] != 'UNKNOWN_ERROR':
        # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
        # ZERO_RESULTS. There is no point retrying these requests.
        raise Exception(result)

def get_lat_lng(address:str):
    '''緯度経度を取り出す'''
    res = geocoding(address)
    latlng = res['results'][0]['geometry']['location']
    return latlng['lat'], latlng['lng']

物件の位置を地図にプロットする

あとはpandasGeoPandasに変換してからプロットするだけです。2

# 変換
lon = df.lon.values
lat = df.lat.values
geo_df = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(lon, lat))
# 描画(同じaxに)
_, ax = plt.subplots()
osaka_diss.plot(ax=ax)
geo_df.plot(ax=ax)
point_on_map.png

これで取引されている位置をだいたい把握することができました。

物件価格を表現

応用編として物件の色で価格帯を表現してみました。 さらに同じ位置に物件が多く重なってしまうので(詳細な住所情報がないため)、正規分布で少し散らしています。

point_on_map_with_color.png

なんと無く地域による価格帯の違いが分かりますが、ちょっとわかりにくいですね。

高額物件はどこに?

ちなみに1億円越えの物件だけプロットすると、大阪市に集中しているのが良く分かったりします。

point_on_map_with_color_100m.png

コロプレス図で表現

地域による物件価格の違いを表現するにはコロプレス図を使う方が分かりやすいです。

例えばコロプレス図で人口密度を描くと、先ほどの物件位置のプロットと対応しており、人口密度が高いほど取引も活発なことが良く分かりますね。 (色が白い部分ほど人口密度が高いエリアです)

p_density.png

平均物件価格

コロプレス図により物件価格に応じて、各地域を塗り分けてみると価格分布の特徴が分かりやすくなりました。 (グレー部分は取引が無いためデータ無しを意味しています)

これぞコロプレス図です。

price_choropleth.png

平均が2,000万円を超えている地域を並べると、こんな様子です。 (ただしかなり裾の長い分布であることを思い出してください)

都市名平均価格[円]
大阪市阿倍野区24,036,978
大阪市北区23,656,365
大阪市福島区23,099,096
大阪市天王寺区22,858,523
豊中市22,804,468
高槻市21,883,620
吹田市21,873,886
池田市21,703,939
大阪市鶴見区21,113,947
大阪市西区20,937,001
大阪市中央区20,921,137
大阪市都島区20,600,894
茨木市20,595,151
箕面市20,542,278

なお基本的にはcolumn引数に色分けしたい対象のカラム名を渡すだけで、コロプレス図は描けてしまいます。

bukken.plot(column='price', scheme='FisherJenks', k=5, cmap="YlOrRd_r", legend=True)

10年以上前だと…

多少の違いはありますが、だいたいの傾向は同じようです。 (この微妙な違いに、何か面白い気配を感じるものの今回はスキップ)

price_choropleth_2007.png

平均上昇幅

次に各地域での伸び率を知りたくなったので、2006年から2019年までで上昇幅をプロットしてみます。 ここでいう上昇幅とは、線形回帰した際の傾きを意味します。 ただし次の記事で見るように、必ずしも線形的に上昇しているわけでは無いので、あくまで参考程度で。

price_choropleth_coef.png

平均上昇幅が50万以上の都市は以下となりました。

都市名平均上昇[円]
大阪市天王寺区886,317
大阪市福島区808,060
摂津市649,487
大阪市西区590,089
高槻市572,935
大阪市中央区513,152
大阪市阿倍野区506,711

まとめ

今回は地図上へのプロットを中心に行ってみましたが、GeoPandasを使用すると比較的簡単に地図が扱えるとことが分かりました。 実は地図の座標系などをちゃんと考えるとかなり面倒なのですが、今回はあまり気にする必要はないかと思います。

参考


  1. 第22回オープンデータトーク 地理データ形式のこれから ↩︎

  2. Creating a GeoDataFrame from a DataFrame with coordinates - GeoPandas 0.6.0 documentation ↩︎

最終更新:2019/12/17 08:53 (Tue)