1. 分析目標
本項目旨在使用聚類算法對110個城市進行分類與排序,以尋找客觀真實的城市分層方法、支持業務運營與決策。node
2. 數據集
數據集來源於某互聯網公司,特徵值標籤已作脫敏處理。數據集尺寸爲111行×5列,第一行爲標題行,其他110行爲實例。python
- 第一列:城市名,將做爲index不參與模型計算;
- 第二列:特徵值a,以數值表現的分類變量,1表明評價最好、4表明最差;
- 第三列:特徵值b,數值型變量,數值越高對業務積極影響越大;
- 第四列:特徵值c,數值型變量,數值越高對業務消極影響越大;
- 第五列:特徵值d,數值型變量,數值越高對業務積極影響越大。
3. 方法論
首先,因爲數據集呈現分類變量與數值變量混合的特色,本次數據分析將採用如下兩種算法並在分析結束後進行對比擇優:算法
- K-means算法:須要將分類變量a轉換爲啞變量,使其成爲數值型變量,而後經過計算歐幾里得距離得出聚類結果。算法運行結束後將使用輪廓係數評價聚類效果。
- K-prototype算法:無需建立啞變量,將分別爲分類變量計算漢明距離、爲數值型變量計算歐幾里得距離而後得出聚類結果。算法運行結束後將使用成本函數評價聚類效果。
其次,數值型變量b、c、d的量綱明顯不等,爲避免量綱影響距離計算中不一樣變量的權重,須要對變量b、c、d進行處理。因爲不知道是否符合正態分佈,在這裏使用歸一化而非標準化。shell
最後,因爲機器沒法理解業務場景,算法自己沒法對不一樣聚類進行排序。在找到合理聚類方法後須要人工構建一個聚類評價指標以實現排序。app
本項目基於python 3.7.3,使用的庫包括pandas、numpy、sklearn、matplotlib、seaborn、kmodes,具體代碼詳見附錄。函數
4. 預處理
預處理步驟包括:spa
- 使用MinMaxScaler對變量b,c,d進行歸一化
- 使用get_dummies爲變量a建立啞變量
處理結束後的數據集以下所示(僅展現前五行爲例):prototype
城市3d |
a_1code |
a_2 |
a_3 |
a_4 |
a |
b |
c |
d |
三亞 |
0 |
0 |
1 |
0 |
3 |
0.016206 |
0.009198 |
0.00757 |
上海 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
東莞 |
0 |
0 |
1 |
0 |
3 |
0.314485 |
0.187345 |
0.299205 |
中山 |
0 |
0 |
0 |
1 |
4 |
0.109215 |
0.060833 |
0.057343 |
臨沂 |
0 |
0 |
0 |
1 |
4 |
0.106044 |
0.035673 |
0.018547 |
5. 分析過程
5.1 K-means
- 參與運算的列:a_1,a_2,a_3,a_4,b,c,d
- 手肘法求解最優k值(聚類數量):k=3
- k=3時的輪廓係數評價:
- 聚類間距:
- 解讀:雖然聚類間距良好,可是聚類2的輪廓係數太低,說明聚類2的聚合效果很差。在檢視聚類2時發現其包含北京、上海、牡丹江、大慶等城市,有悖常規認知,說明該聚類不合理。
- 結論:k-means聚類結果不可用。
5.2 K-prototype
- 參與運算的列:a,b,c,d
- 手肘法求解最優k(使用成本函數製圖):k=4
注1:python中計數從0開始計,因此橫軸數值爲聚類數量-1
注2:此方法結果具備隨機性,有時會產生不止一個肘點,存在最優解和次優解。爲確保k=3爲最優解,此過程被運行了十次,最後驗證最優解衆數爲3。
- 解讀:分爲四個聚類時,四個聚類所包含的城市符合1、2、3、四線城市的常規認知,說明聚類接近真實狀況。
- 結論:K-prototype的聚類結果較爲真實客觀,能夠採用。
6. 聚類排序
爲實現聚類排序,首先調取了每一個聚類的質點:
|
Centroids(Min-max Scaled) |
Centroids(原數據) |
|
||||||
Cluster |
a |
b |
c |
d |
a |
b |
c |
d |
Count |
0 |
4 |
0.05 |
0.02 |
0.01 |
4 |
3853.19 |
6638.98 |
56.46 |
48 |
1 |
2 |
0.66 |
0.52 |
0.59 |
2 |
38517.47 |
178333.07 |
3119.33 |
6 |
2 |
2 |
0.31 |
0.17 |
0.15 |
2 |
18678.75 |
60002.46 |
785.96 |
24 |
3 |
3 |
0.10 |
0.04 |
0.03 |
3 |
7053.54 |
16004.42 |
148.87 |
32 |
此處須要基於業務理解構建一個做爲聚類評價指標。四個特徵值的業務重要程度一致,所以須要採用等權重計算每一個特徵值的,可是變量c對總的貢獻爲負。可是,考慮到量綱的影響,仍需使用歸一化的b、c、d做爲其。若是將a做爲數值型變量進行考量,也應當進行歸一化處理,而且應注意數值越大實際上對的貢獻越低。綜上所述,的構建方法以下:
其中,
經計算,每一個聚類的及排名以下:
Cluster |
Score |
Ranking |
0 |
0.04 |
4 |
1 |
1.40 |
1 |
2 |
0.95 |
2 |
3 |
0.42 |
3 |
經檢視,聚類一、二、三、0很是接近常規認知中的1、2、3、四線城市,證實排序合理。
7. 結論
通過使用k-prototype算法和構建聚類評價指標,成功將110個城市分爲四個聚類及聚類排序。同時運用評價指標,結合歸一化數據可對個體城市實現細粒度上的評價,從而獲得每一個聚類內部的城市排名狀況或跨四個聚類的整體城市排名:
城市 |
a |
b |
c |
d |
cluster |
cluster_tag |
in-cluster_score |
in-cluster_ranking |
上海 |
1 |
57874 |
342463 |
5286 |
1 |
tier1 |
1.510 |
1 |
北京 |
2 |
40210 |
182647 |
3457 |
1 |
tier1 |
1.162 |
2 |
深圳 |
3 |
37620 |
160159 |
3228 |
1 |
tier1 |
1.116 |
3 |
蘇州 |
2 |
34424 |
117925 |
1871 |
1 |
tier1 |
0.897 |
4 |
廣州 |
3 |
29242 |
105356 |
2288 |
1 |
tier1 |
0.875 |
5 |
杭州 |
2 |
31735 |
161448 |
2586 |
1 |
tier1 |
0.834 |
6 |
城市 |
a |
b |
c |
d |
cluster |
cluster_tag |
in-cluster_score |
in-cluster_ranking |
東莞 |
3 |
18958 |
64856 |
1583 |
2 |
tier2 |
0.587 |
1 |
南京 |
2 |
23572 |
84459 |
1266 |
2 |
tier2 |
0.592 |
2 |
南昌 |
2 |
10282 |
36616 |
639 |
2 |
tier2 |
0.260 |
3 |
廈門 |
1 |
15059 |
70708 |
1073 |
2 |
tier2 |
0.369 |
4 |
合肥 |
2 |
18372 |
75802 |
724 |
2 |
tier2 |
0.377 |
5 |
哈爾濱 |
2 |
16126 |
29936 |
332 |
2 |
tier2 |
0.377 |
6 |
嘉興 |
2 |
10918 |
35879 |
370 |
2 |
tier2 |
0.228 |
7 |
大連 |
2 |
13676 |
31977 |
358 |
2 |
tier2 |
0.311 |
8 |
天津 |
2 |
25428 |
65309 |
1010 |
2 |
tier2 |
0.649 |
9 |
寧波 |
2 |
21712 |
64354 |
928 |
2 |
tier2 |
0.538 |
10 |
常州 |
2 |
12683 |
29769 |
355 |
2 |
tier2 |
0.290 |
11 |
成都 |
3 |
30619 |
95611 |
918 |
2 |
tier2 |
0.681 |
12 |
無錫 |
2 |
18622 |
42607 |
632 |
2 |
tier2 |
0.463 |
13 |
武漢 |
2 |
21566 |
92360 |
1399 |
2 |
tier2 |
0.541 |
14 |
瀋陽 |
2 |
23688 |
67889 |
735 |
2 |
tier2 |
0.543 |
15 |
泉州 |
2 |
15615 |
63454 |
761 |
2 |
tier2 |
0.346 |
16 |
溫州 |
1 |
22346 |
72011 |
903 |
2 |
tier2 |
0.527 |
17 |
湖州 |
2 |
6299 |
17706 |
156 |
2 |
tier2 |
0.118 |
18 |
福州 |
1 |
15067 |
82398 |
1031 |
2 |
tier2 |
0.327 |
19 |
西安 |
3 |
23634 |
57532 |
1118 |
2 |
tier2 |
0.645 |
20 |
重慶 |
3 |
31119 |
76562 |
656 |
2 |
tier2 |
0.701 |
21 |
金華 |
2 |
15317 |
48394 |
568 |
2 |
tier2 |
0.346 |
22 |
長沙 |
2 |
19307 |
77104 |
822 |
2 |
tier2 |
0.416 |
23 |
青島 |
2 |
18305 |
56769 |
526 |
2 |
tier2 |
0.393 |
24 |
城市 |
a |
b |
c |
d |
cluster |
cluster_tag |
in-cluster_score |
in-cluster_ranking |
三亞 |
3 |
2025 |
4000 |
42 |
3 |
tier3 |
0.023 |
1 |
九江 |
3 |
4582 |
8585 |
56 |
3 |
tier3 |
0.080 |
2 |
南寧 |
3 |
9909 |
23157 |
361 |
3 |
tier3 |
0.237 |
3 |
南通 |
3 |
10861 |
29783 |
219 |
3 |
tier3 |
0.216 |
4 |
台州 |
3 |
11892 |
27683 |
221 |
3 |
tier3 |
0.250 |
5 |
吉林 |
3 |
4658 |
6652 |
31 |
3 |
tier3 |
0.083 |
6 |
咸陽 |
3 |
3754 |
5903 |
49 |
3 |
tier3 |
0.065 |
7 |
太原 |
3 |
7675 |
16814 |
248 |
3 |
tier3 |
0.175 |
8 |
惠州 |
3 |
9378 |
29550 |
448 |
3 |
tier3 |
0.220 |
9 |
揚州 |
3 |
6396 |
16309 |
141 |
3 |
tier3 |
0.122 |
10 |
新鄉 |
3 |
4417 |
6036 |
58 |
3 |
tier3 |
0.084 |
11 |
泰州 |
3 |
5609 |
15036 |
57 |
3 |
tier3 |
0.089 |
12 |
濟南 |
3 |
13485 |
32222 |
297 |
3 |
tier3 |
0.293 |
13 |
淄博 |
3 |
4308 |
9902 |
68 |
3 |
tier3 |
0.071 |
14 |
湛江 |
3 |
3243 |
6016 |
36 |
3 |
tier3 |
0.048 |
15 |
濰坊 |
3 |
8754 |
20406 |
201 |
3 |
tier3 |
0.184 |
16 |
煙臺 |
3 |
7611 |
15206 |
110 |
3 |
tier3 |
0.152 |
17 |
珠海 |
3 |
4775 |
12372 |
116 |
3 |
tier3 |
0.085 |
18 |
鹽城 |
3 |
7908 |
21129 |
113 |
3 |
tier3 |
0.143 |
19 |
石家莊 |
3 |
13750 |
29776 |
462 |
3 |
tier3 |
0.339 |
20 |
紹興 |
3 |
7073 |
22799 |
188 |
3 |
tier3 |
0.130 |
21 |
蕪湖 |
3 |
4555 |
11467 |
63 |
3 |
tier3 |
0.072 |
22 |
荊州 |
3 |
3325 |
4988 |
20 |
3 |
tier3 |
0.050 |
23 |
營口 |
3 |
3574 |
5605 |
19 |
3 |
tier3 |
0.055 |
24 |
蚌埠 |
3 |
2961 |
6873 |
38 |
3 |
tier3 |
0.039 |
25 |
贛州 |
3 |
7994 |
16110 |
172 |
3 |
tier3 |
0.171 |
26 |
鄭州 |
3 |
18906 |
42977 |
432 |
3 |
tier3 |
0.432 |
27 |
銀川 |
3 |
6829 |
10248 |
149 |
3 |
tier3 |
0.153 |
28 |
鎮江 |
3 |
6047 |
20610 |
101 |
3 |
tier3 |
0.092 |
29 |
長春 |
3 |
12492 |
22492 |
208 |
3 |
tier3 |
0.279 |
30 |
黃石 |
3 |
2118 |
4601 |
22 |
3 |
tier3 |
0.020 |
31 |
齊齊哈爾 |
3 |
4850 |
6836 |
18 |
3 |
tier3 |
0.085 |
32 |
城市 |
a |
b |
c |
d |
cluster |
cluster_tag |
in-cluster_score |
in-cluster_ranking |
中山 |
4 |
7305 |
21639 |
305 |
0 |
tier4 |
0.161 |
1 |
臨沂 |
4 |
7125 |
13044 |
100 |
0 |
tier4 |
0.143 |
2 |
烏魯木齊 |
4 |
1825 |
858 |
5 |
0 |
tier4 |
0.020 |
3 |
佛山 |
4 |
11138 |
33369 |
560 |
0 |
tier4 |
0.277 |
4 |
佳木斯 |
1 |
3781 |
6769 |
20 |
0 |
tier4 |
0.057 |
5 |
蘭州 |
4 |
5566 |
10263 |
128 |
0 |
tier4 |
0.115 |
6 |
包頭 |
4 |
2032 |
1750 |
27 |
0 |
tier4 |
0.027 |
7 |
呼倫貝爾 |
4 |
2499 |
2107 |
5 |
0 |
tier4 |
0.034 |
8 |
呼和浩特 |
4 |
3444 |
3478 |
63 |
0 |
tier4 |
0.066 |
9 |
唐山 |
4 |
4240 |
5776 |
25 |
0 |
tier4 |
0.073 |
10 |
商丘 |
4 |
4619 |
5356 |
17 |
0 |
tier4 |
0.083 |
11 |
大同 |
4 |
2213 |
2399 |
8 |
0 |
tier4 |
0.026 |
12 |
大慶 |
2 |
4379 |
4913 |
12 |
0 |
tier4 |
0.077 |
13 |
威海 |
2 |
5125 |
10654 |
83 |
0 |
tier4 |
0.094 |
14 |
安陽 |
4 |
2582 |
2371 |
21 |
0 |
tier4 |
0.038 |
15 |
宜昌 |
4 |
3298 |
3941 |
15 |
0 |
tier4 |
0.052 |
16 |
寶雞 |
4 |
2565 |
1789 |
12 |
0 |
tier4 |
0.038 |
17 |
宿遷 |
4 |
3054 |
5249 |
19 |
0 |
tier4 |
0.042 |
18 |
岳陽 |
4 |
2784 |
3660 |
13 |
0 |
tier4 |
0.039 |
19 |
延邊 |
4 |
3315 |
4166 |
6 |
0 |
tier4 |
0.050 |
20 |
開封 |
4 |
3523 |
2555 |
8 |
0 |
tier4 |
0.060 |
21 |
徐州 |
4 |
8302 |
16204 |
87 |
0 |
tier4 |
0.163 |
22 |
揭陽 |
2 |
4710 |
11208 |
90 |
0 |
tier4 |
0.082 |
23 |
日照 |
4 |
1190 |
2352 |
4 |
0 |
tier4 |
-0.002 |
24 |
昆明 |
4 |
8389 |
18251 |
177 |
0 |
tier4 |
0.176 |
25 |
景德鎮 |
1 |
2304 |
5690 |
63 |
0 |
tier4 |
0.029 |
26 |
柳州 |
4 |
2279 |
4194 |
4 |
0 |
tier4 |
0.022 |
27 |
桂林 |
4 |
3379 |
4137 |
23 |
0 |
tier4 |
0.055 |
28 |
江門 |
4 |
2978 |
5948 |
27 |
0 |
tier4 |
0.040 |
29 |
瀘州 |
4 |
2023 |
2084 |
5 |
0 |
tier4 |
0.021 |
30 |
洛陽 |
4 |
4999 |
4765 |
38 |
0 |
tier4 |
0.099 |
31 |
濟寧 |
4 |
6007 |
11411 |
93 |
0 |
tier4 |
0.117 |
32 |
海口 |
4 |
4678 |
17398 |
148 |
0 |
tier4 |
0.074 |
33 |
淮北 |
4 |
1105 |
1636 |
8 |
0 |
tier4 |
-0.001 |
34 |
淮安 |
4 |
4921 |
8405 |
34 |
0 |
tier4 |
0.085 |
35 |
牡丹江 |
1 |
4140 |
5646 |
20 |
0 |
tier4 |
0.070 |
36 |
秦皇島 |
4 |
2449 |
2005 |
18 |
0 |
tier4 |
0.035 |
37 |
綿陽 |
4 |
2895 |
4501 |
28 |
0 |
tier4 |
0.042 |
38 |
肇慶 |
4 |
2932 |
5465 |
47 |
0 |
tier4 |
0.044 |
39 |
舟山 |
1 |
2430 |
6081 |
40 |
0 |
tier4 |
0.027 |
40 |
西寧 |
4 |
1710 |
1616 |
19 |
0 |
tier4 |
0.017 |
41 |
貴陽 |
4 |
4506 |
7772 |
181 |
0 |
tier4 |
0.104 |
42 |
赤峯 |
4 |
3819 |
3971 |
19 |
0 |
tier4 |
0.066 |
43 |
運城 |
4 |
2850 |
1866 |
3 |
0 |
tier4 |
0.044 |
44 |
連雲港 |
4 |
3454 |
6768 |
40 |
0 |
tier4 |
0.052 |
45 |
通遼 |
4 |
1702 |
1491 |
2 |
0 |
tier4 |
0.014 |
46 |
遵義 |
4 |
4835 |
5303 |
28 |
0 |
tier4 |
0.091 |
47 |
鞍山 |
4 |
3555 |
6398 |
12 |
0 |
tier4 |
0.051 |
48 |
附錄:代碼
K-means:
from IPython.core.interactiveshell import InteractiveShell InteractiveShell.ast_node_interactivity = "all" %matplotlib inline import pandas as pd import numpy as np from sklearn import preprocessing from sklearn.cluster import KMeans from sklearn.cluster import MiniBatchKMeans from yellowbrick.cluster import KElbowVisualizer from yellowbrick.cluster import SilhouetteVisualizer from yellowbrick.cluster import InterclusterDistance from yellowbrick.model_selection import LearningCurve import matplotlib.pyplot as plt import seaborn as sns sns.set_style("whitegrid") sns.set_context("notebook") df = pd.read_csv('practice_2_clustering_with_cat.csv') df.head() df['a'] = df['a'].astype(object) dummies = pd.get_dummies(df['a'], prefix='a') bcd = df.iloc[:, 2:5] min_max_scaler = preprocessing.MinMaxScaler() x_scaled = min_max_scaler.fit_transform(bcd) X_scaled = pd.DataFrame(x_scaled,columns=bcd.columns) X_scaled = pd.concat([X_scaled,dummies], axis=1,) # Elbow method 手肘法 plt.figure(figsize=(12,9)) model = KMeans() visualizer = KElbowVisualizer(model, k=(1,8)) visualizer.fit(X_scaled) visualizer.show() model=MiniBatchKMeans(n_clusters=3) model.fit(X_scaled) print("Predicted labels ----") model.predict(X_scaled) df['cluster'] = model.predict(X_scaled) plt.figure(figsize=(12,9)) model=MiniBatchKMeans(n_clusters=3).fit(X_scaled) visualizer = SilhouetteVisualizer(model, colors='yellowbrick') visualizer.fit(X_scaled) visualizer.show() plt.figure(figsize=(12,9)) visualizer = InterclusterDistance(model, min_size=10000) visualizer.fit(X_scaled) visualizer.show() df = pd.concat([df,X_scaled], axis=1) df
K-prototype
from IPython.core.interactiveshell import InteractiveShell InteractiveShell.ast_node_interactivity = "all" %matplotlib inline import pandas as pd import numpy as np from sklearn import preprocessing from sklearn.cluster import KMeans from sklearn.cluster import MiniBatchKMeans from yellowbrick.cluster import KElbowVisualizer from yellowbrick.cluster import SilhouetteVisualizer from yellowbrick.cluster import InterclusterDistance from yellowbrick.model_selection import LearningCurve import matplotlib.pyplot as plt import seaborn as sns sns.set_style("whitegrid") sns.set_context("notebook") import numpy as np import pandas as pd from kmodes.kprototypes import KPrototypes %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns df = pd.read_csv('practice_2_clustering_with_cat.csv') df.head() df['a'] = df['a'].astype(object) X = df.iloc[:, 1:5] X.columns = ['a','b','c','d'] X.head() min_max_scaler = preprocessing.MinMaxScaler() bcd = X.iloc[:,1:4] x_scaled = min_max_scaler.fit_transform(bcd) X_scaled = pd.DataFrame(x_scaled,columns=bcd.columns) X = pd.concat([df['a'],X_scaled], axis=1) X_matrix = X.values cost = [] for num_clusters in list(range(1,9)): kproto = KPrototypes(n_clusters=num_clusters, init='Cao') kproto.fit_predict(X_matrix, categorical=[0]) cost.append(kproto.cost_) plt.plot(cost) pd.DataFrame(cost) kproto = KPrototypes(n_clusters=6, init='Cao') clusters = kproto.fit_predict(X_matrix, categorical=[0]) print('====== Centriods ======') kproto.cluster_centroids_ print() print('====== Cost ======') kproto.cost_ centroids = pd.concat([pd.DataFrame(kproto.cluster_centroids_[1]),pd.DataFrame(kproto.cluster_centroids_[0])], axis=1) centroids df['cluster'] = clusters df.head()