不動産取引価格予測

不動産価格はいくらになる?

賞金: 100,000 参加チーム数: 309 終了まで: 18日
コンペティション概要

今回のコンペでは、東京都における過去の不動産取引実績データを用いて、売買価格予測アルゴリズムの開発に挑戦いただきます。


背景課題・目的

不動産価格は、土地・建物といった不動産そのものが持つ特性に加え、周辺環境、景気動向に左右され決まります。
価格形成要因が多岐にわたるため、購入者にとっては「不動産鑑定士」による設定価格の妥当性評価が難しく、不動産市場の不透明感を高める一要因となっております。

平成31年4月に国土交通省より発表された「不動産ビジョン2030」ではこうした状況をふまえ、不動産取引価格に影響する情報の一括提供 及び データ提供方法の改善が必要であると指針を示しています。

またNTTデータ経営研究所が2018年に行った、今後最も有望な不動産テックに関するアンケート調査によると、「不動産の成約価格、物件情報などのデータを収集・分析・共有することで資産価値評価や売買予測等を行うサービス群」への回答が最多となっています。

このような背景をふまえ本コンペティションでは、価格推定アルゴリズムの開発を通して、不動産価格形成に関する理解度を参加者全員で楽しみながら高めること、を目的としております。

データセットが限られておりますため、実際に利用可能なモデル開発とまでは至らないかと存じますが、機械学習アルゴリズム開発の学習に加え、不動産取引に関する理解向上に向けた一歩となりますと幸いです。


賞金

1位 100,000円
※ 対象者には、コンペティション終了後 メールにてご連絡いたします。

締切

2020年4月27日 0:00 JST

ダウンロード

データをダウンロードするにはログインまたはユーザー登録して下さい

概要

コンペに使用するデータは3つに分けられます。

  • 訓練データ(train_data.csv)
  • テストデータ(test_data.csv)
  • 参照用 公示価格データ(published_land_price.csv)

訓練データセットとテストデータセットにはそれぞれ356,345レコードと34,845レコードのサンプルが含まれています。
訓練データは、土地情報と取引金額の値が各レコードごとに記載されています。機械学習モデルの構築に使用してください。

テストデータセットでは、取引金額のデータは存在しません。テストデータセットを使用して、どの程度新しいデータに対してモデルが適合しているかを確認し、各不動産の取引金額を予測してください。

また、提出ファイルの例に関しては、評価方法のタグを参照してください。


データの形式

データセットのカラムは以下の通りになっています。


訓練データ
カラム名 説明
種類 不動産の種類
(「宅地(土地/土地と建物)」「中古マンション等」「農地」「林地」など)
「宅地(土地)」:土地のみの取引
定義は下記の通り
「宅地(土地と建物)」:土地と建物等を一括して取引
「中古マンション等」:区分所有物件(戸単位)の取引
地域 地域名を記載
(「住宅地」など)
市区町村コード 総務省「全国地方公共団体コード」http://www.soumu.go.jp/denshijiti/code.html
都道府県名 都道府県名を記載
(今回は全て東京都)
市区町村名 市町村名を記載
地区名 地区名を記載
最寄駅:名称 駅名を記載
最寄駅:距離(分) 最寄駅からの最短距離を記載
間取り 間取りを記載
(「1LDK」「1DK」など)
面積 単位(m2):土地の面積を記載
土地の形状 「宅地(土地)」「宅地(土地と建物)」について、土地のおおよその形状を「正方形」、「ほぼ正方形」、「長方形」、「ほぼ長方形」、「台形」、「ほぼ台形」、「不整形」、「ほぼ整形」、「袋地等」の別に表示
間口 「宅地(土地)」「宅地(土地と建物)」について、土地の接道幅(m)(土地が道路に接している長さ)を表示
50m以上の場合は、「50m以上」と表示。
間口が把握できないものは空欄
延床面積 単位(m2)
「宅地(土地と建物)」の上物(建物)について、延床面積(㎡)を表示
200㎡未満は「5㎡刻み」とし、200㎡以上は上位3桁目を四捨五入し上位2桁を表示
2,000㎡以上の大規模取引については「2,000㎡以上」と表示
10㎡未満の小規模取引については「10㎡未満」と表示
建設年 和暦+年で記載(H20,S25など)
「宅地(土地と建物)」の上物(建物)及び「中古マンション等」の建物について、当該建物の建築年を表示
1945年以前は「戦前」と表示。建築年が把握できないものは空欄
建物の構造 建物構造略号は次の通り、数字はその階層を示す
(地下階層がある場合、地上階層にはFを地下階層にはBを付与)
SRC:鉄骨鉄筋コンクリート造
RC:鉄筋コンクリート造
S:鉄骨造
LS:軽量鉄骨造
B:ブロック造
W:木造
用途 「宅地(土地と建物) 」の上物(建物)及び「中古マンション等」の専有部分について、利用の現況
複合用途の場合は列記しています。用途が把握できないものは、空欄になっています。
(「住宅」「事務所」「店舗」「工場」「倉庫」「作業場」「駐車場」「その他」など)
今後の利用目的 今後の利用目的が把握できているものについて利用目的を記載(「住宅」「店舗」「事務所」「工場」「倉庫」「その他」など)
前面道路:方位 標準地の側道の方位による区別を表示
(東/西/南/北/南東/南西/北西/北東)
前面道路:種類 道路の種類については、以下に区分して表示。
道路法上の道路は「国道」、「都道府県道」、「市町村道」等
土地区画整理事業施行地区内の道路は、「区画街路」
私人が管理する道で、いわゆる私道と称されているものは、「私道」
その他の道は、「道路」
前面道路:幅員 単位(m)
都市計画 1低専:第一種低層住居専用地域
2低専:第二種低層住居専用地域
1中専:第一種中高層住居専用地域
2中専:第二種中高層住居専用地域
1住居:第一種住居地域
2住居:第二種住居地域
準住居:準住居地域
近商:近隣商業地域
商業:商業地域
準工:準工業地域
工業:工業地域
工専:工業専用地域
田園住:田園住居地域
建ぺい率 「宅地(土地)」「宅地(土地と建物)」「中古マンション等」について、建ぺい率(%)を表示
容積率 「宅地(土地)」「宅地(土地と建物)」「中古マンション等」について、指定容積率(%)を表示
取引時点 西暦+四半期で記載
改装 「未階層」「改装済み」を記載
取引の事情等 特別取引の事情があれば記載
y 取引金額(1,000,000円)


政府公示資料
カラム名 説明
経度 単位(秒)(例:501376.613=139.2712816°=139°16’16.613”)
座標値は世界測地系
緯度 同上
所在地コード 総務省「全国地方公共団体コード」
http://www.soumu.go.jp/denshijiti/code.html
用途 000:住宅地
003:宅地見込地
005:商業地
007:準工業地
009:工業地
010:市街化調整区域内の現況宅地
013:市街化調整区域内の現況林地
連番 一連番号情報
年次 最新選定年次(西暦)
前年所在地コード 総務省「全国地方公共団体コード」
前年用途 000:住宅地
003:宅地見込地
005:商業地
007:準工業地
009:工業地
010:市街化調整区域内の現況宅地
013:市街化調整区域内の現況林地
前年連番 前年の一連番号番号
市区町村名 市区町村名の名称
住居表示 住居表示または地番
行政 総務省「全国地方公共団体コード」http://www.soumu.go.jp/denshijiti/code.html
地積 単位(m2)
利用の概況 住宅/店舗/事務所/銀行/旅館/給油所/工場/倉庫/農地/山林/医院/空地/作業場/原野/その他/用材/雑木の17項目について基準地・標準地の利用の現況を表示
それぞれの項目について
「1:用途地域を示すもの」
「0:それ以外」
を順に17桁の数字で表示
(例:利用現況が「店舗」の場合、「01000000000000000」と表示)
利用状況表示 「利用の現況」が“その他”の場合の利用状況(自由記述)
建物構造 建物構造略号は次の通り、数字はその階層を示す(地下階層がある場合、地上階層にはFを地下階層にはBを付与)
SRC:鉄骨鉄筋コンクリート造
RC:鉄筋コンクリート造
S:鉄骨造
LS:軽量鉄骨造
B:ブロック造
W:木造
施設 水道/ガス/下水の3項目
1:施設の整備されているもの
0:施設の整備されていないもの
をそれぞれの項目について順にセットする(例:水道及びガスの施設がある場合は「110」となる)
形状区分 基準値の形状による区別を表示(「台形」「不整形」など)
間口(比率) 間口幅と奥行き幅の比率
(短い方を基準値10とした際の比率)
奥行(比率) 間口幅と奥行き幅の比率
(短い方を基準値10とした時の比率)
階層(地上) 階層数
階層(地下) 階層数
前面道路区分 標準地の前面道路による区別を表示
(国道/都道/道道/府道/県道/市道/区道/町道/村道/私道/農道/林道/区画街路/道路)
前面道路の方位区分 標準地の前面道路の方位による区別を表示
東/南/西/北/南東/南西/北西/北東
前面道路の幅員 単位(×10cm)
前面道路の駅前区分 標準地の前面道路の駅前による区別を表示
(「駅前広場」など)
前面道路の舗装状況 標準地の前面道路の舗装状況を表示
(「未舗装」など)
側道区分 側道/三方路/四方路/一方路・準角地/背面道/その他
側道方位区分 標準地の側道の方位による区別を表示
(東/西/南/北/南東/南西/北西/北東)
交通施設との近接区分 標準地の交通施設との近接状況を表示
(近接/接面/駅前広場接面)
周辺の土地の利用の概況 周辺の土地の利用の現況
(自由記述)
駅名 最寄り駅名を記載
駅距離 単位(m)、最寄り駅からの最短距離を記載
用途区分 1低専:第一種低層住居専用地域
2低専:第二種低層住居専用地域
1中専:第一種中高層住居専用地域
2中専:第二種中高層住居専用地域
1住居:第一種住居地域
2住居:第二種住居地域
準住居:準住居地域
近商:近隣商業地域
商業:商業地域
準工:準工業地域
工業:工業地域
工専:工業専用地域
田園住:田園住居地域
防火区分 防火:防火地域
準防:準防火地域
都市計画区分 市街化:市街化区域
調区:市街化調整区域
非線引:市街化区域及び市街化調整区域以外の都市計画区域
都計外:都市計画区域外
準都計:準都市計画区域
森林区分 地森計:地域森林計画対象区域
公園区分 国立公(普通):国立公園地域
国立公(2種):国立公園地域第二種特別区域
国立公(3種):国立公園地域第三種特別区域
国定公(普通):国定公園区域
国定公(2種):国定公園第二種特別区域
国定公(3種):国定公園第三種特別区域
建蔽率 単位(%)
容積率 単位(%)
共通地点区分 TRUE:共通地点である
FALSE:共通地点ではない
選定年次ビット 1:選定対象年である場合
0:それ以外」をそれぞれの年次についてセット
(例:平成5年のデータの場合は、11年分・11桁のデータとなる)
‘年数(和暦)’+価格 年次での公示価格(円/m2)
造形移動+’年数(和暦)’ 選定状況/住所漢字/地積/利用の現況/建物構造/供給施設/駅からの距離/用途区分/防火区分/都市計画区分/森林区分/公園区分/建ぺい率/容積率の14項目に対応する14桁の数値で示した。
数値は当該年次の選定状況を下記の通り示した。
1:継続
2:標準地・基準地番号変更
4:新設・選定替えで当該年追加
選定状況以外の項目は
1:前年から変更あり
0:前年から変更なし
をそれぞれの項目について順にセットする
参照元

不動産取引価格情報検索システム クリエイティブ・コモンズ・ライセンス表示4.0国際(https://creativecommons.org/licenses/by/4.0/legalcode.ja
公示地価(国土数値情報) を元に株式会社ProbSpaceが作成

評価指標

不動産取引金額(y)について、実数値で予測してくだい。
モデルの予測性能は評価関数RMSE(Root Mean Squared Error)で評価されます。

  (RMSEの評価値)

$$
\sqrt{\frac{\sum_i(y_{obs,i} -y_{pred,i})^2}{n}}
$$

  • 評価値は0以上の値をとり、精度が高いほど小さな値となります。

提出ファイルの形式

回答用のsubmission.csvを用意する(エントリーとヘッダー行を含む) 。
提出されたファイルに余分な行や列(idとy以外)が含まれていた場合はエラーとなります。

提出ファイルは以下の列のみを必ず含んでください:

  • id(テストデータセットと同じ順序)
  • y(予測された取引金額)

以下は提出ファイルの例です。
id,y
0, 25
1, 30
2, 4.5
3, 7
■Open Review Competition

本コンペでは、開催期間終了後 賞金対象者のコードを公開し、ユーザーの皆様にチーティング有無をレビューしていただき順位確定させる、オープンレビュー方式のコンペティションを行います。


賞金対象ユーザー

コンペ終了後1週間以内:
トピックにて、学習過程の分かるコードの公開をお願いいたします。 (簡易解説までつけていただけると助かります。)

コード公開後3週間:
レビュアー(ユーザー)より、チーティングの疑いに関するコメントがある場合は、ご回答をお願いいたします。
※チーティングとは無関係のコメント(ノウハウに関する質疑 等)についてもご回答いただけると幸いですが、順位確定の判断材料とは致しません。


レビュアー(ユーザーの皆様)
コード公開後3週間: 公開コードを確認いただき、チーティングが疑われる場合は、トピックを通して質疑の投稿をお願いいたします。

レビュアーからの質疑と、回答状況をふまえて、最終的に運営側で順位確定を判断します。

■順位決定ロジック
  1. コンペ期間中はPublicリーダーボード(以下LB)により暫定評価を、最終結果についてはPrivate LBにより評価します。
    ※ Private LBはコンペ終了と同時に表示されます
  2. スコアが同値の場合は、早い日時に提出いただいたユーザーが上位となります。
  3. コンペ終了後であっても、不正が発覚の際は、対象ユーザーは失格となり、全体の順位が繰り上がります。
    順位繰上げにより賞金対象者となられた場合は、繰上げ日より一週間以内に、トピックにてコードを公開いただき、「Open Review Competition」と同様のフローにて順位を確定させていただきます。
■タイムライン

開始日 2020/02/26 0:00 JST
終了日 2020/4/27 0:00 JST

エントリー締め切り なし

■システム利用
  • 参加者ごとに1つのアカウントでご参加ください
  • チーム参加にの場合は、最大5名での参加が可能です
  • 1日あたり、最大5回までの提出が可能です
■禁止事項
  • ユーザー間での情報共有
    コンペティションに関連するコード
  • データを、チーム外のユーザーと共有することはできません。全参加者が利用できる場合に限り、共有可能です。
  • 外部データの使用
    本コンペティションの基本情報/データから取得できるデータのみを用いてチャレンジして下さい。コンペ外データを用いて学習されたモデルの使用も禁止とします。
    (3/6追記)トピック上で、運営より承認された外部データのみ、活用を許可いたします。承認プロセスについては、「外部データの活用申請について」をご確認ください。

※コンペ期間中であっても、不正が疑われる場合は、運営より確認のためメール連絡させていただくことがございます。一週間以内にご回答いただけない場合も、不正と判断させていただきます。

■外部データの活用申請について(3/6追記)

原則外部データ活用は禁止としておりますが、正解データの入手につながらない場合は、下記承認プロセスにより活用できることといたします

  1. トピックを作成し、取得元情報とデータ(格納先URLも可)を添付
  2. 運営にて判断・承認

注)ただし、コンペ終了まで10日をきってからの申請は禁止とします

■著作物の取扱い

本コンペティションで発生した成果物に関する所有権と著作権は、参加者に帰属します。
ただし、参加者が作成した著作物を本サービスを通じて掲載した場合、教育・研究開発にかかる使用に際して、当該参加者は著作者人格権を行使しないものとします。
※第三者が、授業・研修・セミナー等で活用できるようにするための規約となります。ご理解のほどよろしくお願いいたします。

■運営からのお願い

公平性の担保、チーティング等の不正防止のため、予告なくルールの追加・変更を行う場合がございます。
ご不便をおかけすることもあるかと思いますが、サービス向上のためご了承ください。

FAQ

このコンペティションでは賞金はでますか?

はい。
最も精度の高い学習モデルを作成した優勝者には、賞金10万円を贈呈します。
順位確定までのプロセスについては、ルール「Open Review Competition」を参照ください。

チームで参加できますか?

可能です。チームページから作成いただけます。

どこでアカウントをつくればいいですか?

こちらから作成いただけます。

コンペティション参加にはアカウント登録が必要となりますのでご注意ください。

コードを提出するにあたって Seed を固定する必要はありますか?

Seed を固定することが推奨です。
ただし、Seed を固定しなくても提出用コードとしては認めています。

背景画像のクレジットについて

Photo credit: radkuch.13 on Visualhunt.com / CC BY

このチュートリアルでは不動産取引データに対して

  • データの読み込み
  • データの確認と前処理
  • 重回帰モデルの作成、学習
  • モデルの評価
  • テストデータに対する出力

を行います。


環境

  • python 3.6.8
  • numpy 1.17.3
  • pandas 0.25.2
  • matplotlib 3.1.1
  • sklearn 0.21.3

データの読み込み

まずはデータを読み込んで見ましょう。csvデータの読み込みは複数のやり方がありえますが、pandasread_csv関数はその中でも機能が豊富で、扱いやすいためこれを使います。
これを使うと、csvデータを読み込み、pandas.DataFrameにして返してくれます。

import pandas as pd

path = "/content/drive/My Drive/competition/ProbSpace/real_estate/train_data.csv"
train_data = pd.read_csv(path)

また、DataFrameには.head()というメソッドが定義されています。このコードを呼び出すとDataFrame先頭の数行を確認可能です。

train_data.head()
id 種類 地域 市区町村コード 都道府県名 市区町村名 地区名 最寄駅:名称 最寄駅:距離(分) 間取り
1 中古マンション等 NaN 13101 東京都 千代田区 飯田橋 飯田橋 1 2LDK
2 中古マンション等 NaN 13101 東京都 千代田区 飯田橋 飯田橋 5 1K
3 中古マンション等 NaN 13101 東京都 千代田区 飯田橋 飯田橋 3 1LDK
4 中古マンション等 NaN 13101 東京都 千代田区 飯田橋 飯田橋 5 1R
5 宅地(土地と建物) NaN 13101 東京都 千代田区 飯田橋 飯田橋 3 NaN

データの確認と前処理

訓練データにおける取引金額がどのような分布になっているか見てみましょう。各階級ごとの度数をヒストグラムを用いてプロットするのが以下のコードです。

%matplotlib inline
import matplotlib.pyplot as plt
plt.hist(train_data['y'], bins=20)

b5509942-26e3-48fc-8d66-d8c5c3b92b30.png

ヒストグラムとしては、グラフエリアの左側に1本だけ棒状グラフが出力されました。

ほぼ全てのレコードの取引金額が10未満であり、一部の少数のレコードが60近い値を取っているために、上記のように表示されました。

極端に他のレコードと値の異なるものは'外れ値'と言われ、回帰モデルに悪影響を及ぼす場合もあります。この'外れ値'をどのように扱うかは場合により異なります。

今回は、四分位法と呼ばれる外れ値の除外手法にもとづき取引金額90以上のレコードを除外しました。

#外れ値除外
train_data = train_data[train_data["y"] < (90)]
plt.hist(train_data['y'], bins=20)

0ea4755d-4e67-4949-8659-55b38a76706f.png

外れ値を除外したヒストグラムでは20,000,000辺りが最も多く、より低い金額側に偏った分布が見られました。

20,000,000より高価格帯の取引金額は、比較的高い金額まで広範囲に分布していることがわかりました。

続いて、下記のコードを使用して使用するデータ全体の概観を確認します。

train_data.info()

各項目について欠損値以外('non-null')の値がどの程度含まれているか把握できます。また、データの形式についてもここで判別可能です。

#RangeIndex: 319449 entries, 0 to 356343
#Data columns (total 28 columns):
#id         319449 non-null int64
#種類       319449 non-null object
#地域       163547 non-null object
#市区町村コード 319449 non-null int64
#
#               ~中略~
#
#最寄駅:距離(分)309720 non-null object
#建築年           252049 non-null object
#

'地域'は欠損値が多数含まれていることがわかりました。欠損値が多い列を説明変数として採用すると、作成したモデルの汎用性が低くなる可能性があります。

データの形式を見ると、数値についてはfloat64/int64型になっていることが確認できます。
不動産の取引金額との関係性が強いと考えられる'最寄駅:距離(分)''建築年'のデータ形式を見るとobject型となっていました。
回帰モデルの説明変数として扱うためには、数値に変換する必要がありそうです。

'最寄駅:距離(分)''建築年'について、.value_counts()を使用して、中に含まれている値とその個数を確認します。

pd.set_option('display.max_rows', 100)
print(train_data['最寄駅:距離(分)'].value_counts())
print(train_data['建築年'].value_counts)
#最寄駅:距離(分) 個数
#6              25479
#4              23136
#
#    ~中略~
#
#12             10455
#15             10146
#30分?60分       9541
#16              8987
#
#    ~中略~
#
#1H?1H30         1305
#22               756
#27               278

'最寄駅:距離(分)'では、'30分?60分'などの数値以外の値が一部含まれていましたので適当な値に置換して数値として処理できるようにします。

train_data['最寄駅:距離(分)'] = train_data['最寄駅:距離(分)'].replace('30分?60分','45')
train_data['最寄駅:距離(分)'] = train_data['最寄駅:距離(分)'].replace('1H?1H30','75')
train_data['最寄駅:距離(分)'] = train_data['最寄駅:距離(分)'].replace('1H30?2H','105')
train_data['最寄駅:距離(分)'] = train_data['最寄駅:距離(分)'].replace('2H?','120')
train_data['最寄駅:距離(分)'] = pd.to_numeric(train_data['最寄駅:距離(分)'], errors='coerce')

続いて、'建築年'の出力結果についても確認します。

#建築年        個数
#平成19年       11035
#平成18年       10550 
#平成20年       10071
#
#    ~中略~
#
#昭和30年       305
#昭和36年       271
#戦前           256
#昭和35年       242

'建築年'は、'平成''昭和'などの和暦表記になっている他、'戦前'という表記も見られました。こちらも適当な西暦4桁に置換します。

train_data['建築年'] = train_data['建築年'].dropna()
train_data['建築年'] = train_data['建築年'].str.replace('戦前','昭和20年')
train_data['年号'] = train_data['建築年'].str[:2]
train_data['和暦年数'] = train_data['建築年'].str[2:].str.strip('年').fillna(0).astype(int)
train_data.loc[train_data['年号']=='昭和','建築年(西暦)'] = train_data['和暦年数'] + 1925
train_data.loc[train_data['年号']=='平成','建築年(西暦)'] = train_data['和暦年数'] + 1988

続いて、数値データの正規化を行い、平均0、標準偏差1になるように変換を行います。
今回は、'容積率(%)''建築年(西暦)'について正規化をしました。

import numpy as np

l = ['最寄駅:距離(分)','容積率(%)','建築年(西暦)']
for name in l:
  mean = np.nanmean(train_data[name], axis=0)
  std = np.nanstd(train_data[name], axis=0)
  train_data[name] = (train_data[name] - mean)/std
train_data.describe()

.describe()を使用して、基本統計量を確認できます。平均0、標準偏差1に正規化できていることを確認します。

#     最寄駅:距離(分) 容積率(%)  建築年(西暦)
#count  3.097200e+05    3.143390e+05    2.520490e+05
#mean   5.934281e-13    -2.655652e-13    7.684944e-16
#std    1.000002e+00    1.000002e+00    1.000002e+00
#min    -1.099437e+00   -1.342352e+00   -3.862005e+00
#25%    -6.033779e-01   -7.053855e-01   -7.269815e-01
#50%    -2.065307e-01    -3.869024e-01   2.937237e-01
#    ~後略~

予測モデルの作成,学習

モデルに投入する目的変数(y),説明変数(X)を作成します。予測モデルを学習させる上でnull値の含まれたレコードを除外します。

今回は、'最寄駅:名称''最寄駅:距離(分)''容積率(%)''建築年(西暦)'を説明変数として用いました。

#使用する説明変数・目的変数を代入
model_input = train_data[['最寄駅:名称','最寄駅:距離(分)','容積率(%)','建築年(西暦)','y']]

#説明変数・目的変数についてnull値を含むレコードを除外
model_input = model_input.dropna(how='any', axis=0) 

#目的変数と説明変数を代入
X = model_input[['最寄駅:名称', '最寄駅:距離(分)', '容積率(%)', '建築年(西暦)']]
y = model_input['y']

このデータにはいくつかのカテゴリカルデータが含まれているので今のままでは機械学習モデルで扱いづらいです。そこで、カテゴリカルデータに対してget_dummies関数を使いone-hot表現に変換することができます。

今回は、'最寄駅:名称'のみone- hot表現に変換しました。drop_first=Trueを使用することで、多重共線性を弱めることができます。

#one-hot表現へ変換
X = pd.get_dummies(X, drop_first=True)

いよいよ予測モデルの作成に入ります。
model.fit('説明変数','目的変数')と記述することでモデルの学習が可能となります。目的変数を説明変数の組み合わせで説明可能な回帰モデルを作成できます。

#scikit-learnライブラリをimport
import sklearn
from sklearn.linear_model import LinearRegression as LR

#線形回帰モデルのインスタンス化
model = LR()

#予測モデルの作成
model.fit(X, y)

作成したモデルの係数と切片を見てみましょう。
model.coef_で係数の一覧、.intercept_で切片を見ることができます。

#各説明変数の係数
coeff = pd.DataFrame(X.columns)
coeff.columns = ['説明変数']
coeff['係数推定'] = model.coef_
print(coeff)
#0  最寄駅:距離(分)            -1.937073
#1  容積率(%)                -6.352881
#2   建築年(西暦)              6.468335
#3   最寄駅:名称_お花茶屋       -33.78402
#4   最寄駅:名称_こどもの国(神奈川)      -2.508123e
#               ~後略~
[647 rows x 2 columns]
#回帰モデルの切片
model.intercept_
#58.86466422497909

各説明変数の係数を比較することで、どの説明変数がどれほどの影響を与えていたか把握することができます。

各説明変数は、係数の値が正に大きいほど取引金額に対して正の寄与をしており、反対に負に大きいほど取引金額に対して負の寄与をしていると言えます。

上記の出力結果を見ると、'建築年(西暦)'の係数の値が正に大きく、比較的近年に建てられた不動産であるほど取引金額が高いことがわかります。
'容積率(%)'では係数が負に大きく、敷地面積に対する延べ床面積の割合が大きいほど、取引金額は低くなることがわかります。


モデルの評価

テストデータに対する回帰モデルの当てはまりの良さの指標としては'決定係数'などの指標が用いられます。
決定係数の値は、.scoreで確認が可能です。
1に近いほど回帰式で予測された値が実際のデータに当てはまることを表します。

#決定係数
model.score(X,y)
#0.312906449494207

今回作成したモデルの決定係数は0.31程度でした。

また、コンペの評価指標となるRMSEについても出力が可能です。
今回は、sklearn.metricsに含まれるmean_squared_errorメソッドを使用します。
RMSEは平均化された誤差の値を示す指標であり、0に近いほど見積もられる予測誤差が小さく予測精度が高いことを表します。
モデルを元にした予測値は、model.predict('説明変数')で表します。

# RMSE値の出力
import numpy as np
from sklearn.metrics import mean_squared_error
y_true = model_input['y']
y_pred = model.predict(X)
print(np.sqrt(mean_squared_error(y_true, y_pred)))
#15.176513329401711

今回作成したモデルのRMSEは15程度でした。


テストデータに対する出力

最後に、作成したモデルを使用してテストデータでの取引金額を予測します。

テストデータの説明変数については予め、上記でモデルに学習させたデータの説明変数と同様の前処理をする必要があります。

path_test = "/content/drive/My Drive/competition/ProbSpace/real_estate/test_data.csv"
test_data = pd.read_csv(path_test)

print(test_data['最寄駅:距離(分)'].value_counts())

#最寄駅:距離(分)の変換
test_data['最寄駅:距離(分)'] = test_data['最寄駅:距離(分)'].replace('30分?60分','45')
test_data['最寄駅:距離(分)'] = test_data['最寄駅:距離(分)'].replace('1H?1H30','75')
test_data['最寄駅:距離(分)'] = test_data['最寄駅:距離(分)'].replace('1H30?2H','105')
test_data['最寄駅:距離(分)'] = test_data['最寄駅:距離(分)'].replace('2H?','120')
test_data['最寄駅:距離(分)'] = pd.to_numeric(test_data['最寄駅:距離(分)'], errors='coerce')

#建築年を数値データとして処理
test_data['建築年'] = test_data['建築年'].dropna()
test_data['建築年'] = test_data['建築年'].str.replace('戦前','昭和20年')
test_data['年号'] = test_data['建築年'].str[:2]
test_data['和暦年数'] = test_data['建築年'].str[2:].str.strip('年').fillna(0).astype(int)
test_data.loc[test_data['年号']=='昭和','建築年(西暦)'] = test_data['和暦年数'] + 1925
test_data.loc[test_data['年号']=='平成','建築年(西暦)'] = test_data['和暦年数'] + 1988

l2 = ['最寄駅:距離(分)','容積率(%)','建築年(西暦)']
for name in l2:
  mean = np.nanmean(test_data[name], axis=0)
  std = np.nanstd(test_data[name], axis=0)
  test_data[name] = (test_data[name] - mean)/std 

#使用する説明変数・目的変数を代入
model_input_test = test_data[['最寄駅:名称','最寄駅:距離(分)','容積率(%)','建築年(西暦)']]

#説明変数・目的変数についてnull値を含むレコードを除外
model_input_test = model_input_test.dropna(how='any', axis=0) 

# 目的変数と説明変数を代入
X_test = model_input_test

# one hot表現へ変換
X_test = pd.get_dummies(X_test, drop_first=True)

モデルで使うデータとテストデータのカラム数が違うとエラーが発生するので、モデルの説明変数について事前に工夫が必要です。

今回は、学習データとテストデータで重複する説明変数のみを利用して学習と予測を実施します。

また、下記のコードでは訓練データで学習させた説明変数の中で、テストデータに含まれないについても列名を出力しています。

col_list = X.columns.tolist() 
col_test_list = X_test.columns.tolist()
col_joint = col_list + col_test_list
col_dup = [x for x in set(col_joint) if col_joint.count(x) > 1]

col_list_p = [x for x in set(col_joint) if col_list.count(x) == 0]
print(col_list_p)
#出力['最寄駅:名称_沢井']

'最寄駅:名称_沢井'は、取引金額に影響を及ぼす可能性がありますが、テストデータには含まれません。そのため、今回の予測の上ではモデルに学習させてもあまり意味はないと考え、学習させるデータから該当するレコードを除外しました。

その上で訓練データについて再度学習させて、モデルを作成します。

X_test=X_test[X_test['最寄駅:名称_沢井']!=1]

X_train_fix = X[col_dup]
X_test_fix = X_test[col_dup]

model.fit(X_train_fix, y)

先ほど使用したmodel.predict('説明変数')'説明変数'にテストデータの値を代入することで、テストデータの予測値を算出することができます。

下記のようにして、提出用のsubmission.csvを出力することが可能です。

test_predicted = model.predict(X_test_fix)
submit_df = pd.DataFrame({'y': test_predicted})
submit_df.index.name = 'id'
submit_df.index = submit_df.index + 1
submit_df.to_csv('submission.csv')