聚類算法有幾十種之多,聚類算法的選擇,主要參考如下因素:算法
K-Means算法的思想很簡單,對於給定的樣本集,按照樣本之間的距離大小,將樣本集劃分爲K個簇。讓簇內的點儘可能緊密的連在一塊兒,而讓簇間的距離儘可能的大。bash
若是用數據表達式表示,假設簇劃分爲(C1,C2,...Ck),則咱們的目標是最小化平方偏差E:cors
其中μi是簇Ci的均值向量,有時也稱爲質心,表達式爲:dom
首先咱們看看K-Means算法的一些要點。ide
(1)對於K-Means算法,首先要注意的是k值的選擇,通常來講,咱們會根據對數據的先驗經驗選擇一個合適的k值,若是沒有什麼先驗知識,則能夠經過交叉驗證選擇一個合適的k值。ui
(2)在肯定了k的個數後,咱們須要選擇k個初始化的質心,就像上圖b中的隨機質心。因爲咱們是啓發式方法,k個初始化的質心的位置選擇對最後的聚類結果和運行時間都有很大的影響,所以須要選擇合適的k個質心,最好這些質心不能太近。idea
好了,如今咱們來總結下傳統的K-Means算法流程。 spa
輸入是樣本集D={x1,x2,...xm},聚類的簇樹k,最大迭代次數N3d
輸出是簇劃分C={C1,C2,...Ck} code
(1)從數據集D中隨機選擇k個樣本做爲初始的k個質心向量: {μ1,μ2,...,μk}
(2)對於n=1,2,...,N
a) 將簇劃分C初始化爲Ct=∅t=1,2...k
b) 對於i=1,2...m,計算樣本xi和各個質心向量μj(j=1,2,...k)的距離:dij=||xi−μj||22,將xi標記最小的爲dij所對應的類別λi。此時更新Cλi=Cλi∪{xi}
c) 對於j=1,2,...,k,對Cj中全部的樣本點從新計算新的質心μj=1|Cj|∑x∈Cjx
e) 若是全部的k個質心向量都沒有發生變化,則轉到步驟3)
(3)輸出簇劃分C={C1,C2,...Ck}
(1)原始數據 Sklearn中有專門的聚類庫cluster,在作聚類時只需導入這個庫,即可使用其中多種聚類算法,例如K均值、DBSCAN、譜聚類等。 本示例模擬的是對一份沒有任何標籤的數據集作聚類分析,以獲得不用類別的特徵和分佈狀態等,主要使用Sklearn作聚類、用Matplotlib 作圖形展現。數據源文件命名爲clustring.txt。
(2)代碼實現
# 導入庫
import numpy as np # 導入numpy庫
import matplotlib.pyplot as plt # 導入matplotlib庫
from sklearn.cluster import KMeans # 導入sklearn聚類模塊
from sklearn import metrics # 導入sklearn效果評估模塊
# 數據準備
raw_data = np.loadtxt('./cluster.txt') # 導入數據文件
X = raw_data[:, :-1] # 分割要聚類的數據
y_true = raw_data[:, -1]
# 訓練聚類模型
n_clusters = 3 # 設置聚類數量
model_kmeans = KMeans(n_clusters=n_clusters, random_state=0) # 創建聚類模型對象
model_kmeans.fit(X) # 訓練聚類模型
y_pre = model_kmeans.predict(X) # 預測聚類模型
# 模型效果指標評估
n_samples, n_features = X.shape # 總樣本量,總特徵數
inertias = model_kmeans.inertia_ # 樣本距離最近的聚類中心的總和
adjusted_rand_s = metrics.adjusted_rand_score(y_true, y_pre) # 調整後的蘭德指數
mutual_info_s = metrics.mutual_info_score(y_true, y_pre) # 互信息
adjusted_mutual_info_s = metrics.adjusted_mutual_info_score(y_true, y_pre) # 調整後的互信息
homogeneity_s = metrics.homogeneity_score(y_true, y_pre) # 同質化得分
completeness_s = metrics.completeness_score(y_true, y_pre) # 完整性得分
v_measure_s = metrics.v_measure_score(y_true, y_pre) # V-measure得分
silhouette_s = metrics.silhouette_score(X, y_pre, metric='euclidean') # 平均輪廓係數
calinski_harabaz_s = metrics.calinski_harabaz_score(X, y_pre) # Calinski和Harabaz得分
print('總樣本量: %d \t 總特徵數: %d' % (n_samples, n_features)) # 打印輸出樣本量和特徵數量
print(70 * '-') # 打印分隔線
print('ine\tARI\tMI\tAMI\thomo\tcomp\tv_m\tsilh\tc&h') # 打印輸出指標標題
print('%d\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%d' % (
inertias, adjusted_rand_s, mutual_info_s, adjusted_mutual_info_s, homogeneity_s, completeness_s,
v_measure_s,
silhouette_s, calinski_harabaz_s)) # 打印輸出指標值
print(70 * '-') # 打印分隔線
print('簡寫 \t 全稱') # 打印輸出縮寫和全名標題
print('ine \t 樣本距離最近的聚類中心的總和')
print('ARI \t 調整後的蘭德指數')
print('MI \t 互信息')
print('AMI \t 調整後的互信息')
print('homo \t 同質化得分')
print('comp \t 完整性得分')
print('v_m \t V-measure得分')
print('silh \t 平均輪廓係數')
print('c&h \t Calinski和Harabaz得分')
# 模型效果可視化
centers = model_kmeans.cluster_centers_ # 各種別中心
colors = ['#4EACC5', '#FF9C34', '#4E9A06'] # 設置不一樣類別的顏色
plt.figure() # 創建畫布
for i in range(n_clusters): # 循環讀類別
index_sets = np.where(y_pre == i) # 找到相同類的索引集合
cluster = X[index_sets] # 將相同類的數據劃分爲一個聚類子集
plt.scatter(cluster[:, 0], cluster[:, 1], c=colors[i], marker='.') # 展現聚類子集內的樣本點
plt.plot(centers[i][0], centers[i][1], 'o', markerfacecolor=colors[i], markeredgecolor='k',
markersize=6) # 展現各聚類子集的中心
plt.show() # 展現圖像
複製代碼
結果:
總樣本量: 1000 總特徵數: 2
----------------------------------------------------------------------
ine ARI MI AMI homo comp v_m silh c&h
300 0.96 1.03 0.94 0.94 0.94 0.94 0.63 2860
----------------------------------------------------------------------
簡寫 全稱
ine 樣本距離最近的聚類中心的總和
ARI 調整後的蘭德指數
MI 互信息
AMI 調整後的互信息
homo 同質化得分
comp 完整性得分
v_m V-measure得分
silh 平均輪廓係數
c&h Calinski和Harabaz得分
複製代碼
經過不一樣的指標來作聚類效果評估。
(1)樣本距離最近的聚類中心的總和
inertias:inertias是K均值模型對象的屬性,表示樣本距離最近的聚類中心的總和,它是做爲在沒有真實分類結果標籤下的非監督式評估指標。該值越小越好,值越小證實樣本在類間的分佈越集中,即類內的距離越小。
(2) 調整後的蘭德指數
adjusted_rand_s:調整後的蘭德指數(Adjusted Rand Index),蘭德指數經過考慮在預測和真實聚類中在相同或不一樣聚類中分配的全部樣本對和計數對來計算兩個聚類之間的類似性度量。調整後的蘭德指數經過對蘭德指數的調整獲得獨立於樣本量和類別的接近於0的值,其取值範圍爲[-1,1],負數表明結果很差,越接近於1越好意味着聚類結果與真實狀況越吻合。
(3) 互信息
mutual_info_s:互信息(Mutual Information,MI),互信息是一個隨機變量中包含的關於另外一個隨機變量的信息量,在這裏指的是相同數據的兩個標籤之間的類似度的量度,結果是非負值。
(4) 調整後的互信息
adjusted_mutual_info_s:調整後的互信息(Adjusted MutualInformation,AMI),調整後的互信息是對互信息評分的調整得分。它考慮到對於具備更大數量的聚類羣,一般MI較高,而無論其實是否 有更多的信息共享,它經過調整聚類羣的機率來糾正這種影響。當兩個聚類集相同(即徹底匹配)時,AMI返回值爲1;隨機分區(獨立標籤)平均預期AMI約爲0,也可能爲負數。
(5) 同質化得分
homogeneity_s:同質化得分(Homogeneity),若是全部的聚類都只包含屬於單個類的成員的數據點,則聚類結果將知足同質性。其取值範圍[0,1]值越大意味着聚類結果與真實狀況越吻合。
(6) 完整性得分
completeness_s:完整性得分(Completeness),若是做爲給定類的成員的全部數據點是相同集羣的元素,則聚類結果知足完整性。其取值範圍[0,1],值越大意味着聚類結果與真實狀況越吻合。
(7) V-measure得分
v_measure_s:它是同質化和完整性之間的諧波平均值,v=2*(均勻性*完整性)/(均勻性+完整性)。其取值範圍[0,1],值越大意味着聚類結果與真實狀況越吻合。
(8) 輪廓係數
silhouette_s:輪廓係數(Silhouette),它用來計算全部樣本的平均輪廓係數,使用平均羣內距離和每一個樣本的平均最近簇距離來計算, 是一種非監督式評估指標。其最高值爲1,最差值爲-1,0附近的值表示重疊的聚類,負值一般表示樣本已被分配到錯誤的集羣。
(9) 羣內離散與簇間離散的比值
calinski_harabaz_s:該分數定義爲羣內離散與簇間離散的比值,它是一種非監督式評估指標。
用戶價值細分是瞭解用戶價值度的重要途徑,而銷售型公司中對於訂單交易尤其關注,所以基於訂單交易的價值度模型將更適合運營需求。
對於用戶價值度模型而言,因爲用戶的狀態是動態變化的,所以通常須要按期更新,業務方的主要需求是至少每週更新一次。因爲要兼顧歷史狀態變化,所以在每次更新時都須要保存歷史數據,不一樣時間點下的數據將經過日期區分。
輸入源數據score.csv
(1)讀取數據
# 導入庫
import time # 導入時間庫
import numpy as np # 導入numpy庫
import pandas as pd # 導入pandas庫
# 讀取數據
dtypes = {'ORDERDATE': object, 'ORDERID': object, 'AMOUNTINFO': np.float32} # 設置每列數據類型
raw_data = pd.read_csv('sales.csv', dtype=dtypes, index_col='USERID') # 讀取數據文件
# 數據審查和校驗
# 數據概覽
print('Data Overview:')
print(raw_data.head(4)) # 打印原始數據前4條
print('-' * 30)
print('Data DESC:')
print(raw_data.describe()) # 打印原始數據基本描述性信息
print('-' * 60)
複製代碼
結果
Data Overview:
ORDERDATE ORDERID AMOUNTINFO
USERID
142074 2016-01-01 4196439032 9399.0
56927 2016-01-01 4198324983 8799.0
87058 2016-01-01 4191287379 6899.0
136104 2016-01-01 4198508313 5999.0
------------------------------
Data DESC:
AMOUNTINFO
count 86127.000000
mean 744.762939
std 1425.194336
min 0.500000
25% 13.000000
50% 59.000000
75% 629.000000
max 30999.000000
複製代碼
(2)缺失值審查
# 缺失值審查
na_cols = raw_data.isnull().any(axis=0) # 查看每一列是否具備缺失值
print('NA Cols:')
print(na_cols) # 查看具備缺失值的列
print('-' * 30)
na_lines = raw_data.isnull().any(axis=1) # 查看每一行是否具備缺失值
print('NA Recors:')
print('Total number of NA lines is: {0}'.format(na_lines.sum())) # 查看具備缺失值的行總記錄數
print(raw_data[na_lines]) # 只查看具備缺失值的行信息
print('-' * 60)
複製代碼
結果
NA Cols:
ORDERDATE True
ORDERID False
AMOUNTINFO True
dtype: bool
------------------------------
NA Recors:
Total number of NA lines is: 10
ORDERDATE ORDERID AMOUNTINFO
USERID
75849 2016-01-01 4197103430 NaN
103714 NaN 4136159682 189.0
155209 2016-01-01 4177940815 NaN
139877 NaN 4111956196 6.3
54599 2016-01-01 4119525205 NaN
65456 2016-01-02 4195643356 NaN
122134 2016-09-21 3826649773 NaN
116995 2016-10-24 3981569421 NaN
98888 2016-12-06 3814398698 NaN
145951 2016-12-29 4139830098 NaN
複製代碼
(3)異常值處理
# 數據異常、格式轉換和處理
# 異常值處理
sales_data = raw_data.dropna() # 丟棄帶有缺失值的行記錄
sales_data = sales_data[sales_data['AMOUNTINFO'] > 1] # 丟棄訂單金額<=1的記錄
# 日期格式轉換
sales_data['ORDERDATE'] = pd.to_datetime(sales_data['ORDERDATE'], format='%Y-%m-%d') # 將字符串轉換爲日期格式
print('Raw Dtypes:')
print(sales_data.dtypes) # 打印輸出數據框全部列的數據類型
print('-' * 60)
# 數據轉換
recency_value = sales_data['ORDERDATE'].groupby(sales_data.index).max() # 計算原始最近一次訂單時間
frequency_value = sales_data['ORDERDATE'].groupby(sales_data.index).count() # 計算原始訂單頻率
monetary_value = sales_data['AMOUNTINFO'].groupby(sales_data.index).sum() # 計算原始訂單總金額
複製代碼
結果
Raw Dtypes:
ORDERDATE datetime64[ns]
ORDERID object
AMOUNTINFO float32
dtype: object
複製代碼
(4)計算RFM得分
# 計算RFM得分
# 分別計算R、F、M得分
deadline_date = pd.datetime(2017, 0o1, 0o1) # 指定一個時間節點,用於計算其餘時間與該時間的距離
r_interval = (deadline_date - recency_value).dt.days # 計算R間隔
r_score = pd.cut(r_interval, 5, labels=[5, 4, 3, 2, 1]) # 計算R得分
f_score = pd.cut(frequency_value, 5, labels=[1, 2, 3, 4, 5]) # 計算F得分
m_score = pd.cut(monetary_value, 5, labels=[1, 2, 3, 4, 5]) # 計算M得分
# R、F、M數據合併
rfm_list = [r_score, f_score, m_score] # 將r、f、m三個維度組成列表
rfm_cols = ['r_score', 'f_score', 'm_score'] # 設置r、f、m三個維度列名
rfm_pd = pd.DataFrame(np.array(rfm_list).transpose(), dtype=np.int32, columns=rfm_cols,
index=frequency_value.index) # 創建r、f、m數據框
print('RFM Score Overview:')
print(rfm_pd.head(4))
print('-' * 60)
複製代碼
結果
RFM Score Overview:
r_score f_score m_score
USERID
51220 4 1 1
51221 2 1 1
51224 3 1 1
51225 4 1 1
複製代碼
(5)計算RFM總得分
# 計算RFM總得分
# 方法一:加權得分
rfm_pd['rfm_wscore'] = rfm_pd['r_score'] * 0.6 + rfm_pd['f_score'] * 0.3 + rfm_pd['m_score'] * 0.1
# 方法二:RFM組合
rfm_pd_tmp = rfm_pd.copy()
rfm_pd_tmp['r_score'] = rfm_pd_tmp['r_score'].astype(np.str)
rfm_pd_tmp['f_score'] = rfm_pd_tmp['f_score'].astype(np.str)
rfm_pd_tmp['m_score'] = rfm_pd_tmp['m_score'].astype(np.str)
rfm_pd['rfm_comb'] = rfm_pd_tmp['r_score'].str.cat(rfm_pd_tmp['f_score']).str.cat(
rfm_pd_tmp['m_score'])
# 打印輸出和保存結果
# 打印結果
print('Final RFM Scores Overview:')
print(rfm_pd.head(4)) # 打印數據前4項結果
print('-' * 30)
print('Final RFM Scores DESC:')
print(rfm_pd.describe())
# 保存RFM得分到本地文件
rfm_pd.to_csv('sales_rfm_score.csv') # 保存數據爲csv
複製代碼
結果
Final RFM Scores Overview:
r_score f_score m_score rfm_wscore rfm_comb
USERID
51220 4 1 1 2.8 411
51221 2 1 1 1.6 211
51224 3 1 1 2.2 311
51225 4 1 1 2.8 411
------------------------------
Final RFM Scores DESC:
r_score f_score m_score rfm_wscore
count 59676.000000 59676.000000 59676.000000 59676.000000
mean 3.299970 1.013439 1.000134 2.384027
std 1.402166 0.116017 0.018307 0.845380
min 1.000000 1.000000 1.000000 1.000000
25% 2.000000 1.000000 1.000000 1.600000
50% 3.000000 1.000000 1.000000 2.200000
75% 5.000000 1.000000 1.000000 3.400000
max 5.000000 5.000000 5.000000 5.000000
複製代碼
因爲在RFM劃分時,將區間劃分爲5份,所以能夠將這5份區間分別定義了:高、中、通常、差和很是差5個級別,分別對應到R、F、M中的5/4/3/2/1。
基於RFM得分業務方獲得這樣的結論: