線性回歸:預(yù)測(cè)上海車牌成交價(jià)格 - 為程序員服務(wù) http://ju.outofmemory.cn/entry/246310
上海是全國最早實(shí)行私人轎車牌照拍賣方式來控制交通流量的城市,需要通過投標(biāo)拍賣的形式購買車牌。
而車牌的拍賣并不是簡(jiǎn)單的價(jià)高者得, 服務(wù)器 只接受最低可成交價(jià)上下300元區(qū)間內(nèi)的出價(jià),意思就是說,如果現(xiàn)在最低成交價(jià)是60000,你出價(jià)范圍必須在57000~63000之間,并且這個(gè)最低成交價(jià)是在不斷變化的,到了最后幾分價(jià)格上漲太快根本來不及操作。
如果能提前根據(jù)歷年 數(shù)據(jù) 預(yù)測(cè)本次拍賣成交價(jià)格,那么成功率必定比盲拍要高很多。
下面就嘗試使用 scikit-learn 這個(gè)機(jī)器學(xué)習(xí)工具庫來進(jìn)行價(jià)格預(yù)測(cè)。
收集數(shù)據(jù)
在 上海國拍競(jìng)標(biāo)網(wǎng) 等其他公告 網(wǎng)站 收集到了最近幾年的完整拍賣記錄:

另外,一些重要的 政策 搜集也很重要,如果出現(xiàn)異常值可以剔除:
2013年4月,開始有最低警示價(jià)的約束。
2013年10月,取消警示價(jià)。
2013年11月,重啟警示價(jià)。
2014年1月,試行車牌拍賣年度統(tǒng)一警示價(jià)72600元。同時(shí),將實(shí)行個(gè)人、單位分場(chǎng)拍賣。
2014年4月,投標(biāo)卡參拍增至6次。
2014年7月,拍牌者必須提交駕駛證件。
2014年11月,二手車牌照不能私下交易,買賣納入統(tǒng)一拍賣平臺(tái)。
2015 年1月,警示價(jià)設(shè)定標(biāo)準(zhǔn)為剔除價(jià)格波動(dòng)異常月份后,取最近三個(gè)月拍賣成交均價(jià)的加權(quán)平均值。
加載數(shù)據(jù)
數(shù)據(jù)和政策都收集好了,接下來就是數(shù)據(jù)預(yù)處理部分。預(yù)處理主要是讀取數(shù)據(jù)并除掉無用的列,重命名中文列名等等:
df = pd.read_csv('data.csv')df.rename(columns={'拍賣時(shí)間':'date','投放數(shù)量':'number',/ '警示價(jià)':'start','最低成交價(jià)':'low', '平均成交價(jià)':'mean','投標(biāo)人數(shù)':'enrollment'}, inplace=True)df = df[['date','number','start','low','mean','enrollment']]df = df[:-1]
這是瀏覽一下數(shù)據(jù)大概是這樣的:
date
number
start
low
mean
enrollment
0
2013-4
11000
83600
83900
84101
26174
1
2013-5
9000
79900
80700
80803
222243
…
…
…
…
…
…
33
2016-2
8363
80600
83200
83244
196470
初步觀察
我們可以初步瀏覽一下數(shù)據(jù)的關(guān)聯(lián)度。將發(fā)放的車牌數(shù)、警示價(jià)、最低成交價(jià)、參與人數(shù)四個(gè)數(shù)據(jù)作為 x 變量,將平均成交價(jià)作為 y 變量,繪制他們的散點(diǎn)圖觀察分布情況:
import seaborn as sns%matplotlib inlinesns.pairplot(df,x_vars=['number','start','low','enrollment'],y_vars='mean',size=7,kind='reg')
結(jié)果如下:

我看見了什么!一條直線!這簡(jiǎn)直就是完美吻合??!

定睛一看,是最低成交價(jià)。

這本就該完美吻合,最低成交價(jià)不會(huì)比平均成交價(jià)低超過300元。
然后再想一下,在預(yù)測(cè)價(jià)格的時(shí)候,我們可以獲取到的數(shù)據(jù)是:
日期:2016-3
投放數(shù)量:8310
警示價(jià):80600
投標(biāo)人數(shù):目前未知,當(dāng)天可知,假設(shè)為 200000
所以我們需要關(guān)注的是:投放數(shù)量、警示價(jià)、投標(biāo)人數(shù),之間的關(guān)系。
單獨(dú)繪制警示價(jià)和成交均價(jià)的柱狀圖:
df.plot(y=['start','mean'],kind='Bar',figsize=[16,6])
觀察一下警示價(jià)與成交均價(jià)之間的關(guān)系:

似乎差值十分接近啊,將每個(gè)月的差值單獨(dú)繪制看看:
df['mean-start'] = df['mean']-df['start']ax = df.plot(y=['mean-start'],figsize=(16,6))ax.set_xticks(df.index)ax.set_xticklabels(df.date,rotation=90)
繪制結(jié)果如下:

本來差值穩(wěn)定在1000左右,結(jié)果到了2015年突然失去了控制??赡芎腿藬?shù)陡增有關(guān),也有可能和某些政策出臺(tái)有關(guān),暫且就先這樣。
線性回歸
我們先用最基礎(chǔ)的線性回歸模型來 測(cè)試 一下。
首先先把 X 和 y 單獨(dú)取出來,選擇投放數(shù)量、警示價(jià)、投標(biāo)人數(shù)作為 X 變量,將成交均價(jià)作為 y 變量:
feature_cols = ['number', 'start', 'enrollment']X = df[feature_cols]y = df['mean']
然后區(qū)分訓(xùn)練集和測(cè)試集:
from sklearn.cross_validation import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)print X_train.shape # (25,3)print y_train.shape # (25,)print X_test.shape # (9,3)print y_test.shape # (9,)
然后進(jìn)行線性回歸分析:
from sklearn.linear_model import LinearRegressionlinreg = LinearRegression()linreg.fit(X_train, y_train)
看一下各個(gè)特征的系數(shù):
print linreg.intercept_# 991.359968817zip(feature_cols, linreg.coef_)# [('number', 0.31616020072308526),# ('start', 0.9541013878172625),# ('enrollment', 0.015502778511636449)]
也就是說,最后的公式是:
y = 991.36 + 0.316 *投放數(shù)量 + 0.954 *警示價(jià) + 0.0155 * 投標(biāo)人數(shù)
這個(gè)結(jié)果合情合理,和我們一開始觀察數(shù)據(jù)的結(jié)果接近。
接下來評(píng)測(cè)一下這個(gè)模型的準(zhǔn)確性,用模型預(yù)測(cè)測(cè)試數(shù)據(jù)集,計(jì)算預(yù)測(cè)結(jié)果和真實(shí)結(jié)果的 RMSE 值:
from sklearn import metricsy_pred = linreg.predict(X_test)zip(y_test,y_pred)print np.sqrt(metrics.mean_squared_error(y_test, y_pred))
計(jì)算結(jié)果是:RMSE = 1174.38,并不是很理想。RMSE 可以用于模型評(píng)價(jià),后面優(yōu)化 參數(shù) 的時(shí)候可以進(jìn)行模型之間的對(duì)比。
直觀對(duì)比一下原始數(shù)據(jù)和預(yù)測(cè)數(shù)據(jù):
fig, ax = plt.subplots()x = np.arange(len(y_test))ax.bar(x,y_test-y_pred,width=w)fig.show()
顯示結(jié)果如下:

而理論上,誤差值不能超過300才能搶到牌照。可見預(yù)測(cè)的結(jié)果并沒太多作用。
觀察到模型中投標(biāo)人數(shù)的影響似乎不是很大,不妨把投標(biāo)人數(shù)從模型中去除,看看結(jié)果如何:
feature_cols = ['number', 'start']X = df[feature_cols]y = df['mean']X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)linreg.fit(X_train, y_train)y_pred = linreg.predict(X_test)print np.sqrt(metrics.mean_squared_error(y_test, y_pred))
計(jì)算結(jié)果是:RMSE = 1318.04,似乎誤差更大了。
模型雖然不準(zhǔn),不過預(yù)測(cè)一下2016年3月的成交均價(jià)還是可以的。從公告來看,發(fā)放牌照8310張,警示價(jià)80600,假設(shè)參與人數(shù)為20000,那么預(yù)計(jì)成交價(jià):
linreg.predict([[8310,80600,200000]])
結(jié)果是:83619.78 元。
小結(jié)
在 Coursera 上跟著學(xué) Machine Learning 也有段時(shí)間了,一直是跟著教程使用 GraphLab Create 來做。今天第一次用 scikit-learn 進(jìn)行數(shù)據(jù)分析,即使是最最最簡(jiǎn)單的線性回歸也是一路磕磕絆絆,學(xué)藝不精吶。
如果有更好的想法歡迎和我交流:)
從結(jié)果來看,這個(gè)模型預(yù)測(cè)的結(jié)果誤差會(huì)在 2000 元以內(nèi),并不能達(dá)到最后需要的 300 元的要求,只能使用其他方法來解決了。
預(yù)知后事如何。

我解決了也不會(huì)告訴你。
原文http ://blog.callmewhy.com/2016/03/13/predict-plate-price-with-linear-regression/