基於京東手機銷售數據用回歸決策樹預測價格


今天給大家推薦一個數據分析與挖掘的實戰項目案例“基於京東手機銷售數據用回歸決策樹預測價格”。該項目先基於京東手機銷售數據做出一系列分析后,利用回歸決策樹僅根據手機外部特征進行價格預測。

本項目來自於實驗樓《樓+ 數據分析與挖掘實戰》第五期學員:Ted_Wei。

數據獲取

由於手機的價格以及評論數是需要經過 javascript 渲染的動態信息,單純用 requests 模塊是爬取不到的。我的解決方案是首先使用 selenium 的 webbrowser 模塊使用本地 Chrome 瀏覽器爬取每一款手機在京東的內部id,價格,以及評論數。然后再根據爬取到的id找到每款手機的銷售頁面,爬取每款手機的關鍵參數。

爬蟲代碼parsing_code.py可通過此頁面: https://www.kaggle.com/ted0001/dm05-998494/data 獲取。

數據清洗

獲取到的數據包含1199行,21列,每一行代表一款正在銷售的手機,每一列包含關於手機的一項參數(比如價格,內存大小,像素,等等)。 獲取到的數據大多為自然語言,非數值信息,對於分析十分不友好。所以數據分析的重點在於,找到實際上的缺失信息(如‘無’,‘參考官方數據’等都可認為是缺失數據NaN),以使用re模塊解析自然語言,提取其中有用的數值信息。

數據清洗的步驟其實相當繁瑣,如果把代碼貼出也會占用過多篇幅,因此數據清洗的代碼data_clean.py可通過此頁面: https://www.kaggle.com/ted0001/dm05-998494/data 獲取。

數據分析

下面直接進入正題,主要完成估計各品牌手機銷量並進行比較分析、對決定手機價格因素的探索、嘗試用機器學習方法預測手機價格。

首先調用所需的模塊

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 inline

from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_absolute_error as mae

讀取已經清洗好的數據,數據也可在 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在存儲為csv file以后原來的index會變為一個列,因此要重新設置index
data.shape

輸出結果:

(1199,21)

估計各品牌手機銷量並進行比較分析

拿到清洗好的數據后的第一個想法,便是對各個品牌的手機的銷量進行一個比較。由於京東只顯示了每部手機的評論數量而不是具體銷量,我們只好默認評論數量comments和銷量成正比,從而估計各個手機品牌的銷量占比

pie_plt=data.groupby(['brand']).sum()['comments'].sort_values(ascending=False)#統計每個品牌評論總數,以此作為我們對銷量的估計
pie_plt

輸出結果:

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

我們可以對以上數據用扇形圖更好地展現出來

#繪制各個手機品牌估計銷量的占比扇形圖
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()

輸出結果:

從以上扇形圖我們可以估計,在中國市場銷量前三的手機品牌分別是華為,蘋果,小米,分別占到了總銷量的30.6%,26.5%,21.6%,剩下的品牌銷量則遠遠不及銷量前三的品牌。 為了驗證以上估計的正確性,我查閱了2019年中國手機市場各個品牌份額的相關資料。發現華為,蘋果,小米的占比分別為34%,9%,12%,除華為外,小米和蘋果的市場份額數據均有較大出入。而vivo,oppo的市場份額則分別是19%和18%,與原來的數據差距就更大了。 如果京東的評論數量可以大致反映手機線上銷售情況的話,我想造成數據有如此大差異的原因可能在於沒有考慮中國市場的線下銷售。根據平時的生活經驗,蘋果和小米的線下門店數量(特別是在小城市)是遠遠不及oppo,vivo的門店數量的。整個手機市場既包括線上市場也包括線下市場,而造成我統計到的數據與權威數據差異的原因,很可能是因為我的數據沒有包含線下銷售。 如果能夠獲得線下銷售數據,那么也可以對以上推論進行進一步的驗證。

還有一點出乎我意料的是,諾基亞和飛利浦手機的估計銷量占比竟然也都超過了3%(甚至超過vivo,oppo),於是便想看看這兩個品牌分別銷售哪種價位手機

data[(data['brand']=='NOKIA')|(data['brand']=='Philips')]['price'].median()#諾基亞和飛利浦手機價格中位數

輸出結果:

208.5
data[(data['brand']=='NOKIA')|(data['brand']=='Philips')]['price'].mean()#諾基亞和飛利浦手機價格平均數

輸出結果:

336.1190476190476

這樣看來,諾基亞和飛利浦的手機價格多在200-300元左右,再根據item id訪問京東網站后,發現果不其然,這兩個品牌所銷售的大多是功能機。(諾基亞有部分智能手機) 在這個智能手機已經全面普及的年代,想不到功能機也還是有它的一畝三分地,並沒有完全被市場淘汰(尤其是線上銷售渠道)。這可能是因為部分老人仍然更習慣使用功能機,以及功能機待機時間長,鈴聲音量大,能滿足部分人群的特殊需求。

對決定手機價格因素的探索

另一個值得研究的問題便是手機的價格和手機配置參數的關系。為了讓我們有一個整體把握,我們會先畫出各個數值數據間的correlation matrix,然后再探索非數值數據(categorical data),如品牌,屏幕材料,對價格的影響。

畫出Correlation Matrix 並進行分析

由於蘋果手機和安卓手機的價格、配置差異較大,而且蘋果手機配置參數在我們的數據集中大多缺失,我們研究這個問題時,就暫且只考慮安卓手機。(價格為9999的手機是華為將於2019年7月發行的榮耀X9,由於該型號價格異常,故也將其從我們的分析中排除。)

correlation=data[(data['brand']!='Apple')&(data['price']!=9999)].corr()
#繪制相應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()

輸出結果:

從上圖我們可以看出,價格 price 和存儲空間 ROM 以及內存 RAM 的關聯度最大,分別達到了0.71和0.68。其次便是電池容量battery,后置攝像頭個數rear camera,屏幕大小screen size,關聯度都分別達到了0.42。這也比較符合我們常識性的判斷。 我們還可以很明顯的看到,評價個數comments的column和row都呈現深紫色,代表comments和各個數值參數的關聯都很小。 (由於我們的數據集缺失值較多,在舍棄掉部分缺失值的行后,以上correlation matrix的數值會出現一定程度的變化)

手機品牌對手機價格影響的探索

當然,決定手機價格很關鍵的因素還有一些非數值數據(categorical data)。最容易想到的便是手機的品牌了。

data.groupby(['brand']).median()['price'].sort_values(ascending=False).values.std() #計算不同品牌價格中位數集合的標准差

輸出結果:

1409.0576123336064

以上數據也可以通過柱狀圖來更直觀地展示。

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')

輸出結果:

Text(0.5, 1.0, 'Median price of handphones of various brands')

image

我們可以看到,各個品牌手機中位數價格層次不齊,這也和我們的常識性判斷吻合,因為不同手機品牌的定位以及消費群體均有較大差異。

不同屏幕材料對手機價格影響的探索

還有一個很關鍵的因素其實是手機的屏幕材料,我們也可以用同樣的方法比較不同屏幕材料對價格的影響。

data.groupby(['screen material']).median()['price'].sort_values(ascending=False).values.std() #計算不同屏幕材料價格中位數集合的標准差

輸出結果:

1523.0026019740856

各種屏幕材料的手機的價格中位數展示如下

data.groupby(['screen material']).median()['price'].sort_values(ascending=False)

輸出結果:

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')

輸出結果:

Text(0.5, 1.0, 'Median price of handphones of various screen materials')

可以注意到的是,以上價格數值均在千元以上,而我們的數據集中還包含有價格很低廉的功能機,那它們的屏幕又都是什么材料呢?

data[(data['brand']=='NOKIA')|(data['brand']=='Philips')]['screen material'].value_counts()

輸出結果:

TFT             27
IPS              3
TFT LCD(IPS)     1
Name: screen material, dtype: int64

可以看到,諾基亞和飛利浦的手機(大多為功能機)的屏幕材料大多為TFT和IPS。

我們又可以反過來看看又到底是哪些價位的手機在使用TFT和IPS呢?

#繪制屏幕材料為IPS或TFT手機的價格分布圖
hist_plot=data[(data['screen material']=='IPS')|(data['screen material']=='TFT')]['price']#查看所有屏幕材料為IPS或TFT手機的價格
sns.distplot(hist_plot)
plt.title('Price Distribution Plot of Handphones Whose Screen Material is TFT or IPS ')

輸出結果:

Text(0.5, 1.0, 'Price Distribution Plot of Handphones Whose Screen Material is TFT or IPS ')

通過觀察以上分布圖以及進一步在數據集data中查看屏幕材料為TFT或IPS的手機發現,IPS主要用於華為的中低端手機,價格在千元以下,或者1300元左右。其中,價格在200元以下的功能機的屏幕材料均為TFT。 出乎我意料的是,也有部分高端手機使用的是IPS或TFT材料,比如華為的榮耀V20,蘋果的iphone 8,使用的是IPS材料;華為mate20和華為p20使用的均為TFT材料,這些手機的價格都在3500元以上。 通過以上的探索分析我們可以知道,高端智能機和低端功能機所使用的屏幕材料也很可能是一樣的。當然我們也不排除,同樣是TFT或IPS材料,它們內部也可能有區別。

嘗試用機器學習方法預測手機價格

在前面的小節中,我們探索了決定手機價格的幾大因素,手機存儲空間ROM,內存RAM,以及品牌,屏幕材料等都是決定手機價格的關鍵因素。 在這一小節中,我會使用回歸決策樹(Regression Decision Tree)的算法僅僅根據手機的外部特征來預測手機的價格。決策數的特征值僅僅采用了手機的品牌brand、后置攝像頭數量rear camera、以及手機重量weight作為我們的特征(feature),目標(target)當然則是我們的價格price 這樣做的原因一來是因為ROM和RAM存在太多的缺失值。如果選取這兩個值做為特征,那么我們會丟失掉太多訓練數據。 二來是想嘗試在不知道手機具體配置,僅僅通過觀察測量手機外部特征能否較為准確地預測手機價格。 以下數據顯示,如果選用ROM、RAM、和brand作為特征,那么我們只能得到原數據集31%左右的數據用作訓練和測試。

data.dropna(subset=['ROM','RAM','brand','price']).shape[0]/data.shape[0]

輸出結果:

0.30692243536280234

所有列缺失值數據統計:

data.isnull().sum().sort_values(ascending=False)

輸出結果:

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對價格影響非常明顯,所以雖然缺失值較多,我們也必須考慮這個特征。

考慮到品牌brand是非數值數據,我們選取使用回歸決策樹算法來進行機器學習建模。

從原來的數據集提取我們需要的數據

df=data.loc[:,['price','rear camera','brand','weight']].dropna()

由於回歸決策樹只接受數值型數據(numerical data),我們需要對brand進行獨熱編碼(one-hot encoding)

to_model=pd.get_dummies(df)#對非數值型數據進行獨熱編碼

提取特征值和目標值。 (考慮到各種手機品牌的型號數量畢竟很有限,而且部分品牌數據量較少,我們在這里就沒有划分訓練集和測試集了)

x=to_model.iloc[:,1:].values
y=to_model.iloc[:,0].values

訓練回歸決策數模型

model=DecisionTreeRegressor()
model.fit(x,y)

輸出結果:

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')

檢驗我們的模型對各個品牌的預測准確性。

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:].values
    y_data=to_model[to_model[to_fill]==1].iloc[:,0].values

    test_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])

輸出結果:

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

輸出結果:

以上的 DataFrame error_df 表示該決策樹模型對於每個品牌手機預測的准確性,誤差都均在 15% 以內,這個模型還是相對比較准確的。 實際上這個模型最關鍵的是提取了手機的重量weight這一關鍵信息,因為每個型號的手機重量多少是有些區別的,拿一個稍微精確一點的電子秤便能量出區別,決策數只不過是記住了數據而已。造成預測結果誤差的原因我想多半還是因為不同的賣家對同一型號手機的標價不同吧。

項目總結

雖然沒有詳細地呈現數據采集以及數據清理的過程,但是這兩個步驟確是所花時間最多的步驟。雖然京東的網頁對於爬蟲新手已經十分友好,但是頭一回爬取 javascript 渲染后的價格、評論數據還是頗有挑戰性。數據清理主要難點在於數據大多以自然語言呈現,要找到實際上的缺失值,以及將自然語言轉變為數值(比如評論數 comments,后置攝像頭數量 rear cameras )。除去寫這個 kaggle kernel,這兩個步驟大概花了所有時間的70%。 對於采集到的數據進行分析也不是之前想象到的那么容易,為了發掘更深一層次的信息,對於每一次通過 pandas 函數得到的結果都需要認真地分析結果,思考為什么會有這個結果。 總之,這次項目挑戰收獲還是比較大,也是頭一次自己完成數據的采集,清洗,以及分析的全過程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM