銀行員 RとPythonに出会う

Rネタを中心に、いろいろと更新していきます

~不動産とファイナンス・賃貸物件入居者編(2)~「機械学習を使って東京23区のお買い得賃貸物件を探してみた」を千葉県でやってみる

今回は前回の続きをやっていきます。

引き続き参考はこちらです。

www.analyze-world.com

 

参考のままですが、コードは追記しておきました。

では、まず前回見ていなかった築年数から見ていきます。

f:id:d_s:20180827221357p:plain

市川、浦安という東京(江戸川区)の隣接地域の築年数が高く、流山・鎌ヶ谷築年数が低いという結果が出ました。

流山、鎌ヶ谷については近年開発されている地域なので納得感がありますが、千葉は意外でした。

よくよく考えると、物件情報が掲載されている時点で、空室・または退去見込みの物件が掲載されているわけです。

よって、市川・浦安等の東京へのアクセスが良いエリアは、築浅物件ほど埋まっているのかもしれません。

「年数の経った物件が残りやすい→掲載物件ベースでは築年数が高く出やすい」という流れがあるのでしょうか?

こんな感じで物件掲載情報での集計だとバイアスがありそうなので、解釈には注意が必要だと思います。

 

予測

ここからが一番ワクワクします。 

はじめに結論ですが、まずまず予測できたと感じるものの。。。

データセット内で、物件名と物件内容が一致していないものを多数見つけました!!

前回の分析中で、欠損を含むデータをドロップして気がかりで・・・と話しましたが、やはり嫌な予感が的中しました。

どうやら、スクレイピングの最中に何らかの原因で物件名と物件の内容にズレが起きていたようです。

物件内容自体は間違い無いと思うのですが、やはりやり直した方が良さそうです。

 

ということで、スクレイピングするところからやり直すこととします。

とは言ってもせっかくなので、一応の結果は挙げておきます。参考程度ということで。

 以下ランダムフォレストの結果です。

f:id:d_s:20180828001255p:plain

パラメーターは、tuneRF関数でグリッドサーチしました。個々の決定木を作成する際に使用する特徴量の数「mtry」を求めた結果、6となりましたので、mtry=6でモデルを作成しました。

急速に収束していく様子がわかります。

次にランダムフォレストで学習した結果の変数(特徴量)の重要度を見てみます。

f:id:d_s:20180828001309p:plain

other_cost(敷金、礼金、保証金等)とarea(専有面積)、age(築年数)の重要度が高いことがわかります。以降、最寄駅、市、間取り1LDK、階、最寄り駅までの距離、間取り1K・・・と続きます。

間取りはダミー化してしまっているので不利な気もしますが、全体的に納得できるような腑に落ちない何かがあるような。。。

なお、RandomForestの特徴量の重要度算出について以下の記事が参考になります。

Random Forestで計算できる特徴量の重要度 - なにメモ

 

つづいて、予測の結果です。

f:id:d_s:20180828002941p:plain

データセット全体を、学習用:テスト用に8:2に分けて検証を行っています。

赤線のテストデータに対し、ランダムフォレストの結果が青ですので、それなりに予測できていそうですが、先に述べたようにデータセットに留意が必要です。

物件名は一応伏せてありますが、アウトプットの中ざっと見た中で気になった物件を一つ挙げてみます。

f:id:d_s:20180828141747p:plain

どうでしょうか?

こちらの物件は築古の1Kですが、津田沼駅徒歩7分の立地で、リフォームしてあるようです。

敷金礼金なしで、管理費共益費込41,000円は安いのでないでしょうか。

再チャレンジが必要になりましたが、一連のアウトプットを経て勉強になりました。

まとめ

今回の分析を通じて、どうしても追加したい思った変数がありました。 

 

 

物件の構造です。

 

 

特徴量の重要度を見て何か腑に落ちない気がしたのは、まだ追加したいと思う変数が複数あったからです。

木造か軽鉄か、または鉄筋コンクリートなのかって物件選びで大事なポイントですよね。

また、物件の方角や駐車場有無なんかもあった方がより予測精度が上がるかもしれません。

総戸数がわかれば、その物件の入居率も変数として投入できます。

 

ということで、改めてsuumoを見てみます。

f:id:d_s:20180828225321p:plain

 

 

そういえば、今回「詳細を見る」の画面を気にもしていませんでした。

スクレイピングしてきたのは、この一覧画面で得られる情報に限定されていたわけですので、「詳細を見る」の中を見てみます。

f:id:d_s:20180828225236p:plain

 

f:id:d_s:20180828225502p:plain

あるじゃないですか!!

物件の向きから構造まで、そうそうこれが欲しかったんです。

実行時間としては相当な時間を覚悟しますが、物件詳細をループで処理していけば何とかなる気がしてきました。

 

よ~し、どうせスクレイピングし直すなら全部入れてしまえ!!

ということで、全項目取得します。

 

前回、Rでのスクレイピングを途中で断念してしまったので、あえて苦しみますがRでやり直そうと思います。

Pythonに逃げるかもしれませんが、少しでもRに貢献したく。。。)

まだ途中ですが、ざっとこんな感じのデータフレームができそうです。

f:id:d_s:20180828232000p:plain

おおちゃくしてRstudioのキャプチャをのっけてしまい横が収まっていないですが、建物構造やその物件にかかる条件なんかも取得できています。

その他取得できる項目を全部変数化できればより面白くなりそうです。

まだ恥ずかしながらエラー地獄に陥っているためコードを公開できる状態ではありませんが、完成したら続編記事内で載せたいと思います。

スクレイピングはド素人ですが、Rで奮闘します。

 

 

やり出すと、何だかやり込みたくなってきました。

 

 

 次回につづきます。

 

 

#市別築年数
age_med <- with(df, reorder(city, age, median))
par(las=1, cex.axis=0.7, family = "HiraKakuProN-W3")
boxplot(age ~ age_med, data = df,
        xlab = "築年数", ylab = "",
        main = "市別築年数", varwidth = TRUE, horizontal=TRUE, ylim = c(0,70), outline=FALSE)

# モデルに投入前する変数を選択
select_var1 <- c('names',"city", 'other_cost', "age", "floor", "area",
                 "monthly_cost", "rout1",'distance1','layout')
df2 <- df %>%
  filter(monthly_cost <= 170000) %>%
  select(select_var1)

# 相関を見ておく
cor(df2[,-c(1,2,8,10)])

# train, test分割
index <- createDataPartition(df2$monthly_cost, p=.8, list=F)
train <- df2[index,]
test <- df2[-index,]

# tuneRFでグリッドサーチ。個々の決定木を作成する際に使用する特徴量の数mtryを求める
tuneRF(train[,-1], train[,-1], doBest=T) # 結果→6
rf <- randomForest(monthly_cost~., data=train[,-1], mtry=6)
saveRDS(rf, file="rf")
summary(rf)
plot(rf)
print(rf$importance)
varImpPlot(rf, main="Contribution")

# 予測を行う
rf_pred <- predict(rf, test[-1]) 
rf_pred_csv <- data.frame(rf_pred)
write.csv(rf_pred_csv, "C:\\Users\\daich\\Documents\\R_src\\realestate\\rf_pred.csv")

# テストデータと予測データをソートしておく
test_sort <- test %>%
  arrange((monthly_cost))
rf_pred_sort <- predict(rf, test_sort[-1])

ggplot(test_sort) + 
  geom_line(aes(x=1:nrow(test_sort),y=rf_pred_sort,color="red"))+ 
  geom_line(aes(x=1:nrow(test_sort),y=monthly_cost,color="blue"))+ 
  #geom_line(aes(x=1:nrow(test),y=df_lm_pred,color="green"))+
  ggtitle("テストデータ vs 予測データ(ランダムフォレスト)")+xlab("index")+ylab("家賃")+ 
  scale_color_hue(name = "", labels = c("テストデータ","ランダムフォレスト") ) +
  theme(axis.text.x = element_text(angle = 180, hjust =1))+theme_bw(base_family = "HiraKakuProN-W3")+
  ylim(0,200000)


df2$pred_rf <- predict(rf, newdata = df2)
df2$diff <- df2$pred_rf - df2$monthly_cost
df_sort <- df2 %>%
  arrange(desc(diff))
df_sort <- df_sort[,c(1,2,10,7,11,12,3,8,9,4,5,6)]
df_sort$names <- rep(1:length(df_sort$names))
colnames(df_sort) <- c('物件名','市','間取り','1.家賃','2.予想家賃','予想家賃-家賃',
                       '敷金/礼金/保証金','最寄り駅','最寄駅距離','築年数','階','専有面積')