Python で不動産物件ごとに価格と地図上の位置を把握する

安い不動産がどの地域にあるか確認したいです

geopyで緯度経度情報に変更して、foliumで可視化ができるぞ

関係のあるブログ

以前のブログでスクレイピング処理をしました。

スクレイピングしたデータを処理しやすくするために前処理をします。

地図上で物件の情報を見れると有用です。今回は下記のように物件を金額ごとに色分けして、値段も万円単位で確認できるようにしました。

Python で不動産物件の経度緯度情報を取得する

必要なライブラリを取得します。

# -*- coding: utf-8 -*-
from tqdm import tqdm
import pandas as pd
from geopy.geocoders import Nominatim
from retry import retry
import re
import unicodedata
import folium

geopyを使用して住所から経度緯度情報を取得します。ただし詳細な住所から経度緯度情報は取得できません。

対象となっているのは東京の物件のため、区の情報が住所にふくまれます。

市以降の情報があっても経度緯度情報が取得できず、区によっては区以降の情報があると経度緯度情報が取得できません。

なのでおおよその場所が分かる程度になります。

@retry(tries=3, delay=10, backoff=2)
def get_lat_long_info(address, geolocator):
    address = "日本 " + re.sub("[0-9.]+|市.*|-", "", address)
    location = geolocator.geocode(address)
    if location is None:
        address = re.sub("区.*", "", address)
        location = geolocator.geocode(address)
    loc_dict = dict(location.raw)
    loc_dict['lat'] = float(loc_dict['lat'])
    loc_dict['lon'] = float(loc_dict['lon'])
    return loc_dict

下記コードで住所を取得し、各住所に対して上記のコードの関数で緯度経度を取得していきます。

数字が全角になっているので正規化して半角にします。処理数が多いので、取得するデータ数をdata_limitで制限しています。

def get_lat_lon_from_address(pandas_df, data_limit):
    address_l = list(pandas_df["所在地"])[::data_limit]
    
    latlons = []
    for address in tqdm(address_l):
        geolocator = Nominatim(user_agent="test-agent")
        address = unicodedata.normalize("NFKC", address)
        loc_dict = get_lat_long_info(address, geolocator)
        latlons.append(loc_dict)

    return latlons 

前回のブログで前処理したCSVを読み込みます。

df = pd.read_csv("suumo.jp.real_estate_all_convert.csv")
df.head()

下記コードで500件の物件の住所から経度、緯度情報を取得します。latlonsは辞書形式のデータになっています。

data_limit = 500
latlons = get_lat_lon_from_address(df, data_limit)

latlonsに価格情報を付与するための関数を作成します。

def get_price(pandas_df, data_limit, latlons):

    max_price = pandas_df['販売価格(万円)'].max()
    normalize_price_l = 2 ** (list(pandas_df["販売価格(万円)"])[::data_limit] / max_price * 70)
    
    price_l = list(pandas_df["販売価格(万円)"])[::data_limit]
    update_latlons = []
    for loc_dict, normalize_price, price in tqdm(zip(latlons, normalize_price_l, price_l)):
        loc_dict['price'] = price
        loc_dict['normalize_price'] = normalize_price
        update_latlons.append(loc_dict)

    return update_latlons 

先程の関数を使用してlatlonsに価格情報を付与します。

経度緯度の平均値を取得します。これは地図の中央位置を設定するために使用します。

latlons = get_price(df, data_limit, latlons)
latlons_df = pd.DataFrame(latlons)
latlons_df_mean = latlons_df.mean()
loc_center = [latlons_df_mean["lat"], latlons_df_mean["lon"]]

各値段ごとに色を変える関数を作成します。

  • 1000万以下:赤
  • 1000万レンジ:青
  • 2000万レンジ:緑
  • 3000万レンジ:紫
  • 4000万レンジ:オレンジ
  • 5000万レンジ:ダークレッド
  • 6000万レンジ:黒
  • 7000万レンジ:ベージュ
  • 8000万レンジ:ダークブルー
  • 9000万レンジ:ダークグリーン
  • 1億レンジ:cadetblue
  • 2億レンジ:灰色
  • 3億レンジ:ピンク
  • 4億レンジ:lightblue
import re

def color_map(price):
    prog_dict = {
        "red" : re.compile('[1-9][0-9][0-9]'),
        "blue" : re.compile('1[0-9][0-9][0-9]'),
        "green" : re.compile('2[0-9][0-9][0-9]'),
        "purple" : re.compile('3[0-9][0-9][0-9]'),
        "orange" : re.compile('4[0-9][0-9][0-9]'),
        "darkred" : re.compile('5[0-9][0-9][0-9]'),
        "black" : re.compile('6[0-9][0-9][0-9]'),
        "beige" : re.compile('7[0-9][0-9][0-9]'),
        "darkblue" : re.compile('8[0-9][0-9][0-9]'),
        "darkgreen" : re.compile('9[0-9][0-9][0-9]'),
        "cadetblue" : re.compile('1[0-9][0-9][0-9][0-9]'),
        "gray" : re.compile('2[0-9][0-9][0-9][0-9]'),
        "pink" : re.compile('3[0-9][0-9][0-9][0-9]'),
        "lightblue" : re.compile('4[0-9][0-9][0-9][0-9]'),
    }
    
    price = str(price)
    
    if prog_dict['red'].match(price) is not None and len(price) == 3:
        return 'red'
    
    if prog_dict['blue'].match(price) is not None and len(price) == 4:
        return 'blue'
    
    if prog_dict['green'].match(price) is not None and len(price) == 4:
        return 'green'
    
    if prog_dict['purple'].match(price) is not None and len(price) == 4:
        return 'purple'
    
    if prog_dict['orange'].match(price) is not None and len(price) == 4:
        return 'orange'
    
    if prog_dict['darkred'].match(price) is not None and len(price) == 4:
        return 'darkred'
    
    if prog_dict['black'].match(price) is not None and len(price) == 4:
        return 'black'
    
    if prog_dict['beige'].match(price) is not None and len(price) == 4:
        return 'beige'
    
    if prog_dict['darkblue'].match(price) is not None and len(price) == 4:
        return 'darkblue'
    
    if prog_dict['darkgreen'].match(price) is not None and len(price) == 4:
        return 'darkgreen'
    
    if prog_dict['cadetblue'].match(price) is not None and len(price) == 5:
        return 'cadetblue'
    
    if prog_dict['gray'].match(price) is not None and len(price) == 5:
        return 'gray'
    
    if prog_dict['pink'].match(price) is not None and len(price) == 5:
        return 'pink'
    
    if prog_dict['lightblue'].match(price) is not None and len(price) == 5:
        return 'lightblue'
    

色と金額の関係を把握できるように図にしておきます。

1000万以下は700万にしているのは図にしたとき把握しづらいためです。y軸を対数にした方が把握しやすくなります。

import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt


price = [700, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 30000, 40000]
str_price = [str(each_price) for each_price in price]
length = np.arange(len(price)) 

color = [color_map(each_price) for each_price in price]

fig, ax = plt.subplots()

# rect = ax.bar(str_price, price, color=color, label=str_price)

rect = ax.bar(str_price, price, width=width, color=color)
plt.xticks(rotation=90)

plt.legend(loc='best')
fig.show()

”ax.set_yscale(‘log’)”を使用するとy軸を対数にできます。

import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt


price = [700, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 30000, 40000]
str_price = [str(each_price) for each_price in price]
length = np.arange(len(price)) 

color = [color_map(each_price) for each_price in price]

fig, ax = plt.subplots()

ax.set_yscale('log')
# rect = ax.bar(str_price, price, color=color, label=str_price)

rect = ax.bar(str_price, price, width=width, color=color)
plt.xticks(rotation=90)

plt.legend(loc='best')
fig.show()

地図上に物件を配置し、価格ごとに色分けをします。

foliumを使用して可視化をします。地図の中央位置を設定し、各不動産を地図上にマークとして設定していきます。その際に価格ごとに色を変えていきます。

この地図ですが、Jupyter LabもしくはJupyter Notebookでないと表示されないので注意してください、

# visualize_map(latlons, loc_center)
map1 = folium.Map(location = loc_center, tiles='Openstreetmap', zoom_start = 5, control_scale=True)
for loc_dict in latlons:
    folium.CircleMarker([loc_dict['lat'], loc_dict['lon']],  
                        radius=3, 
                        weight=5,
                        color=color_map(loc_dict['price']),
                        popup=str(loc_dict['price'])).add_to(map1)
folium.LayerControl().add_to(map1)
map1
Close Bitnami banner
Bitnami