不動産DX記事まとめ

物件データのクレンジングを行う

作成者: 片山 幹健|24/03/31 4:57

ZENRIN MAPs API活用の記事の続きです。

データのクレンジングについて

前回は、ZENRIN MAPs APIを利用して最新の物件情報のデータを取得しましたが、今回はそのデータを使用して、自社で保有している他の物件情報との統合、データクレンジングを行なっていきます。

例えば、Webクローリングによって以下のような売買情報サイトに掲載されている物件情報のデータを取得しているとします。

クローリングによるデータ取得の参考記事

売却想定価格 住所 築年数 構造 土地面積 建築面積 利回り
2億8,600万円値下げ 港区西麻布2丁目 ビル(一棟)東京都港区西麻布2丁目 1990年築 鉄骨造 79.4m2 204.57m2 6.01%
2億9,000万円値下げ 渋谷区神宮前4丁目 ビル(一棟)東京都渋谷区神宮前4丁目 2014年築 木造 50.1m2 78.93m2 3.50%
25億1,000万円 港区西麻布1丁目 ビル(一棟)東京都港区西麻布1丁目 2023年築(新築) 鉄骨造 85.45m2 567.39m2 2.78%
2億8,600万円 港区西麻布2丁目 ビル(一棟)東京都港区西麻布2丁目 1990年築 鉄骨造 79.4m2 204.57m2 6.01%
19億1,840万円 港区芝大門2丁目 ビル(一棟)東京都港区芝大門2丁目 2022年築 鉄骨造 131.24m2 730.76m2 3.44%

上記の情報は、住所のところが丁目までしかなく、そのほかの項目にも表記がバラバラだったり、数値で欲しい項目が数値にはなっていなかったりします。

そこで、まずは分析しやすいようにデータの各項目を整えるところからスタートします。これをデータクレンジングと呼んでいます。

データクレンジングについて

今回は、表記の揺らぎや数値データとして欲しい項目を単位や余計なテキスト情報を除いて数値のみにする処理を行います。完成形のイメージは下記です。

売却想定価格 住所 築年数 構造 土地面積 建築面積 利回り
286000000 港区西麻布2丁目 1990 鉄骨造 79.4 204.57 6.01
290000000 渋谷区神宮前4丁目 2014 木造 50.1 78.93 3.5
2510000000 港区西麻布1丁目 2023 鉄骨造 85.45 567.39 2.78
286000000 港区西麻布2丁目 1990 鉄骨造 79.4 204.57 6.01
1918400000 港区芝大門2丁目 2022 鉄骨造 131.24 730.76 3.44
650000000 渋谷区東2丁目 1997 SRC 115.18 289.72 4.67

1. 環境の準備: データをクレンジングするための環境を準備します。

今回はGoogle Colabとスプレッドシートを使用する方法をご紹介します。

Google Colabは、無料でPythonというプログラミングコードの実行環境を整備できるサービスです。Googleのサービスなので、同じGoogleサービスであるスプレッドシートとの連携もしやすいです。

Google Colabについて

以下が、データクレンジングを行うためのPythonのサンプルコードです。指定したスプレッドシートのデータをクレンジングし、その結果をProcessed Dataというシートを作成して転記するようにしています。
 
import gspread
from google.auth import default
from google.colab import auth
import pandas as pd
import re

# Google Colabでの認証
auth.authenticate_user()
creds, _ = default()
gc = gspread.authorize(creds)

# スプレッドシートのキー
SPREADSHEET_KEY = 'スプレッドシートのファイルID'

# データ前処理関数
# Function to convert '売却想定価格' to numerical value
def convert_price(value):
if pd.isna(value) or value.strip() == '':
return None
value = value.replace('億', '').replace('万円', '').replace('円', '').replace(',', '').replace('値下げ', '').replace('(消費税込)', '')
if '.' in value:
value = value.split('.')[0]
parts = value.split('万')
if len(parts) == 2 and parts[0] and parts[1]:
return int(parts[0]) * 100000000 + int(parts[1])
elif parts[0]:
return int(parts[0]) * 10000
return None

# Function to standardize address format
def standardize_address(address):
if pd.isna(address):
return address
address = address.split(' ')[0] # Remove additional text
# Convert full-width numbers to half-width
address = address.translate(str.maketrans('0123456789', '0123456789'))
return address

# Function to convert '築年数' to numerical year format
def convert_construction_year(year):
if pd.isna(year) or year.strip() == '':
return None
if isinstance(year, str):
# 年の部分を抽出
year = year.split('年')[0]

try:
# 和暦(昭和、平成、令和)を西暦に変換
if '昭和' in year:
return int(year.replace('昭和', '')) + 1925 - 1
elif '平成' in year:
return int(year.replace('平成', '')) + 1988 - 1
elif '令和' in year:
return int(year.replace('令和', '')) + 2018 - 1
# 西暦の場合はそのままの数値を返す
else:
return int(year)
except ValueError:
return None # 数値変換エラーの場合はNoneを返す
return None # 数値以外の場合はNoneを返す

# Function to extract numerical value for '土地面積' and '建築面積'
def extract_area(area):
if pd.isna(area) or '階' in area:
return float('nan')
# 'm'または'm²'の前にある数値を抽出
match = re.search(r'(\d+(\.\d+)?)\s*(m|㎡)', area)
if match:
return float(match.group(1)) # 最初の数値グループを浮動小数点数に変換
return float('nan') # 数値が見つからない場合はNaNを返す

# Function to convert '利回り' to numerical value
def convert_yield(value):
if pd.isna(value) or '―' in value or value.strip() == '':
return float('nan')
# 全角と半角のパーセント記号を考慮して、それより前の部分を抽出
if '%' in value:
value = value.split('%')[0]
elif '%' in value:
value = value.split('%')[0]
return float(value.strip())

# スプレッドシートからデータを読み込む
workbook = gc.open_by_key(SPREADSHEET_KEY)
worksheet = workbook.worksheet('シート1')
data = worksheet.get_all_values()
df = pd.DataFrame(data[1:], columns=data[0])

# データを前処理
df['売却想定価格'] = df['売却想定価格'].apply(convert_price)
df['住所'] = df['住所'].apply(standardize_address)
df['築年数'] = df['築年数'].apply(convert_construction_year)
df['土地面積'] = df['土地面積'].apply(extract_area)
df['建築面積'] = df['建築面積'].apply(extract_area)
df['利回り'] = df['利回り'].apply(convert_yield)

# 特殊な浮動小数点数をNoneに置き換える
df.replace([float('nan'), float('inf'), -float('inf')], None, inplace=True)

# 処理後のデータを新しいシートに書き込む
processed_worksheet = workbook.add_worksheet(title='Processed Data', rows=df.shape[0], cols=df.shape[1])
processed_worksheet.update([df.columns.values.tolist()] + df.values.tolist())
 
ここからは、個別にコードを解説していきます。
売却想定価格については、億や万円などが混ざっており、数値と文字が混在しているため、数値データとして扱いにくい状態になっています。
 
# Function to convert '売却想定価格' to numerical value
def convert_price(value):
if pd.isna(value) or value.strip() == '':
return None
value = value.replace('億', '').replace('万円', '').replace('円', '').replace(',', '').replace('値下げ', '').replace('(消費税込)', '')
if '.' in value:
value = value.split('.')[0]
parts = value.split('万')
if len(parts) == 2 and parts[0] and parts[1]:
return int(parts[0]) * 100000000 + int(parts[1])
elif parts[0]:
return int(parts[0]) * 10000
return None

 

そこで、文字を取り除いて、数値表記にするようコードを記述しています。

続いて、住所情報については、数値に全角と半角が混ざっているのが、データとして扱いにくくなる原因なので、全角数値を半角に統一するようコードを記述しています。

次の築年数については、西暦と和暦が混ざっていたりすると、データとして扱いにくいため、和暦を西暦に変換し、さらに文字情報を取り除いて数値のみで表記するようにコードを記述しています。

# Function to standardize address format
def standardize_address(address):
if pd.isna(address):
return address
address = address.split(' ')[0] # Remove additional text
# Convert full-width numbers to half-width
address = address.translate(str.maketrans('0123456789', '0123456789'))
return address
 
# Function to convert '築年数' to numerical year format
def convert_construction_year(year):
if pd.isna(year) or year.strip() == '':
return None
if isinstance(year, str):
# 年の部分を抽出
year = year.split('年')[0]

try:
# 和暦(昭和、平成、令和)を西暦に変換
if '昭和' in year:
return int(year.replace('昭和', '')) + 1925 - 1
elif '平成' in year:
return int(year.replace('平成', '')) + 1988 - 1
elif '令和' in year:
return int(year.replace('令和', '')) + 2018 - 1
# 西暦の場合はそのままの数値を返す
else:
return int(year)
except ValueError:
return None # 数値変換エラーの場合はNoneを返す
return None # 数値以外の場合はNoneを返す
 
最後に、土地面積や建築面積、利回りは単位の表記が半角、全角混ざっていたりすると、取り扱いがしにくいので、数値のみになるようコードを記述しています。
 
# Function to extract numerical value for '土地面積' and '建築面積'
def extract_area(area):
if pd.isna(area) or '階' in area:
return float('nan')
# 'm'または'm²'の前にある数値を抽出
match = re.search(r'(\d+(\.\d+)?)\s*(m|㎡)', area)
if match:
return float(match.group(1)) # 最初の数値グループを浮動小数点数に変換
return float('nan') # 数値が見つからない場合はNaNを返す

# Function to convert '利回り' to numerical value
def convert_yield(value):
if pd.isna(value) or '―' in value or value.strip() == '':
return float('nan')
# 全角と半角のパーセント記号を考慮して、それより前の部分を抽出
if '%' in value:
value = value.split('%')[0]
elif '%' in value:
value = value.split('%')[0]
return float(value.strip())
 

データクレンジングは一見するとかなり地味で大変ですが、データを分析しやすいように整えておかないと、データ分析ができないため、非常に重要な工程になります。

不動産DXに関するご相談などもお気軽にどうぞ。