基于京东手机销售数据用回归决策树预测价格
今天給大家推薦一個(gè)數(shù)據(jù)分析與挖掘的實(shí)戰(zhàn)項(xiàng)目案例“基于京東手機(jī)銷售數(shù)據(jù)用回歸決策樹預(yù)測價(jià)格”。該項(xiàng)目先基于京東手機(jī)銷售數(shù)據(jù)做出一系列分析后,利用回歸決策樹僅根據(jù)手機(jī)外部特征進(jìn)行價(jià)格預(yù)測。
本項(xiàng)目來自于實(shí)驗(yàn)樓《樓+ 數(shù)據(jù)分析與挖掘?qū)崙?zhàn)》第五期學(xué)員:Ted_Wei。
數(shù)據(jù)獲取
由于手機(jī)的價(jià)格以及評論數(shù)是需要經(jīng)過 javascript 渲染的動(dòng)態(tài)信息,單純用 requests 模塊是爬取不到的。我的解決方案是首先使用 selenium 的 webbrowser 模塊使用本地 Chrome 瀏覽器爬取每一款手機(jī)在京東的內(nèi)部id,價(jià)格,以及評論數(shù)。然后再根據(jù)爬取到的id找到每款手機(jī)的銷售頁面,爬取每款手機(jī)的關(guān)鍵參數(shù)。
爬蟲代碼parsing_code.py可通過此頁面: https://www.kaggle.com/ted0001/dm05-998494/data 獲取。
數(shù)據(jù)清洗
獲取到的數(shù)據(jù)包含1199行,21列,每一行代表一款正在銷售的手機(jī),每一列包含關(guān)于手機(jī)的一項(xiàng)參數(shù)(比如價(jià)格,內(nèi)存大小,像素,等等)。 獲取到的數(shù)據(jù)大多為自然語言,非數(shù)值信息,對于分析十分不友好。所以數(shù)據(jù)分析的重點(diǎn)在于,找到實(shí)際上的缺失信息(如‘無’,‘參考官方數(shù)據(jù)’等都可認(rèn)為是缺失數(shù)據(jù)NaN),以使用re模塊解析自然語言,提取其中有用的數(shù)值信息。
數(shù)據(jù)清洗的步驟其實(shí)相當(dāng)繁瑣,如果把代碼貼出也會(huì)占用過多篇幅,因此數(shù)據(jù)清洗的代碼data_clean.py可通過此頁面: https://www.kaggle.com/ted0001/dm05-998494/data 獲取。
數(shù)據(jù)分析
下面直接進(jìn)入正題,主要完成估計(jì)各品牌手機(jī)銷量并進(jìn)行比較分析、對決定手機(jī)價(jià)格因素的探索、嘗試用機(jī)器學(xué)習(xí)方法預(yù)測手機(jī)價(jià)格。
首先調(diào)用所需的模塊
import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)import matplotlib.pyplot as plt import seaborn as sns %matplotlib inlinefrom sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeRegressor from sklearn.metrics import mean_absolute_error as mae讀取已經(jīng)清洗好的數(shù)據(jù),數(shù)據(jù)也可在 https://www.kaggle.com/ted0001/dm05-998494/data 獲取。
data=pd.read_csv('../input/data-acquisitioncleaning/cleaned_data.csv') data=data.set_index(['Unnamed: 0']) #DataFrame在存儲(chǔ)為csv file以后原來的index會(huì)變?yōu)橐粋€(gè)列,因此要重新設(shè)置index data.shape輸出結(jié)果:
(1199,21)估計(jì)各品牌手機(jī)銷量并進(jìn)行比較分析
拿到清洗好的數(shù)據(jù)后的第一個(gè)想法,便是對各個(gè)品牌的手機(jī)的銷量進(jìn)行一個(gè)比較。由于京東只顯示了每部手機(jī)的評論數(shù)量而不是具體銷量,我們只好默認(rèn)評論數(shù)量comments和銷量成正比,從而估計(jì)各個(gè)手機(jī)品牌的銷量占比
pie_plt=data.groupby(['brand']).sum()['comments'].sort_values(ascending=False)#統(tǒng)計(jì)每個(gè)品牌評論總數(shù),以此作為我們對銷量的估計(jì) pie_plt輸出結(jié)果:
brand HUAWEI 11320592.0 Apple 9797100.0 XIAOMI 7995236.0 NOKIA 1324000.0 Philips 1227100.0 OPPO 1205300.0 vivo 1101330.0 K-Touch 793300.0 MEIZU 574900.0 SAMSUNG 539800.0 smartisan 364000.0 lenovo 275500.0 realme 157000.0 Meitu 109200.0 nubia 86000.0 chilli 58000.0 360 31000.0 ZTE 17000.0 Coolpad 12000.0 BlackBerry 10000.0 WE 90.0 Name: comments, dtype: float64我們可以對以上數(shù)據(jù)用扇形圖更好地展現(xiàn)出來
#繪制各個(gè)手機(jī)品牌估計(jì)銷量的占比扇形圖 fig,axes=plt.subplots(figsize=(12,12)) comment_sum=pie_plt.values.sum() percen=[np.round(each/comment_sum*100,2) for each in pie_plt.values] axes.pie(pie_plt.values,labels=pie_plt.index,labeldistance=1.2,autopct = '%3.1f%%') axes.legend([pie_plt.index[i]+': '+str(percen[i])+"%" for i in range(len(percen))],loc='upper right',bbox_to_anchor=(1, 0, 1, 1)) axes.set_title('Estimated Handphone Market Share in China') plt.show()輸出結(jié)果:
從以上扇形圖我們可以估計(jì),在中國市場銷量前三的手機(jī)品牌分別是華為,蘋果,小米,分別占到了總銷量的30.6%,26.5%,21.6%,剩下的品牌銷量則遠(yuǎn)遠(yuǎn)不及銷量前三的品牌。 為了驗(yàn)證以上估計(jì)的正確性,我查閱了2019年中國手機(jī)市場各個(gè)品牌份額的相關(guān)資料。發(fā)現(xiàn)華為,蘋果,小米的占比分別為34%,9%,12%,除華為外,小米和蘋果的市場份額數(shù)據(jù)均有較大出入。而vivo,oppo的市場份額則分別是19%和18%,與原來的數(shù)據(jù)差距就更大了。 如果京東的評論數(shù)量可以大致反映手機(jī)線上銷售情況的話,我想造成數(shù)據(jù)有如此大差異的原因可能在于沒有考慮中國市場的線下銷售。根據(jù)平時(shí)的生活經(jīng)驗(yàn),蘋果和小米的線下門店數(shù)量(特別是在小城市)是遠(yuǎn)遠(yuǎn)不及oppo,vivo的門店數(shù)量的。整個(gè)手機(jī)市場既包括線上市場也包括線下市場,而造成我統(tǒng)計(jì)到的數(shù)據(jù)與權(quán)威數(shù)據(jù)差異的原因,很可能是因?yàn)槲业臄?shù)據(jù)沒有包含線下銷售。 如果能夠獲得線下銷售數(shù)據(jù),那么也可以對以上推論進(jìn)行進(jìn)一步的驗(yàn)證。
還有一點(diǎn)出乎我意料的是,諾基亞和飛利浦手機(jī)的估計(jì)銷量占比竟然也都超過了3%(甚至超過vivo,oppo),于是便想看看這兩個(gè)品牌分別銷售哪種價(jià)位手機(jī)
data[(data['brand']=='NOKIA')|(data['brand']=='Philips')]['price'].median()#諾基亞和飛利浦手機(jī)價(jià)格中位數(shù)輸出結(jié)果:
208.5 data[(data['brand']=='NOKIA')|(data['brand']=='Philips')]['price'].mean()#諾基亞和飛利浦手機(jī)價(jià)格平均數(shù)輸出結(jié)果:
336.1190476190476這樣看來,諾基亞和飛利浦的手機(jī)價(jià)格多在200-300元左右,再根據(jù)item id訪問京東網(wǎng)站后,發(fā)現(xiàn)果不其然,這兩個(gè)品牌所銷售的大多是功能機(jī)。(諾基亞有部分智能手機(jī)) 在這個(gè)智能手機(jī)已經(jīng)全面普及的年代,想不到功能機(jī)也還是有它的一畝三分地,并沒有完全被市場淘汰(尤其是線上銷售渠道)。這可能是因?yàn)椴糠掷先巳匀桓?xí)慣使用功能機(jī),以及功能機(jī)待機(jī)時(shí)間長,鈴聲音量大,能滿足部分人群的特殊需求。
對決定手機(jī)價(jià)格因素的探索
另一個(gè)值得研究的問題便是手機(jī)的價(jià)格和手機(jī)配置參數(shù)的關(guān)系。為了讓我們有一個(gè)整體把握,我們會(huì)先畫出各個(gè)數(shù)值數(shù)據(jù)間的correlation matrix,然后再探索非數(shù)值數(shù)據(jù)(categorical data),如品牌,屏幕材料,對價(jià)格的影響。
畫出Correlation Matrix 并進(jìn)行分析
由于蘋果手機(jī)和安卓手機(jī)的價(jià)格、配置差異較大,而且蘋果手機(jī)配置參數(shù)在我們的數(shù)據(jù)集中大多缺失,我們研究這個(gè)問題時(shí),就暫且只考慮安卓手機(jī)。(價(jià)格為9999的手機(jī)是華為將于2019年7月發(fā)行的榮耀X9,由于該型號(hào)價(jià)格異常,故也將其從我們的分析中排除。)
correlation=data[(data['brand']!='Apple')&(data['price']!=9999)].corr() #繪制相應(yīng)correlation matrix的heatmap fig,axes=plt.subplots(figsize=(8,8)) cax=sns.heatmap(correlation,vmin=-0.25, vmax=1,square=True,annot=True) axes.set_xticklabels(['RAM', 'ROM', 'battery', 'comments', 'price', 'rear camera','resolution', 'screen size', 'weight']) axes.set_yticklabels(['RAM', 'ROM', 'battery', 'comments', 'price', 'rear camera','resolution', 'screen size', 'weight']) axes.set_title('Heatmap of Correlation Matrix of numerical data') plt.show()輸出結(jié)果:
從上圖我們可以看出,價(jià)格 price 和存儲(chǔ)空間 ROM 以及內(nèi)存 RAM 的關(guān)聯(lián)度最大,分別達(dá)到了0.71和0.68。其次便是電池容量battery,后置攝像頭個(gè)數(shù)rear camera,屏幕大小screen size,關(guān)聯(lián)度都分別達(dá)到了0.42。這也比較符合我們常識(shí)性的判斷。 我們還可以很明顯的看到,評價(jià)個(gè)數(shù)comments的column和row都呈現(xiàn)深紫色,代表comments和各個(gè)數(shù)值參數(shù)的關(guān)聯(lián)都很小。 (由于我們的數(shù)據(jù)集缺失值較多,在舍棄掉部分缺失值的行后,以上correlation matrix的數(shù)值會(huì)出現(xiàn)一定程度的變化)
手機(jī)品牌對手機(jī)價(jià)格影響的探索
當(dāng)然,決定手機(jī)價(jià)格很關(guān)鍵的因素還有一些非數(shù)值數(shù)據(jù)(categorical data)。最容易想到的便是手機(jī)的品牌了。
data.groupby(['brand']).median()['price'].sort_values(ascending=False).values.std() #計(jì)算不同品牌價(jià)格中位數(shù)集合的標(biāo)準(zhǔn)差輸出結(jié)果:
1409.0576123336064以上數(shù)據(jù)也可以通過柱狀圖來更直觀地展示。
bar_plt=data.groupby(['brand']).median()['price']fig,axes=plt.subplots(figsize=(20,8)) axes.bar(bar_plt.index,bar_plt.values) axes.set_title('Median price of handphones of various brands')輸出結(jié)果:
Text(0.5, 1.0, 'Median price of handphones of various brands')我們可以看到,各個(gè)品牌手機(jī)中位數(shù)價(jià)格層次不齊,這也和我們的常識(shí)性判斷吻合,因?yàn)椴煌謾C(jī)品牌的定位以及消費(fèi)群體均有較大差異。
不同屏幕材料對手機(jī)價(jià)格影響的探索
還有一個(gè)很關(guān)鍵的因素其實(shí)是手機(jī)的屏幕材料,我們也可以用同樣的方法比較不同屏幕材料對價(jià)格的影響。
data.groupby(['screen material']).median()['price'].sort_values(ascending=False).values.std() #計(jì)算不同屏幕材料價(jià)格中位數(shù)集合的標(biāo)準(zhǔn)差輸出結(jié)果:
1523.0026019740856各種屏幕材料的手機(jī)的價(jià)格中位數(shù)展示如下
data.groupby(['screen material']).median()['price'].sort_values(ascending=False)輸出結(jié)果:
screen material Dynamic AMOLED 5999.0 OLED曲面屏 5488.0 OLED 4288.0 Super AMOLED 2998.0 AMOLED曲面屏 2908.0 LCD 2399.0 AMOLED 2299.0 TFT LCD(IPS) 1999.0 LTPS 1999.0 IPS 1399.0 TFT 1199.0 Name: price, dtype: float64用柱狀圖展示如下:
bar_plt2=data.groupby(['screen material']).median()['price']fig,axes=plt.subplots(figsize=(18,8)) axes.bar(bar_plt2.index,bar_plt2.values) axes.set_title('Median price of handphones of various screen materials')輸出結(jié)果:
Text(0.5, 1.0, 'Median price of handphones of various screen materials')可以注意到的是,以上價(jià)格數(shù)值均在千元以上,而我們的數(shù)據(jù)集中還包含有價(jià)格很低廉的功能機(jī),那它們的屏幕又都是什么材料呢?
data[(data['brand']=='NOKIA')|(data['brand']=='Philips')]['screen material'].value_counts()輸出結(jié)果:
TFT 27 IPS 3 TFT LCD(IPS) 1 Name: screen material, dtype: int64可以看到,諾基亞和飛利浦的手機(jī)(大多為功能機(jī))的屏幕材料大多為TFT和IPS。
我們又可以反過來看看又到底是哪些價(jià)位的手機(jī)在使用TFT和IPS呢?
#繪制屏幕材料為IPS或TFT手機(jī)的價(jià)格分布圖 hist_plot=data[(data['screen material']=='IPS')|(data['screen material']=='TFT')]['price']#查看所有屏幕材料為IPS或TFT手機(jī)的價(jià)格 sns.distplot(hist_plot) plt.title('Price Distribution Plot of Handphones Whose Screen Material is TFT or IPS ')輸出結(jié)果:
Text(0.5, 1.0, 'Price Distribution Plot of Handphones Whose Screen Material is TFT or IPS ')通過觀察以上分布圖以及進(jìn)一步在數(shù)據(jù)集data中查看屏幕材料為TFT或IPS的手機(jī)發(fā)現(xiàn),IPS主要用于華為的中低端手機(jī),價(jià)格在千元以下,或者1300元左右。其中,價(jià)格在200元以下的功能機(jī)的屏幕材料均為TFT。 出乎我意料的是,也有部分高端手機(jī)使用的是IPS或TFT材料,比如華為的榮耀V20,蘋果的iphone 8,使用的是IPS材料;華為mate20和華為p20使用的均為TFT材料,這些手機(jī)的價(jià)格都在3500元以上。 通過以上的探索分析我們可以知道,高端智能機(jī)和低端功能機(jī)所使用的屏幕材料也很可能是一樣的。當(dāng)然我們也不排除,同樣是TFT或IPS材料,它們內(nèi)部也可能有區(qū)別。
嘗試用機(jī)器學(xué)習(xí)方法預(yù)測手機(jī)價(jià)格
在前面的小節(jié)中,我們探索了決定手機(jī)價(jià)格的幾大因素,手機(jī)存儲(chǔ)空間ROM,內(nèi)存RAM,以及品牌,屏幕材料等都是決定手機(jī)價(jià)格的關(guān)鍵因素。 在這一小節(jié)中,我會(huì)使用回歸決策樹(Regression Decision Tree)的算法僅僅根據(jù)手機(jī)的外部特征來預(yù)測手機(jī)的價(jià)格。決策數(shù)的特征值僅僅采用了手機(jī)的品牌brand、后置攝像頭數(shù)量rear camera、以及手機(jī)重量weight作為我們的特征(feature),目標(biāo)(target)當(dāng)然則是我們的價(jià)格price 這樣做的原因一來是因?yàn)镽OM和RAM存在太多的缺失值。如果選取這兩個(gè)值做為特征,那么我們會(huì)丟失掉太多訓(xùn)練數(shù)據(jù)。 二來是想嘗試在不知道手機(jī)具體配置,僅僅通過觀察測量手機(jī)外部特征能否較為準(zhǔn)確地預(yù)測手機(jī)價(jià)格。 以下數(shù)據(jù)顯示,如果選用ROM、RAM、和brand作為特征,那么我們只能得到原數(shù)據(jù)集31%左右的數(shù)據(jù)用作訓(xùn)練和測試。
data.dropna(subset=['ROM','RAM','brand','price']).shape[0]/data.shape[0]輸出結(jié)果:
0.30692243536280234所有列缺失值數(shù)據(jù)統(tǒng)計(jì):
data.isnull().sum().sort_values(ascending=False)輸出結(jié)果:
CPU freq 998 CPU cores 824 front camera 773 RAM 720 rear camera specs 710 ROM 667 CPU model 479 screen material 381 brand 347 rear camera 249 battery 177 resolution 134 month 115 charging port 99 screen size 85 model 70 weight 66 SIM cards 56 year 6 price 3 comments 0 dtype: int64品牌brand對價(jià)格影響非常明顯,所以雖然缺失值較多,我們也必須考慮這個(gè)特征。
考慮到品牌brand是非數(shù)值數(shù)據(jù),我們選取使用回歸決策樹算法來進(jìn)行機(jī)器學(xué)習(xí)建模。
從原來的數(shù)據(jù)集提取我們需要的數(shù)據(jù)
df=data.loc[:,['price','rear camera','brand','weight']].dropna()由于回歸決策樹只接受數(shù)值型數(shù)據(jù)(numerical data),我們需要對brand進(jìn)行獨(dú)熱編碼(one-hot encoding)
to_model=pd.get_dummies(df)#對非數(shù)值型數(shù)據(jù)進(jìn)行獨(dú)熱編碼提取特征值和目標(biāo)值。 (考慮到各種手機(jī)品牌的型號(hào)數(shù)量畢竟很有限,而且部分品牌數(shù)據(jù)量較少,我們在這里就沒有劃分訓(xùn)練集和測試集了)
x=to_model.iloc[:,1:].values y=to_model.iloc[:,0].values訓(xùn)練回歸決策數(shù)模型
model=DecisionTreeRegressor() model.fit(x,y)輸出結(jié)果:
DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None,max_leaf_nodes=None, min_impurity_decrease=0.0,min_impurity_split=None, min_samples_leaf=1,min_samples_split=2, min_weight_fraction_leaf=0.0,presort=False, random_state=None, splitter='best')檢驗(yàn)我們的模型對各個(gè)品牌的預(yù)測準(zhǔn)確性。
error_list=[] for each in df['brand'].value_counts().index: to_fill='brand_{}'.format(each)x_data=to_model[to_model[to_fill]==1].iloc[:,1:].valuesy_data=to_model[to_model[to_fill]==1].iloc[:,0].valuestest_result=model.predict(x_data)merror=mae(y_data.reshape(len(y_data),1),test_result.flatten())error=(np.abs(test_result-y_data)/y_data).mean()print(each,end=' : ') print(np.round(merror,2),end=', ')print(str(np.round(error*100,3))+'%')error_list.append([each,merror,error])輸出結(jié)果:
HUAWEI : 238.55, 15.16% XIAOMI : 202.0, 12.277% Apple : 663.28, 8.087% OPPO : 177.65, 9.582% vivo : 134.78, 8.747% Philips : 7.01, 2.841% MEIZU : 51.79, 3.009% SAMSUNG : 269.2, 3.24% K-Touch : 7.23, 4.144% NOKIA : 12.5, 1.321% lenovo : 33.33, 3.374% Meitu : 120.0, 6.141% smartisan : 0.0, 0.0% realme : 0.0, 0.0% nubia : 0.0, 0.0% 360 : 0.0, 0.0% BlackBerry : 0.0, 0.0% Coolpad : 0.0, 0.0% ZTE : 0.0, 0.0% chilli : 0.0, 0.0% error_df=pd.DataFrame(error_list,columns=['brand','mean_absolute_error','mean_proportional_error']) error_df輸出結(jié)果:
以上的 DataFrame error_df 表示該決策樹模型對于每個(gè)品牌手機(jī)預(yù)測的準(zhǔn)確性,誤差都均在 15% 以內(nèi),這個(gè)模型還是相對比較準(zhǔn)確的。 實(shí)際上這個(gè)模型最關(guān)鍵的是提取了手機(jī)的重量weight這一關(guān)鍵信息,因?yàn)槊總€(gè)型號(hào)的手機(jī)重量多少是有些區(qū)別的,拿一個(gè)稍微精確一點(diǎn)的電子秤便能量出區(qū)別,決策數(shù)只不過是記住了數(shù)據(jù)而已。造成預(yù)測結(jié)果誤差的原因我想多半還是因?yàn)椴煌馁u家對同一型號(hào)手機(jī)的標(biāo)價(jià)不同吧。
項(xiàng)目總結(jié)
雖然沒有詳細(xì)地呈現(xiàn)數(shù)據(jù)采集以及數(shù)據(jù)清理的過程,但是這兩個(gè)步驟確是所花時(shí)間最多的步驟。雖然京東的網(wǎng)頁對于爬蟲新手已經(jīng)十分友好,但是頭一回爬取 javascript 渲染后的價(jià)格、評論數(shù)據(jù)還是頗有挑戰(zhàn)性。數(shù)據(jù)清理主要難點(diǎn)在于數(shù)據(jù)大多以自然語言呈現(xiàn),要找到實(shí)際上的缺失值,以及將自然語言轉(zhuǎn)變?yōu)閿?shù)值(比如評論數(shù) comments,后置攝像頭數(shù)量 rear cameras )。除去寫這個(gè) kaggle kernel,這兩個(gè)步驟大概花了所有時(shí)間的70%。 對于采集到的數(shù)據(jù)進(jìn)行分析也不是之前想象到的那么容易,為了發(fā)掘更深一層次的信息,對于每一次通過 pandas 函數(shù)得到的結(jié)果都需要認(rèn)真地分析結(jié)果,思考為什么會(huì)有這個(gè)結(jié)果。 總之,這次項(xiàng)目挑戰(zhàn)收獲還是比較大,也是頭一次自己完成數(shù)據(jù)的采集,清洗,以及分析的全過程。
總結(jié)
以上是生活随笔為你收集整理的基于京东手机销售数据用回归决策树预测价格的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「我去,这也能行!」令人惊叹的8个深度学
- 下一篇: C 语言快速入门,21 个小项目足矣!「