akatak’s blog

プログラム初心者の50代ビジネスマンがセカンドキャリアを目指して働きながらPythonを中心に独学しています。自らの覚え書きや感じたことなどを脈絡もなく書き連ねるブログです。

Pythonによる不動産情報のデータ取得&分析(5)【売却物件/データ前処理編】

akatak.hatenadiary.jp

さて、前回記載したスクリプトでSUUMOさんのサイトからスクレイピングしましたデータを、分析しやすいようにきれいにしていきましょう。このデータの前処理は、データの状況にもよりますので、なかなか完全な自動化は難しいですよね。自動化できる良い方法があれば、どなたか是非教えて下さい!

さて、例によって、Jupyter Notebookにてデータ前処理を行っていきましょう。データの状況を都度確認しながら、データ前処理を実行していくには、Jupyter Notebookは非常に有効だと思います。まだ、お使いでない方は是非試していただきたいと思います。

まず、データを取り込みましょう。pandas numpy reをインポートします。re正規表現を利用して文字列を操作するために必要なモジュールです。

前回保存しておいたcsvファイルをpandasのdataframeとしてdfに読み込みます。index_col=0として、ファイルの0列目(pythonでは0から始まる)をindex列として指定します。引数でこれを指定しないと、pandasが自動的に0から数値をindexとして割り付けてしまいます。

import pandas as pd
import numpy as np
import re

df = pd.read_csv('data/suumo_used_mansion.csv', index_col = 0)

info()にてdataframeの概要を見てみます。また、head()にて最初の5行もどんな感じか見ておきましょう。

f:id:akatak:20180915061142p:plain f:id:akatak:20180915061350p:plain

約23000行のデータ系列ですね。ところが、データ系列をExcel等に取り込んで眺めてみると分かるのですが、同じ物件のデータが場合によっては3個も4個もあるいはもっとあります。異なる業者が同じ物件をSUUMOさんのホームページに登録しているようです。

そこで重複行を削除してしまいましょう。pandasには「重複行を抽出」したり、「重複行を削除」するメソッドが準備されています。前者がduplicated()、後者がdrop_duplicates()です。

まず、duplicated()を利用して、どの位重複があるのかを確認しましょう。引数subsetで重複を確認する列を指定できます。また、keep=Falseと指定すると、重複行全てTrueを返します(keep='first'の指定で、重複行のうち最初に出てくる行がFalseとなり、それ以外がTrue。keep='last'とすると最後の行がFalseとなり、それ以外の行がTrueとなります)。sum()で合計すると重複行をカウントできます。

f:id:akatak:20180915075911p:plain

9216も重複していますね。 物件名が「オリエンタル大森」のもののみ抽出して確認すると、以下の通り。

f:id:akatak:20180915080122p:plain

重複行の最初の行のみ残して、後は削除してしまいましょう。

df.drop_duplicates(subset=['物件名', '価格', '専有面積', '間取り'], keep='first', inplace=True)

ただし、Excel等に取り込んだデータをよく見てみると物件名が「ザ・パークハウス小石川春日」と「◆ザ・パークハウス小石川春日◆2駅4路線利用可!」と異なっていますが、その他の列を見ると専有面積もバルコニー面積も価格も住所も一緒のものがあります。

完全ではないにしろ、ある程度きれいにしておきたいところですので、これらも除外しておきましょう。

f:id:akatak:20180915081308p:plain

さて、相当数を削除した結果、indexが歯抜け状態になっていますので、indexを付け直しましょう。

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

ここで、データ系列の概要を見てみると、以下の通り、データ数は14631個になりました。

f:id:akatak:20180915081840p:plain

さて、ここから、賃貸物件と同様に、データを加工していきます。

# 文字列を複数文字で split。住所から「都県」列・「市区」列を新たに作成
df['都県'] = df['住所'].apply(lambda x: re.split('[都県]', x)[0]) 
df['市区'] = df['住所'].apply(lambda x: re.split('[都県市区]', x)[1]) 

次に、築年数を計算するために以下の加工・計算を行います。

from datetime import datetime

df['築年'] = df['築年月'].apply(lambda x: x.split('年')[0])
df['築月'] = df['築年月'].apply(lambda x: x.split('年')[1].strip('月'))
df['築月'] = df['築月'].apply(lambda x: x if x != '' else '6')

df['築年月'] = df['築年'] +'/'+ df['築月'] +'/15'  # 一律に日付を15日に設定
df['築年月'] = pd.to_datetime(df['築年月'])  #  stringからdatetimeオブジェクトに変換
df['築日数'] = df['築年月'].apply(lambda x:(datetime.today() - x).days) #今日までの日数計算
df['築年数'] = df['築日数']/365.25 # 年数(概算)を計算(うるう年も考慮)

計算の過程で作った「築年」「築月」「実日数」の列は分析でも不要なので、削除してしまいます。

df.drop(['築年','築月','築日数'], axis=1, inplace=True)

続きまして、「1億円」「1080万円」等のStringとなっているものを、数値に変換していきます。まずは、「4998万円※権利金含む」などの記載があるものの処理から。

df['価格'] = df['価格'].apply(lambda x: x.split('※')[0] if '※権利金' in x else x)

次に「億円」表示のものを「万円」に変換。また、「1億800万円」のような表示を「万円」に変換します。

df['価格'] = df['価格'].apply(lambda x: x.strip('億円') + '0000万円' if '億円' in x else x)
df['価格'] = df['価格'].apply(lambda x: str(int(x.split('億')[0]) *10000 + int(x.split('億')[1].split('万円')[0])) + '万円' if '億' in x else x)

念のため、下2文字が「万円」でないものがあるか確認すると、 f:id:akatak:20180915083628p:plain

一つだけだけありました。仕方が無いので、個別で調整します。

df.iloc[2761,2] = '2335.6万円'

後は、範囲で指定しているものもありますが、これも個別で上下の平均値でも入れておきましょう。 f:id:akatak:20180915083937p:plain f:id:akatak:20180915083947p:plain

価格の仕上げとして、数値化しておきます。

df['価格'] = df['価格'].apply(lambda x: int(float(x.strip('万円')) * 10000))

間取り、部屋数、路線、駅、手段、分は賃貸物件の場合と同様に処理します。

df['間取り'] = df['間取り'].str.replace('ワンルーム', '1RM')
df['部屋数'] = df['間取り'].apply(lambda x: int(re.search('[0-9]+', x).group(0)))
df['路線'] = df['最寄駅'].apply(lambda x: x.split('「')[0])
df['駅'] = df['最寄駅'].apply(lambda x: x.split('「')[1].split('」')[0])
df['手段'] = df['最寄駅'].apply(lambda x: x.split('「')[1].split('」')[1][0])
df['手段'] = df['手段'].apply(lambda x: x.replace('徒', '歩')) # 手段の「徒」を「歩」に変えます。
df['分'] = df['最寄駅'].apply(lambda x: re.search('[0-9]+',x.split('「')[1].split('」')[1]).group(0))

あと、今回は物件階の影響も調べたいので、加工していきましょう。 データを一覧すると f:id:akatak:20180915084551p:plain

うわー、仕方ないですね。一つ一つ変換していきましょう。

df['物件階'] = df['物件階'].str.replace('B2階','-2階')
df['物件階'] = df['物件階'].str.replace('B1階','-1階')
df['物件階'] = df['物件階'].str.replace('2-3階','2.5階')
df['物件階'] = df['物件階'].str.replace('5-6階','5.5階')
df['物件階'] = df['物件階'].str.replace('3-4階','3.5階')
df['物件階'] = df['物件階'].str.replace('B1-1階','-0.5階')
df['物件階'] = df['物件階'].str.replace('1-2階','1.5階')
df['物件階'] = df['物件階'].str.replace('4-5階','4.5階')
df['物件階'] = df['物件階'].str.replace('6-7階','6.5階')
df['物件階'] = df['物件階'].str.replace('1-3階','2階')
df['物件階'] = df['物件階'].str.replace('B1-2階','1階')
df['物件階'] = df['物件階'].str.replace('8-9階','8.5階')
df['物件階'] = df['物件階'].str.replace('1--1階','0.5階')
df['物件階'] = df['物件階'].str.replace('B1-3階','1階')
df['物件階'] = df['物件階'].str.replace('1-1階','1階')
df['物件階'] = df['物件階'].str.replace('46-47階','46.5階')
df['物件階'] = df['物件階'].str.replace('37-38階','37.5階')
df['物件階'] = df['物件階'].str.replace('B1.5階','-1.5階')
df['物件階'] = df['物件階'].str.replace('B2階','-2階')
df['物件階'] = df['物件階'].apply(lambda x: float(x.strip('階')) if x != '-' else x.replace('-', ''))

総戸数も数値に変換します。

df['総戸数'] = df['総戸数'].apply(lambda x: int(x.strip('戸')) if x !='-' else x.replace('-', ''))

リフォームは有無のみ「リフォームflag」に入れます。有り=True・無し=Falseです。

df['リフォームflag'] = df['リフォーム'] != "['-\\r\\n\\t\\t\\t']"

今回は、単価を分析の対象としたいので、追加します。

df['単価'] = df['価格'] / df['専有面積'] #単価(単位):円/㎡

権利形態としてとして各種「借地権」の物件もありますが、分析の対象は「所有権」のものに絞りたいと思います。最後に加工後のデータを保存します。

df_owner = df[df['権利形態'] == '所有権']
df_owner.to_csv('data/suumo_used_mansion_mod_owner.csv')

以上、駆け足でしたがいかがでしたか。結構骨が折れる作業かと思います。慣れてくると、あーでもないこーでもないと考えることを楽しみながら、加工できますので、作業自体はそんなに苦痛ではないですよ。

分析は次回にしましょう。それでは。