GIS奮闘記

元GISエンジニアの技術紹介ブログ。主にPythonを使用。

スポンサーリンク

VSCode Map Preview を使って テキストエディタ上で地図を表示してみよう!

さて、本日はテキストエディタ上で地図を表示してみようと思います。使用するテキストエディタは Visual Studio Code です。

Visual Studio Code とは

Visual Studio CodeはMicrosoftが開発しているWindows、Linux、macOS用のソースコードエディタである。デバッグ、埋め込みGitコントロールとGitHub、シンタックスハイライト、インテリジェントなコード補完 、スニペット、コードリファクタリングのサポートが含まれる。(出典:Wikipedia)

VSCode Map Preview とは

Visual Studio Code で使用できるプラグインで、様々な地図データを扱うことができます。

marketplace.visualstudio.com

対応フォーマット

  • CSV files (as of 0.5.0)
  • GPX
  • GeoJSON
  • IGC
  • KML
  • TopoJSON
  • WFS
  • GML
  • GML2
  • GML3
  • WKT

使い方

まずは VSCode Map Preview をインストールします。

f:id:sanvarie:20210512111345p:plain

今回はこちらのサイトでダウンロードした日本地図の GeoJSON を使用してみます。対象の地図データをドラッグアンドドロップし、「Map Preview」ボタンを押します。

f:id:sanvarie:20210512112100p:plain

このように地図が表示されます。

f:id:sanvarie:20210512112202p:plain

属性を表示させることもできますね。

f:id:sanvarie:20210512112246p:plain

また、背景地図を変えることもできます。

f:id:sanvarie:20210512112322p:plain

さいごに

一昔前は地図データを扱うのは少し敷居が高かった気がしますが、最近は本当に色々なソフトで地図データの可視化を行うことができるようになっている気がします。これからさらにリッチな機能が追加されていくでしょうし、少し地図データを使うだけなら高価な GIS ソフトを買う必要は無くなりましたね。

機械学習で行った定期預金申し込み推論結果を Tableau で可視化してみよう!

さて、本日は機械学習で行った定期預金申し込み推論結果を Tableau で可視化してみようと思います。機械学習を行うことも難しいのですが、その結果をわかりやすく表示することも多くの方が頭を抱える悩みではないかと思います。Tableau を使えばそんな悩みもすぐに解決するのではないかと思うので、ぜひ一緒に使ってみましょう!

今回使用する機械学習アルゴリズム

今回はランダムフォレストを使用します。

ランダムフォレストは2001年に Leo Breiman によって提案された[1]機械学習のアルゴリズムであり、分類、回帰、クラスタリングに用いられる。決定木を弱学習器とするアンサンブル学習アルゴリズムであり、この名称は、ランダムサンプリングされたトレーニングデータによって学習した多数の決定木を使用することによる。ランダムフォレストをさらに多層にしたアルゴリズムにディープ・フォレストがある。対象によっては、同じくアンサンブル学習を用いるブースティングよりも有効とされる。(出典:Wikipedia)

関連エントリー

興味がある方はぜひ読んでみてください。

www.gis-py.com

www.gis-py.com

今回使用するデータ

2008年から2011年の間のポルトガルの銀行顧客の行動履歴、経済指標と、その顧客が実際に定期預金を申し込んだかどうかの情報を使用します。以下サイトの「 bank-additional-full.csv」を使用します。

archive.ics.uci.edu

開くとこんな感じになっています。データがすごくきれいに整っているので、加工の必要は無さそうですね。

f:id:sanvarie:20210508193647p:plain

注意

このデータですが、「;」が区切り文字になっています。色々都合が悪かったので、「,」に変換しました。

フィールド

データに関しては以下を参照してください。全てのフィールドは使用せずに必要なもののみをピックアップして機械学習を行おうと思います。

フィールド名 説明
age 年齢
job 職業
marital 未婚・既婚
education 教育水準
default 債務不履行があるか
housing 住宅ローン有無
loan 個人ローン有無
contact 連絡方法
month 最終コンタクト月
day_of_week 最終コンタクト日
duration 最終コンタクト時間(秒)
campaign 現キャンペーンにおけるコンタクト回数
pdays 経過日数:全キャンペーンコンタクト後の日数
previous 現キャンペーン前のコンタクト回数
poutcome 前回キャンペーンの成果
emp.var.rate 雇用変動率
cons.price.idx 消費者物価指数
cons.conf.idx 消費者信頼感指数
euribor3m 欧州銀行間取引金利
nr.employed 被雇用者数
y 定期預金申し込み有無

環境

Windows10 64bit
Python3.8.5
Tableau Desktop Public Edition 2021.1.0

手順

  1. データ理解
  2. モデルの作成
  3. 推論の実施

1.データ理解

住宅ローン有無ごとの定期預金申し込み比率を確認しましたが、あまり差はなさそうですね。

f:id:sanvarie:20210509083647p:plain

コンタクト時間での申し込み比率を確認しました。コンタクト時間が長くなれば申し込み比率があがることがわかります。

f:id:sanvarie:20210509084306p:plain

コンタクト回数とコンタクト時間の相関関係を確認します。コンタクト回数が少なくコンタクト時間が長いと申し込みされる傾向にあることがわかります。

f:id:sanvarie:20210509084507p:plain

2.モデルの作成

上記で確認したデータを使用して学習モデルを作成します。

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

df = pd.read_csv(r"D:\data\machine-learning\bank-additional\bank-additional-full.csv")

# 出力変数
y = df["y"]

# 入力変数
x = df.iloc[:,0:-1]

# ダミー変数化
x = pd.get_dummies(x)
y = pd.get_dummies(y)

# 訓練データと検証データに分割
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=0)

# モデル作成
forest = RandomForestClassifier(max_depth = 10, n_estimators=50, max_features = 25, random_state=0)

# 訓練データをモデルに学習させる
forest.fit(x_train, y_train)

訓練データに対する精度とテストデータに対する精度を確認します。

print(forest.score(x_train, y_train))
print(forest.score(x_test, y_test))

訓練データ「0.9458094590657473」、テストデータ「0.9205593862289987」という結果でした。かなりいい感じだと思います。

ハイパーパラメータの調整

今回使用する RandomForestClassifier に対するパラメータ「max_depth」「 n_estimators」「max_features」ですが、「GridSearchCV」を使用してベストな値を確認しました。

  • max_depth・・・一つ一つの木の深さ
  • n_estimators・・・ランダムフォレストで使用する決定木の数
  • max_features・・・使用する特徴量
from sklearn.model_selection import GridSearchCV
estimator =RandomForestClassifier()

param_grid = {"max_depth": [1,10,25,50],
              "n_estimators": [1,10,25,50],
              "max_features": [1,10,25,50]}

cv =5

x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=0)
tuned_model =GridSearchCV(estimator=estimator, param_grid=param_grid)

tuned_model.fit(x_train, y_train)
pd.DataFrame(tuned_model.cv_results_).T

このような表で最適なハイパーパラメータの組み合わせを確認することができます。

f:id:sanvarie:20210509094847p:plain

ただ、表で確認せずとも best_params_ という値に最適なハイパーパラメータが入っているので、確認してみましょう。

tuned_model.best_params_

以下のような結果になりました。この結果を上記の「モデルの作成」のコードで使用しています。

f:id:sanvarie:20210509094955p:plain

後続の処理で使用するので、best_estimator_ に入っている最適なモデルを引き継ぎましょう。

best_model = tuned_model.best_estimator_ 

3.推論の実施

作成されたモデルを使って新しいデータに対して推論を実施します。

サンプルデータ作成

推論用にサンプルデータを作成します。

import pandas as pd

df_original = pd.read_csv(r"D:\data\machine-learning\bank-additional\bank-additional-full.csv")
df_original.iloc[:, :] = df_original.iloc[:,:].sample(frac=1).reset_index(drop=True)
df_original.to_csv(r"D:\data\machine-learning\bank-additional\sample.csv")

元データ f:id:sanvarie:20210509103446p:plain

サンプルデータ f:id:sanvarie:20210509103503p:plain

サンプルデータから予測値を算出、結果をCSVとして出力し、Tableau で結果を確認します。

# サンプルデータ読込
sample = pd.read_csv(r"D:\data\machine-learning\bank-additional\sample.csv")

# 入力変数
sample_x = sample.iloc[:,1:-1]

# ダミー変数化
sample_x = pd.get_dummies(sample_x)

# 最適なモデルから予測値を取得
y_new = best_model.predict_proba(sample_x)

a = []
for i in y_new:
    for ii in i:
        a.append(ii[1])

df_y_new = pd.DataFrame(a, columns=["predict"])

sample_predict = pd.concat([sample,df_y_new],axis=1)

# CSV出力
sample_predict.to_csv(r"D:\data\machine-learning\bank-additional\sample_predict.csv")

Tableau で結果を確認

職業別ごとの申し込み確率

admin が申し込み確率が高いことがわかります。

f:id:sanvarie:20210509121000p:plain

コンタクト時間ごとの申し込み確率

コンタクト時間はおおよそ5分以内にした方がよいことがわかります。

f:id:sanvarie:20210509121528p:plain

個人ローン有無ごとの申し込み確率

個人ローンを組んでいない顧客の方が申し込み確率が高くなることがわかります。

f:id:sanvarie:20210509121734p:plain

住宅ローン有無ごとの申し込み確率

住宅ローンを組んでいる方がやや申し込み確率が高くなることがわかります。

f:id:sanvarie:20210509122002p:plain

最終コンタクト月ごとの申し込み確率

なぜか最終コンタクト月が5月だと申し込み確率が高くなっています。興味深いですね。

f:id:sanvarie:20210509122403p:plain

さいごに

いかがでしたでしょうか。ランダムフォレストを使うことによってかなり高い精度で定期預金申し込みの推論ができたかと思います。ここに詳細な顧客データなどを結合して Tableau で確認してみるともっと面白い結果が見れたのではないかと思います。機械学習については引き続き紹介していきたいと思います。本日は以上です。

Python での探索的データ解析には pandas-profiling を使おう

さて、本日は pandas-profiling について紹介してみようと思います。機械学習をやっていると避けて通れないのが探索的データ解析ですが、かなり面倒な作業ですよね。ただ、pandas-profilign を使えばすごく簡単に探索的データ解析を行うことができます。

探索的データ解析とは

統計では、探索的データ分析は、データセットを分析して主な特性を要約するアプローチであり、多くの場合、統計グラフィックスやその他のデータ視覚化手法を使用します。(出典:Wikipedia)

pandas-profiling とは

Pandas のデータフレームを読み込んでそのデータに対して探索的データ解析を行ってくれるライブラリです。詳細は以下を参照してください。

github.com

必要なライブラリ

pandas と pandas-profiling をインストールしてください。

環境

Windows10 64bit
Python3.8.5

使用するデータ

2008年から2011年の間のポルトガルの銀行顧客の行動履歴、経済指標と、その顧客が実際に定期預金を申し込んだかどうかのデータを使用します。以下サイトの「 bank-additional-full.csv」を使用します。

archive.ics.uci.edu

開くとこんな感じになっています。このデータですが、「;」が区切り文字になっていたので「,」に変換しました。

f:id:sanvarie:20210508193647p:plain

サンプルコード

import pandas as pd
import pandas_profiling as pdp

df = pd.read_csv('bank-additional-full.csv')
pdp.ProfileReport(df, title="Pandas Profiling Report")

これだけです。めちゃめちゃ簡単ですね。

概要

f:id:sanvarie:20210508202738p:plain

各フィールドの情報

f:id:sanvarie:20210508202811p:plain

f:id:sanvarie:20210508202834p:plain

選択したカラムのヒートマップ

f:id:sanvarie:20210508203700p:plain

f:id:sanvarie:20210508203719p:plain

相関関係

f:id:sanvarie:20210508203152p:plain

サンプルデータ

f:id:sanvarie:20210508203251p:plain

さいごに

pandas-profiling を使うことでこんなに簡単に探索的データ解析ができることがわかりました。ぜひ使ってみてください。本日は以上です。

Prophet で時系列解析を行い電力需要の推論値を Tableau で可視化してみよう!

さて、本日は Prophet を利用して電力需要の推論値を可視化してみようと思います。前回に引き続き今回も機械学習に関するエントリーを書いてみました。機械学習に関してはまだまだ学習中の身で拙い内容かもしれませんがこれから機械学習を始めてみようという方の一助になれたら幸いです。

今回チャレンジすること

気象情報を利用した東京都の電力需要推論

使用する機械学習ライブラリ

Prophet というライブラリを使用します。Prophet は Facebook が公開してるライブラリで、簡単に時系列予測が行え、トレンドや季節性などが構造化された結果を得ることができます。 なお、インストール前に PyStan というライブラリもインストールする必要があります。Windows 環境でのインストール方法は以下エントリーで紹介していますので、興味のある方はぜひ読んでみてください。

www.gis-py.com

使用するデータ

f:id:sanvarie:20210505060552p:plain

f:id:sanvarie:20210506105040p:plain

  • 気象データ(気象庁) ・・・以下エントリーでスクレイピングした東京都の2016年1月1日~2020年12月31日までのデータを使用します(weather.csv)。

www.gis-py.com

f:id:sanvarie:20210506104218p:plain

環境

Windows10 64bit
Python3.8.5
Tableau Desktop Public Edition 2021.1.0

手順

  1. データ加工
    1. 電力データに対する処理
      1. 電力データを一つのCSVにまとめる
      2. 「DATE」「TIME」カラムを結合して「DATE_TIME」カラムを作成
    2. 気象データに対する処理
      1. 24時を0時に変換
      2. weather.csv の 「年月日」「時間」カラムを結合して「年月日_時間」カラムを作成し weather_data.csv として CSV 出力
  2. データの確認
  3. モデルの作成
  4. モデルの評価

1.データ加工

電力データに対する処理

# -*- coding: utf-8 -*-
import os
import glob
import pandas as pd

data_dir = r"D:\data\machine learning"
file_name = "union_electricity_data.csv"

df_list = []

# 電力データ検索
for data in glob.glob(r"D:\data\machine learning/?????????.*"):
    df = pd.read_csv(data, encoding = "cp932")
    df_list.append(df)

# CSVをマージ
df_concat = pd.concat(df_list)

# DATE_TIME列作成
df_concat["DATE_TIME"] = pd.to_datetime(df_concat["DATE"].str.cat(df_concat["TIME"], sep=" "))

# 出力
df_concat.to_csv(os.path.join(data_dir, file_name), index=False, encoding = "utf-8")

以下のようになりました。

f:id:sanvarie:20210506115500p:plain

f:id:sanvarie:20210506115528p:plain

気象データに対する処理

# -*- coding: utf-8 -*-
import os
import pandas as pd

data_dir = r"D:\data\machine learning"
file_name = "weather.csv"

# CSV 読込
df = pd.read_csv(os.path.join(data_dir, file_name), encoding = "cp932")

# 24時を0時に変換
df["時間"] = df["時間"].replace(24, 0)

# 年月日_時間カラム作成
df["年月日_時間"] = df["年月日"].str.cat(df["時間"].astype(str), sep=" ") + ":00:00"
df["年月日_時間"] = pd.to_datetime(df["年月日_時間"])

# 出力
df.to_csv(os.path.join(data_dir, "weather_data.csv"), index=False, encoding = "utf-8")

以下のようになりました。

f:id:sanvarie:20210506122043p:plain

2.データの確認

加工したデータを Tableau で確認します。

f:id:sanvarie:20210507072157p:plain

2018年のデータに絞って表示してみました。これを見ると夏場と冬場に電力消費量が高くなるのがわかります。

f:id:sanvarie:20210507073319p:plain

f:id:sanvarie:20210507073452p:plain

曜日と時間帯でもデータを確認してみると、平日の日中に電力使用量が多いことがわかります。

f:id:sanvarie:20210507073806p:plain

上段が電力使用量、下段が気温です。気温が低いもしくは高いときに電力使用量が多くなる傾向にある気がしますね。

f:id:sanvarie:20210507074144p:plain

3.モデルの作成

Prophet では時刻や日時を表すカラムをds、推論したい値をyという名前に変更する必要があるので、まずはカラム名を変更します。また、ここで不要なカラムの削除と2016年~2019年のデータを学習データとして抽出します。

import pandas as pd

# 電力データを読み込み
df = pd.read_csv(r"D:\data\machine learning\union_electricity_data.csv")

# 予測に不要なカラムを削除
df = df.drop(["DATE", "TIME"], axis=1)

# 2016年~2019年のデータに絞る
df = df.query("DATE_TIME >= '2016-04-01' and DATE_TIME <='2019-12-31'")

# カラム名変更(Prophet対応)
df = df.rename(columns={"DATE_TIME":"ds","実績(万kW)":"y"})

df.head()

f:id:sanvarie:20210507075226p:plain

上記で抽出した2016年~2019年のデータを使用して2020年の電力使用量を推論し結果を CSV 出力します。

from fbprophet import Prophet

data_dir = r"D:\data\machine learning"
model = Prophet()
model.fit(df)

# 24サンプル×365日(予測したい2020年の日数)
future = model.make_future_dataframe(24*365, freq="H")

# 推論
forecast = model.predict(future)

# CSVに出力
forecast.to_csv(r"D:\data\machine learning\future.csv", index=False, encoding = "utf-8")

4.モデルの評価

前項で出力した future.csv を Tableau で可視化してみます。電力データ、気象データと結合します。

f:id:sanvarie:20210507082925p:plain

オレンジが2020年の推論値で青が実績値です。夏場と冬場に電力使用量が多くなる傾向はしっかり推論できている感じですね。

f:id:sanvarie:20210507083649p:plain

誤差の確認のため、計算フィールドとして「MAPE」を作成します。

f:id:sanvarie:20210507084046p:plain

MAPE とは

平均絶対パーセント誤差は、平均絶対パーセント偏差とも呼ばれ、統計における予測方法の予測精度の尺度です。通常、精度は次の式で定義される比率として表されます(出典:Wikipedia)。

シートのタイトルに MAPE を表示させました。MAPE=8.416%とということがわかりました。まぁ悪くはない結果でしょうか。

f:id:sanvarie:20210507092333p:plain

もう少し詳細を確認するために計算フィールドとして「誤差」を作成します。

f:id:sanvarie:20210507092657p:plain

下段が誤差の表示です。夏と冬を中心に大きな誤差が出てしまっていますね。

f:id:sanvarie:20210507092754p:plain

猛暑日や真冬日などのデータを追加して学習すればもう少し誤差を小さくすることができそうな気がします。

さいごに

Prophet を使うことで時系列解析がすごく簡単にできることがわかりました。難しいのはそこまでにどのようにデータを整備すればという部分でしょうか。今回は簡単な分析になってしまったのですが、祝日やお盆など不定期なイベントのデータを Prophet に読み込ませた方がより正確な推論ができるかと思います。あとはもっと詳細な気象データを使うことで今まで見えてこなかった傾向が見えるかもしれません。機械学習に関しては今後も本ブログで紹介したいと思います。本日は以上です。

気象データを Python でスクレイピングする方法

さて、本日は気象庁の HP から気象データをスクレイピングしようと思います。

スクレイピングとは

ウェブスクレイピングとは、ウェブサイトから情報を抽出するコンピュータソフトウェア技術のこと。ウェブ・クローラーあるいはウェブ・スパイダーとも呼ばれる。 通常このようなソフトウェアプログラムは低レベルのHTTPを実装することで、もしくはウェブブラウザを埋め込むことによって、WWWのコンテンツを取得する。(出典:Wikipedia)

関連エントリー

過去のエントリーで他サイトのスクレイピングについて書いていますので興味のある方はぜひ参考にしてみてください。

www.gis-py.com

www.gis-py.com

事の発端

以下のサイトから東京の一時間ごとの気象データをダウンロードしようとしたのですが、一回のダウンロードの容量制限があり数年間分のデータをダウンロードするのは少し面倒だなと思い今回のエントリーに至りました。

www.data.jma.go.jp

f:id:sanvarie:20210505091011p:plain

スクレイピングするサイト

上記のダウンロードサイトではなく過去の気象データ検索のサイトを利用してスクレイピングを行おうと思います。

www.data.jma.go.jp

f:id:sanvarie:20210505092226p:plain

使用するライブラリ

Beautiful Soup というライブラリを使用します。Beautiful Soupは、HTMLおよびXMLドキュメントを解析するためのPythonパッケージです。 HTMLからデータを抽出するために使用できる解析済みページの解析ツリーを作成します。これはWebスクレイピングに役立ちます。(出典:Wikipedia)

ダウンロードするデータ

今回は以下データをダウンロードします。場所、期間を変えたいという方は掲載するサンプルコードを少し変えれば問題ないかと思います。ただ、データの単位を変えたい場合は少し工夫が必要になりますが今回はそれについては割愛します。

  • 場所・・・東京
  • 期間・・・2016年~2020年
  • データの単位・・・時間ごと

環境

Windows10 64bit
Python3.8.5

サンプルコード

気象データをスクレイピングするサンプルコードです。

# -*- coding: utf-8 -*-
import os
import datetime
import csv
import urllib.request
from bs4 import BeautifulSoup

def str2float(weather_data):
    try:
        return float(weather_data)
    except:
        return 0

def scraping(url, date):

    # 気象データのページを取得
    html = urllib.request.urlopen(url).read()
    soup = BeautifulSoup(html)
    trs = soup.find("table", { "class" : "data2_s" })

    data_list = []
    data_list_per_hour = []

    # table の中身を取得
    for tr in trs.findAll('tr')[2:]:
        tds = tr.findAll('td')

        if tds[1].string == None:
            break;

        data_list.append(date)
        data_list.append(tds[0].string)
        data_list.append(str2float(tds[1].string))
        data_list.append(str2float(tds[2].string))
        data_list.append(str2float(tds[3].string))
        data_list.append(str2float(tds[4].string))
        data_list.append(str2float(tds[5].string))
        data_list.append(str2float(tds[6].string))
        data_list.append(str2float(tds[7].string))
        data_list.append(str2float(tds[8].string))
        data_list.append(str2float(tds[9].string))
        data_list.append(str2float(tds[10].string))
        data_list.append(str2float(tds[11].string))
        data_list.append(str2float(tds[12].string))
        data_list.append(str2float(tds[13].string))

        data_list_per_hour.append(data_list)

        data_list = []

    return data_list_per_hour

def create_csv():
    # CSV 出力先ディレクトリ
    output_dir = r"D:\blog\data\weather"

    # 出力ファイル名
    output_file = "weather.csv"

    # データ取得開始・終了日
    start_date = datetime.date(2016, 1, 1)
    end_date   = datetime.date(2020, 12, 31)

    # CSV の列
    fields = ["年月日", "時間", "気圧(現地)", "気圧(海面)",
              "降水量", "気温", "露点湿度", "蒸気圧", "湿度",
              "風速", "風向", "日照時間", "全天日射量", "降雪", "積雪"] # 天気、雲量、視程は今回は対象外とする

    with open(os.path.join(output_dir, output_file), 'w') as f:
        writer = csv.writer(f, lineterminator='\n')
        writer.writerow(fields)

        date = start_date
        while date != end_date + datetime.timedelta(1):

            # 対象url(今回は東京)
            url = "http://www.data.jma.go.jp/obd/stats/etrn/view/hourly_s1.php?" \
                  "prec_no=44&block_no=47662&year=%d&month=%d&day=%d&view="%(date.year, date.month, date.day)

            data_per_day = scraping(url, date)

            for dpd in data_per_day:
                writer.writerow(dpd)

            date += datetime.timedelta(1)

if __name__ == '__main__':
    create_csv()

やはり少々データ量が多いだけあって処理完了まで少しだけ時間がかかりました。ただ、結果はばっちりですね!

f:id:sanvarie:20210506110555p:plain

f:id:sanvarie:20210506110627p:plain

さいごに

気象庁のデータのダウンロードページは便利なのですが、様々な場所のデータを数年間分欲しいといった場合に手動でダウンロードを行うというのはあまり現実ではないかと思います。その際に本エントリーで紹介したようなスクレイピングが役に立つかと思います。また、世の中にある様々な Web サイトのデータ(例:食べログ)がほしいといった場合もスクレイピングをすることで今回のようにデータを取得することができます。興味のある方はぜひチャレンジしてみてください!本日は以上です。

機械学習を使って不動産取引価格の予測値を可視化してみよう!

さて、本日は機械学習ですね。今までのエントリーと異なりデータを加工・可視化するのではなく、収集したデータを使って将来の予測値を可視化してみようと思います。

機械学習とは

機械学習(きかいがくしゅう、英: Machine Learning)とは、経験からの学習により自動で改善するコンピューターアルゴリズムもしくはその研究領域で[1][2]、人工知能の一種であるとみなされている。「訓練データ」もしくは「学習データ」と呼ばれるデータを使って学習し、学習結果を使って何らかのタスクをこなす。例えば過去のスパムメールを訓練データとして用いて学習し、スパムフィルタリングというタスクをこなす、といった事が可能となる。(出典:Wikipedia)

今回使用する機械学習ライブラリ

今回は scikit-learn を使おうと思います。かなり広く使われているライブラリのようで、おそらく私のような初学者はまずこれを使うのがいいのではないかと思います。

scikit-learn.org

今回使用するデータ

不動産取引価格ダウンロード 国土交通省のデータを使用します。

2010年~2020年の横須賀市のデータをダウンロードしてみます。

f:id:sanvarie:20210501202732p:plain

データはこんなイメージですね。

f:id:sanvarie:20210501073316p:plain

各フィールド

データに関しては以下を参照してください。全てのフィールドは使用せずに必要なもののみをピックアップして機械学習を行おうと思います。

フィールド名 説明
No 連番
種類 「宅地(土地)」「中古マンション等」など。今回は「宅地(土地と建物)」を利用する。
地域 「住宅地」「商業値」など。 今回は「住宅地」を利用する。
市区町村コード 市区町村コード
都道府県名 都道府県名
市区町村名 市区町村名
地区名 地区名
最寄駅:名称 物件の最寄駅名
最寄駅:距離(分) 物件から最寄駅までの距離(分)
取引価格(総額) 物件の取引価格。これが今回推論したい値
坪単価 坪単価
間取り 間取り
面積(㎡) 面積(㎡)
取引価格(㎡単価) 取引価格(㎡単価)
土地の形状 「長方形」「不整形」など
間口 間口
延床面積(㎡) 延床面積(㎡)
建築年 建築年。元号表記。
建物の構造 「SRC」「木造」など
用途 「住宅」「共同住宅」など
今後の利用目的 「住宅」「事務所」など
前面道路:方位 前面道路の方位
前面道路:種類 前面道路の種類
前面道路:幅員(m) 前面道路の幅員(m)
都市計画 「商業地域」「第1種中高層住居専用地域」など
建ぺい率(%) 建ぺい率(%)
容積率(%) 容積率(%)
取引時点 「2016年第3四半期」など
改装 改装済みか否か
取引の事情等 特筆事項。「瑕疵有りの可能性」など

環境

Windows10 64bit
Python3.8.5
Tableau Desktop Public Edition 2021.1.0

手順

  1. 横須賀市各駅の位置情報取得
  2. データ加工
    1. 「種類」フィールドの値が「宅地(土地と建物)」のデータのみ使用
    2. 「地域」フィールドの値が「住宅地」のデータのみ使用
    3. 「用途」フィールドの値が「住宅」のデータのみ使用
    4. 今回推論に使用するフィールドにNULLがあるレコードの除去
    5. 「築年数」(元号表記)から築年数を算出
    6. 「築年数」(元号表記)から不正な値(「戦前」)を昭和15年に置換する
    7. 「最寄駅:距離(分)」の不正な値(「1H30?2H」など)を分に変換
    8. 「最寄駅:名称」の「逗子・葉山」を「逗子」に置換
    9. 外れ値の除去(1億5000万円より高い物件は今回は除去)
    10. 外れ値の除去(延床面積(㎡)が250㎡より大きいレコードは削除)
  3. モデルの作成
  4. モデルの評価

1.横須賀市各駅の位置情報取得

こちらのサイトのデータを利用し CSV を作成しました。(鎌倉や逗子が入っているというツッコミはなしでお願いします・・・)

f:id:sanvarie:20210504152515p:plain

2.データ加工

以下のコードで「手順」に記載した加工をすべて行っています。

import os
import pandas as pd

# 使用するファイル
input_dir = r"D:\blog\data\property"
property_data = "14201_20101_20204.csv"

# 出力するファイル
output_data = "output_data.csv"

# CSV読込
df = pd.read_csv(os.path.join(input_dir, property_data), encoding="cp932")

# 「種類」フィールドの値が「宅地(土地と建物)」のデータのみ使用
df = df[df['種類'] == "宅地(土地と建物)"]

# 「地域」フィールドの値が「住宅地」データのみ使用
df = df[df['地域'] == "住宅地"]

# 「用途」フィールドの値が「住宅」を含むデータのみ使用
df = df[df['用途'].str.contains("住宅") == True]

# 欠損値除去
df = df.dropna(subset=["最寄駅:距離(分)","延床面積(㎡)","建築年","建物の構造",'地域','用途'])

# 連番付与
serial_num = pd.RangeIndex(start=1, stop=len(df.index) + 1, step=1)
df["ID"] = serial_num

# 不要なフィールドを削除
df = df[["ID", "最寄駅:名称","最寄駅:距離(分)","延床面積(㎡)","建築年","建物の構造","取引価格(総額)"]]

# 最寄駅:距離(分)の不正な値を置換
df = df.replace({'最寄駅:距離(分)': {"1H?1H30": 60}})
df = df.replace({'最寄駅:距離(分)': {"1H30?2H": 90}})
df = df.replace({'最寄駅:距離(分)': {"2H?": 120}})
df = df.replace({'最寄駅:距離(分)': {"30分?60分": 30}})

# 建築年の不正な値を置換
df = df.replace({'建築年': {"戦前": "昭和15年"}})

# 建築年の元号表記を築年数に変換
df['年号'] = df['建築年'].str[:2]
df['和暦_年'] = df['建築年'].str[2:].str.replace('年','').astype(int)
df.loc[df['年号']=='昭和','築年数'] = 2021 - (df['和暦_年'] + 1925)
df.loc[df['年号']=='平成','築年数'] = 2021 - (df['和暦_年'] + 1988)
df.loc[df['年号']=='令和','築年数'] = 2021 - (df['和暦_年'] + 2019)

#「最寄駅:名称」の「逗子・葉山」を「逗子」に置換
df = df.replace({'最寄駅:名称': {"逗子・葉山": "逗子"}})

# 外れ値の除去(取引価格(総額)が1億50000万円より大きいレコードは削除)
df = df[df['取引価格(総額)'] <= 150000000]

# 外れ値の除去(延床面積(㎡)が250㎡より大きいレコードは削除)
df = df[df['延床面積(㎡)'].astype(int) <= 250]

# データの加工結果を出力
df.to_csv(os.path.join(input_dir, output_data))

出力した CSV を Tableau で確認してみます。各駅の取引価格(総額)を箱ひげ図で表示してみました。駅の順番はあやしいですが、「三崎口」や「三浦海岸」など三浦半島の先端に行くにしたがって金額が低くなることがわかります。また、サンプル数が少ないのですが、「逗子」や「鎌倉」は金額が高いですね。高級住宅街などがあるせいでしょうか。

f:id:sanvarie:20210501204239p:plain

価格と広さの関係性を確認してみようと思います。

f:id:sanvarie:20210501204906p:plain

R2乗値が約0.19ということは価格と広さにはあまり相関関係がないと言えそうです。

次に価格と築年数の関係性を確認してみようと思います。

f:id:sanvarie:20210501205136p:plain

R2乗値を見る限り、築年数が大きければ価格が下がる傾向にあると言えそうです。

また、「1.横須賀市各駅の位置情報取得」で作成した CSV を Tableau で読み込んでみました。

f:id:sanvarie:20210502080833p:plain

「取引価格(総額)」が高いほどポイントが赤色になるように設定しました。「逗子」や「鎌倉」エリアが高いことがわかります。

3.モデルの作成

まずは学習に必要なフィールドのみを抽出します。また、「1.横須賀市各駅の位置情報取得」で作成した CSV の緯度経度情報を output_data.csv にマージします。

import pandas as pd
import numpy as np

# 使用するファイル
input_dir = r"D:\blog\data\property"
output_data = "output_data.csv"
station_master = "yokosuka_station_master.csv"

# 前の手順で出力した CSV の読込
df_output = pd.read_csv(os.path.join(input_dir, output_data), encoding="utf-8")

# 駅マスタ読込
df_station = pd.read_csv(os.path.join(input_dir, station_master), encoding="utf-8")

# マージ
df = pd.merge(df_output, df_station, left_on='最寄駅:名称', right_on='station')
df = df.drop(["id", "station"], axis=1)

df.reset_index(drop=True, inplace=True)

# 出力変数
t = df["取引価格(総額)"]

# 学習に必要なカラムを抽出
x = df.iloc[:,[1,2,3,5,9]]

x.head()

f:id:sanvarie:20210504151709p:plain

ちなみに カラム抽出前のデータフレームは以下のように駅マスタの緯度経度が追加されています。

df.head()

f:id:sanvarie:20210504142513p:plain

object 型のカラムをダミー変数化します。

x = pd.get_dummies(x)
x.head()

f:id:sanvarie:20210504131244p:plain

データを訓練データと検証データに分割してモデルの学習を行います。

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

# 訓練データと検証データに分ける
x_train, x_val, t_train, t_val = train_test_split(x, t, test_size = 0.3, random_state = 0)

# 線形回帰モデル
model = LinearRegression()

model.fit(x_train, t_train)

モデルの評価を行います。訓練データで決定係数を確認しましたが、あまりいい値ではないですね。。

model.score(x_train, t_train)

f:id:sanvarie:20210504134116p:plain

検証データでも決定係数を確認してみます。

model.score(x_train, t_train)

f:id:sanvarie:20210504134202p:plain

予測値の推論

とりあえず今回は精度向上の試みはせずに先に進めようと思います。生成されたモデルを使って全てのデータの入力変数を使用して価格を推論します。

# 価格推論
pred = model.predict(x)

# 列名付与
y = pd.DataFrame(pred, columns=["predict"])

# 読み込んだ CSV の右端に推論結果を追加
results = pd.concat([df, y], axis=1)

# CSV 出力
results.to_csv(r"D:\blog\data\property\predict.csv", index=False)

4.モデルの評価

推論した価格と実際の価格を比較して誤差がどの程度かを Tableau で確認してみようと思います。

推論価格をX軸に、実際の価格をY軸にして散布図を作ってみました。本来は斜め45度のに分布するのが良いのですが、結果を見るとうーん、といった感じですね。

f:id:sanvarie:20210504143307p:plain

このように推論値がマイナスになっているものもあり、マイナスはいかんだろう感じです(笑)

f:id:sanvarie:20210504143410p:plain

他のを見ても推論値と実際の値とのずれが大きいことがわかります。

f:id:sanvarie:20210504144111p:plain

f:id:sanvarie:20210504144138p:plain

中にはかなり惜しいものもありますね。

f:id:sanvarie:20210504144307p:plain

「誤差」フィールドを作成して各駅ごとの推論値と実際値の誤差を表示してみました。「三浦海岸」「久里浜」「鎌倉」あたりが誤差が少ないことがわかります。

f:id:sanvarie:20210504150211p:plain

外れ値を確認してみると誤差が9千万以上ありますね。これはあまりいい結果とは言えないですね・・・

f:id:sanvarie:20210504150350p:plain

地図上で確認してみます。グレーになっているポイント(「三浦海岸」「久里浜」「鎌倉」など)が誤差が少ない駅ですね。

f:id:sanvarie:20210504151204p:plain

さいごに

はじめて本ブログで機械学習について触れてみましたがいかがでしたでしょうか。結果はいまいちでしたが、もう少しデータを詳細に分析し適切な学習モデルを作成することでより正確な推論値を得ることができるかと思います。これからの時代はただデータを GIS 上で可視化するだけでは差別化を行うことは難しく、機械学習などを用いて将来の予測値などを可視化する必要があるかと思っています。自分自身学ばなければならないことが多いのですが、時間をかけて学習する価値はあるかと思っています。これからも機械学習について継続的に本ブログで紹介しようかと思いますのでよろしくお願いします。本日は以上です。

Windows の Anaconda 環境に PyStan をインストールする方法

さて、本日は Windows の Anaconda 環境に PyStan をインストールする方法について書いてみようと思います。というのも先日この作業にはまったので備忘録として本エントリーを残そうと思います。本エントリーが同じ問題に直面した方の手助けになれば幸いです。

PyStan とは

Stan(スタン)はC++で書かれた統計的推論のための確率的プログラミング言語。 Stan言語では、対数確率密度関数を計算する命令型プログラムを使用して、(ベイジアン) 統計モデルを実装できる(出典: Wikipedia)。PyStan は Python で Stan を使用するためのライブラリです。

環境

Windows 10 64bit
Anaconda3-2020.11

Anaconda をインストール

以下のサイトで Windows 版の Anaconda をダウンロードしてインストールを行います。また、環境変数の設定も一緒に行います。

www.anaconda.com

私は以下に Anaconda 環境を入れました。
D:\anaconda3

環境変数 Path に以下を追加します。
D:\anaconda3
D:\anaconda3\Scripts

Anaconda 環境に PyStan をインストール

以下コマンドで Anaconda の仮想環境を構築することができます。今回は「1.仮想環境構築(ライブラリ無し)」で試してみます。

1.仮想環境構築(ライブラリ無し)

conda create --name XXX python=y.y

2.仮想環境構築(全ライブラリ含む)

conda create --name XXX python=y.y anaconda

仮想環境構築

Anaconda Prompt を開いて以下を実行します。Python のバージョンは3.7以上をインストールする必要があります。

conda create --name my-stan python=3.8.5

以下のコマンドで構築した仮想環境をアクティブにします。

conda activate my-stan

仮想環境をアクティブにしたので、必要なライブラリをインストールします。

conda install Cython Numpy Pandas matplotlib jupyter

pystanも入れてみます!

conda install pystan

C++のコンパイラーも必要になるので、以下コマンドでインストールします。

conda install libpython m2w64-toolchain -c msys2

インストール後、PYTHONPATH\envs\Lib\distutils に distutils.cfg というファイルが作成されます。もしなかったら手動で作成してみてください。ファイルの中身は以下のようになっています。

[build]           
compiler=mingw32 

Jupyter Notebook から作成した仮想環境に切り替えられるように以下コマンドを実行します。

ipython kernel install --user --name=my-stan

念のため再起動してこれで準備は完了です!

f:id:sanvarie:20210501172033p:plain

PyStan のインポートができました!使い方などはまたあらためて紹介できればと思います。本日は以上です。