Pythonによる不動産情報のデータ取得&分析(5)【売却物件/データ前処理編】
さて、前回記載したスクリプトで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行もどんな感じか見ておきましょう。
約23000行のデータ系列ですね。ところが、データ系列をExcel等に取り込んで眺めてみると分かるのですが、同じ物件のデータが場合によっては3個も4個もあるいはもっとあります。異なる業者が同じ物件をSUUMOさんのホームページに登録しているようです。
そこで重複行を削除してしまいましょう。pandasには「重複行を抽出」したり、「重複行を削除」するメソッドが準備されています。前者がduplicated()
、後者がdrop_duplicates()
です。
まず、duplicated()
を利用して、どの位重複があるのかを確認しましょう。引数subsetで重複を確認する列を指定できます。また、keep=Falseと指定すると、重複行全てTrueを返します(keep='first'の指定で、重複行のうち最初に出てくる行がFalseとなり、それ以外がTrue。keep='last'とすると最後の行がFalseとなり、それ以外の行がTrueとなります)。sum()
で合計すると重複行をカウントできます。
9216も重複していますね。 物件名が「オリエンタル大森」のもののみ抽出して確認すると、以下の通り。
重複行の最初の行のみ残して、後は削除してしまいましょう。
df.drop_duplicates(subset=['物件名', '価格', '専有面積', '間取り'], keep='first', inplace=True)
ただし、Excel等に取り込んだデータをよく見てみると物件名が「ザ・パークハウス小石川春日」と「◆ザ・パークハウス小石川春日◆2駅4路線利用可!」と異なっていますが、その他の列を見ると専有面積もバルコニー面積も価格も住所も一緒のものがあります。
完全ではないにしろ、ある程度きれいにしておきたいところですので、これらも除外しておきましょう。
さて、相当数を削除した結果、indexが歯抜け状態になっていますので、indexを付け直しましょう。
df.reset_index(drop=True, inplace=True)
ここで、データ系列の概要を見てみると、以下の通り、データ数は14631個になりました。
さて、ここから、賃貸物件と同様に、データを加工していきます。
# 文字列を複数文字で 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文字が「万円」でないものがあるか確認すると、
一つだけだけありました。仕方が無いので、個別で調整します。
df.iloc[2761,2] = '2335.6万円'
後は、範囲で指定しているものもありますが、これも個別で上下の平均値でも入れておきましょう。
価格の仕上げとして、数値化しておきます。
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))
あと、今回は物件階の影響も調べたいので、加工していきましょう。 データを一覧すると
うわー、仕方ないですね。一つ一つ変換していきましょう。
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')
以上、駆け足でしたがいかがでしたか。結構骨が折れる作業かと思います。慣れてくると、あーでもないこーでもないと考えることを楽しみながら、加工できますので、作業自体はそんなに苦痛ではないですよ。
分析は次回にしましょう。それでは。