背景
車中泊ができて格好良い車がほしい!
→マツダのCX-8プロアクティブ良くない?
→中古車サイト、探すのめんどくさい
→最も求めている車をデータサイエンスの力で探し出そう!←イマココ
というわけで、中古車サイトからデータを引っ張ってきて希望に合う車を見つけるための分析をしてみました。
最終的にそれっぽい車が見つかれば良いので、細かいことはあんまり考えずにやっていきます。
実装
スクレイピング
今回は中古車販売サイトのカーセンサーさんからデータをスクレイピングしてきました。
一応robots.txtは確認済です。
以下コード
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests
from bs4 import BeautifulSoup
import time
from tqdm import tqdm
cars_info = []
for i in tqdm(range(17)):
i += 1
target_url = f"https://www.carsensor.net/usedcar/bMA/s094/index{i}.html?LP=MA_S094"
r = requests.get(target_url)
soup = BeautifulSoup(r.text, "lxml")
for car_elem in soup.find_all(class_="cassetteMain__carInfoContainer"):
total_price_main = car_elem.find(class_="totalPrice__mainPriceNum").get_text()
try:
total_price_sub = car_elem.find(class_="totalPrice__subPriceNum").get_text()
except AttributeError: # 小数点以下がない場合
total_price_sub = ""
total_price = float(total_price_main + total_price_sub)
car_info = []
for elem in car_elem.find_all(class_="specList__emphasisData"):
car_info.append(elem.get_text())
cars_info.append(car_info + [total_price])
time.sleep(1)
これでcars_infoリスト内に総額、走行距離、年式を取得できました。
分析
データが揃ったら、次は前処理をします。
まずは型変更。
取得時に型変更をしておけばよかった、、、(直すのもめんどくさいのでここで処理)
df = pd.DataFrame(cars_info, columns=["model_year", "mileage", "price"])
df["mileage"] = df["mileage"].astype(float)
df["model_year"] = df["model_year"].astype(int)
外れ値データがあったので真っ当そうなデータだけに絞ります。
df = df.query("mileage<20").reset_index(drop=True)
きれいなデータになったところで可視化して見ていきます。
まずは簡単にヒストグラム。
for col in df.columns:
plt.hist(df[col], bins=30)
plt.title(col)
plt.show()
ボリュームゾーンが一目瞭然ですね。
次は散布図を描いてみます。
今回は「とにかく走行距離に対してお得感のある車がほしい」ので、
年式は一旦置いておいて走行距離と価格の関係性に着目します。
plt.scatter(df["mileage"], df["price"], s=1)
負に相関してそうですがちょっと線形っぽくないところもあるので、
価格を対数変換してみます。
plt.scatter(df["mileage"], np.log(df["price"]), s=1)
ちょっとはマシになったかな。
本来であればちゃんと検証した方がいいですが、今回は見た目で対数変換後の値を採用。
df["log_price"] = np.log(df["price"])
線形回帰をして回帰直線を引いてみます。
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(df[["mileage"]].values, df["log_price"])
pred_price = lr.predict(df[["mileage"]].values)
plt.scatter(df["mileage"], df["log_price"], s=1)
plt.plot(df["mileage"], pred_price, color="red")
大体こんなもんでしょう。
この回帰直線がいわば”相場”なので、
回帰直線の下側かつ回帰直線から最も遠いデータが”最もお得な車”と考えられます。
error = (pred_price - df['log_price'])
df.iloc[error.argmax()]
model_year 2019.000000
mileage 3.000000
price 189.500000
log_price 5.244389
Name: 215, dtype: float64
これで最もお得な車を特定することができました。
3万キロしか走っていなくて190万円程度はかなりお得です。
後日談
やっぱ車中泊するならSUVじゃなくない?となりお蔵入り。
諸々雑ですがCX−8がほしい方は参考にどうぞ。