案例:使用K-Means對(duì)用戶進(jìn)行分群
K-Means 定義
K-Means 算法是聚類算法的一種,所以先了解聚類算法。
聚類分析是在沒(méi)有給定劃分類別情況下,根據(jù)數(shù)據(jù)相似度進(jìn)行樣本分組的一種方法。是一種無(wú)監(jiān)督的學(xué)習(xí)算法。劃分依據(jù)主要是自身的距離或相似度將他們劃分為若干組,劃分原則是組內(nèi)樣本最小化而組間(外部)距離最大化。聚類多數(shù)場(chǎng)景下用在「數(shù)據(jù)探索」環(huán)節(jié),也就是用來(lái)了解數(shù)據(jù)。它無(wú)法提供明確的行動(dòng)指向,更多是為后期挖掘和分析提供參考,無(wú)法回答“為什么”和“怎么辦”的問(wèn)題。

聚類分析和分類區(qū)別:分類是從特定的數(shù)據(jù)中挖掘模式,作出分類判斷;聚類是根據(jù)數(shù)據(jù)本身特點(diǎn),按照不同的模型來(lái)判斷數(shù)據(jù)之間的相似性、相似性高的一組數(shù)據(jù)聚成一簇。
K-Means 算法是基于距離的聚類方法,在最小誤差函數(shù)的基礎(chǔ)上將數(shù)據(jù)劃分為預(yù)定的類數(shù) K,采用距離作為相似性的評(píng)價(jià)指標(biāo),認(rèn)為兩個(gè)對(duì)象的距離越近,其相似度越高。適用于連續(xù)型數(shù)據(jù)。使用場(chǎng)景可以是:用戶分群分析。
算法過(guò)程
從 n 個(gè)樣本數(shù)據(jù)中隨機(jī)選取 K 個(gè)對(duì)象作為初始的聚類中心(在一開始確定 K 值上,憑業(yè)務(wù)經(jīng)驗(yàn)劃分,所以K值的選定不一定合理);
分別計(jì)算每個(gè)樣本到各個(gè)聚類中心的距離,將對(duì)象分配到距離最近的聚類中;
所有對(duì)象分配完成后,重新計(jì)算 K 個(gè)聚類的中心(類似重新計(jì)算虛擬中心);
與前一次計(jì)算得到的 K 個(gè)聚類中心比較,如果聚類中心發(fā)生變化,轉(zhuǎn)至步驟 2,否則轉(zhuǎn)至步驟 5;
當(dāng)質(zhì)心不發(fā)生變化時(shí),停止并輸出聚類結(jié)果;
度量距離:度量樣本之間的相似性最常用的是歐幾里得距離、曼哈頓距離和閔可夫斯基距離(Python 中目前支持歐氏距離)。
偽代碼如圖所示
初始質(zhì)心如何確定比較好?
所以可以看到,在 K-Means 中有一個(gè)重要的環(huán)節(jié),那就是如何放置初始質(zhì)心,初始質(zhì)心放置的位置不同,聚類的結(jié)果很可能不一樣。一個(gè)好的初始質(zhì)心可以避免更多的計(jì)算,讓算法收斂穩(wěn)定且更快。算法的第一步是在 n 個(gè)樣本中隨機(jī)抽取 K 個(gè)對(duì)象作為初始聚類中心,在初始的時(shí)候可能會(huì)生成在一起,導(dǎo)致算法運(yùn)算慢且不收斂,對(duì)于此類問(wèn)題可以采用 K-means++ 作為優(yōu)化,其核心就是選擇離已選中心點(diǎn)最遠(yuǎn)的點(diǎn),初始質(zhì)心相互間離得盡可能遠(yuǎn)。
如何確定K值?
在 K-Means 中,K 值是人為確定的,如果有業(yè)務(wù)經(jīng)驗(yàn),可以根據(jù)業(yè)務(wù)經(jīng)驗(yàn)來(lái)判斷,如果此類數(shù)據(jù)沒(méi)有先驗(yàn)知識(shí),也不知道如何聚類,劃分的依據(jù)就依賴于組間差異最大化,組內(nèi)差異最小化的評(píng)估指標(biāo)。對(duì)于這個(gè)問(wèn)題可以采用輪廓系數(shù)來(lái)判定,輪廓系數(shù)的取值為(-1,1),輪廓系數(shù)越接近于 1 越好,負(fù)數(shù)則表示聚類效果非常差。單個(gè)樣本的輪廓系數(shù)計(jì)算公式如下:
a:樣本與其自身所在的簇中的其他樣本的相似度,等于樣本與同一簇中所有其他點(diǎn)之間的平均距離;
b:樣本與其他簇中的樣本的相似度,等于樣本與下一個(gè)最近的簇中的所有點(diǎn)之間的平均距離。
值越接近 1 表示樣本與自己所在的簇中的樣本很相似,并且與其他簇中的樣本不相似;越接近 -1 說(shuō)明樣本點(diǎn)與簇外的樣本更相似,與簇內(nèi)樣本不相似;當(dāng)輪廓系數(shù)接近 0 時(shí),則代表簇類差異和簇外差異的樣本相似度一致,無(wú)明顯分界。
K-Means 案例演示
數(shù)據(jù)集:航空公司客戶特征字段。
分析流程為:
import?pandas?as?pd
import?numpy?as?np
import?os
import?seaborn?as?sns
import?matplotlib.pyplot?as?plt
%matplotlib?inline
plt.rcParams['font.family']?=?['Arial?Unicode?MS']?
pd.set_option('display.max_columns',?None)
#?sns.set_style("darkgrid",{"font.sans-serif":['simhei','Droid?Sans?Fallback']})
os.chdir('/data')
df?=?pd.read_csv('air_data.csv')
df.head()
部分?jǐn)?shù)據(jù)字段
描述性統(tǒng)計(jì)分析
查看整體數(shù)據(jù)描述性分析
explore?=?df.describe(percentiles=[],?include='all').T
explore
部分?jǐn)?shù)據(jù)字段
explore['null']?=?len(df)?-?explore['count']
df_check?=?explore[['null',?'max',?'min']]
df_check.columns?=?[['空值記錄數(shù)',?'最大值',?'最小值']]
df_check
部分?jǐn)?shù)據(jù)字段
查看單個(gè)主體變量會(huì)員逐年增長(zhǎng)情況
df['FFP_DATE_year']?=?pd.to_datetime(df['FFP_DATE']).dt.year
#?獲取每個(gè)年份的合計(jì)數(shù)量。方式一
count?=?df['FFP_DATE_year'].value_counts(sort=False).reset_index()
#?獲取每個(gè)年份的合計(jì)數(shù)量。方式二
#?count?=?df.groupby('FFP_DATE')['FFP_DATE'].count()
count.columns?=?['入會(huì)時(shí)間',?'入會(huì)人數(shù)']
count.plot(kind='bar',?x='入會(huì)時(shí)間',?y='入會(huì)人數(shù)',?figsize=(12,8))

查看單個(gè)分類型變量比例變化情況(直方堆積圖)
#?使用數(shù)據(jù)交叉表,計(jì)算每個(gè)年度時(shí)間節(jié)點(diǎn),出現(xiàn)男女分類的數(shù)量情況
cross_table?=?pd.crosstab(index=df['FFP_DATE_year'],columns=df['GENDER'])
cross_table

#?通過(guò)div函數(shù),讓分組每行合計(jì)等于1,用來(lái)看每個(gè)分組占比
cross_table?=?cross_table.div(cross_table.sum(1),?axis=0)
cross_table

cross_table.plot(kind='bar',?stacked=True)

查看單個(gè)分類型變量占比情況(直方圖&餅圖)
#?男女分別數(shù)量合計(jì)占比
sns.countplot(x='GENDER',data=df)

#?男女會(huì)員分別所有數(shù)量。方式一
#?male_count?=?df.groupby('GENDER')['GENDER'].count()[0]
#?female_count?=?df.groupby('GENDER')['GENDER'].count()[1]
#?方式二
male_count?=?pd.value_counts(df['GENDER'])['男']
female_count?=?pd.value_counts(df['GENDER'])['女']
plt.pie([male_count,?female_count],?
????????labels=['男',?'女'],?
????????colors=['lightskyblue',?'lightcoral'],
????????autopct='%1.1f%%',
???????)
plt.show

查看單個(gè)分類型變量頻數(shù)情況(直方圖)
#?方式一:?直接使用sns.countplot 查看單個(gè)變量的分類情況
#?sns.countplot(x='FFP_TIER',data=df)
#?方式二:
FFP_TIER_Level4?=?pd.value_counts(df['FFP_TIER'])[4]
FFP_TIER_Level5?=?pd.value_counts(df['FFP_TIER'])[5]
FFP_TIER_Level6?=?pd.value_counts(df['FFP_TIER'])[6]
plt.bar(x=range(3),?
????????height=[FFP_TIER_Level4,?FFP_TIER_Level5,?FFP_TIER_Level6],
????????width=0.4,
????????alpha=0.8,
????????color='skyblue')
plt.xticks([index?for?index?in?range(3)],?[4,?5,?6])
plt.show()

查看單個(gè)數(shù)值型變量分布情況(箱型圖)
#?會(huì)員年齡分布箱型圖
plt.figaspect
plt.boxplot(df['AGE'].dropna(),
???????????patch_artist=True,
???????????vert=False,
???????????boxprops?=?{'facecolor':?'lightblue'},
???????????labels=['會(huì)員年齡'])
plt.grid(linestyle=":",?color="r")
plt.title('會(huì)員年齡分布箱型圖')
plt.show()

相關(guān)性分析
#?'FFP_TIER','FLIGHT_COUNT','LAST_TO_END','SEG_KM_SUM','EXCHANGE_COUNT','Points_Sum','FFP_DATE'的相關(guān)系數(shù)分析
df_corr?=?df.loc[:,?['FFP_TIER','FLIGHT_COUNT','LAST_TO_END','SEG_KM_SUM','EXCHANGE_COUNT','Points_Sum','FFP_DATE']]
age1?=?df['AGE'].fillna(0)
df_corr['AGE']?=?age1.astype('int64')
#?df的相關(guān)系數(shù)
df_corr?=?df_corr.corr()
df_corr

#?設(shè)置圖形大小
plt.subplots(figsize=(10,10))
#?特征數(shù)據(jù)熱力圖
ax?=?sns.heatmap(df_corr,?annot=True,?cmap='Blues')
ax.set_ylim([8,?0])
ax

數(shù)據(jù)預(yù)處理
數(shù)據(jù)清洗
#?去除票價(jià)為空的行?
airline_notnull?=?df.loc[df['SUM_YR_1'].notnull()?&?df['SUM_YR_2'].notnull(),?:]
#?保留票價(jià)非零數(shù)據(jù),或者平均折扣率不為零且總飛行數(shù)大于0的記錄;AGE去除大于100的記錄
index1?=?airline_notnull['SUM_YR_1']?!=?0
index2?=?airline_notnull['SUM_YR_2']?!=?0
index3?=?(airline_notnull['SEG_KM_SUM']?>?0)?&?(airline_notnull['avg_discount']?!=?0)
index4?=?airline_notnull['AGE']?>?100
airline?=?airline_notnull[(index1?|?index2)?&?index3?&?~index4]
airline.head()
部分?jǐn)?shù)據(jù)字段
屬性歸約
airline?=?airline[['FFP_DATE',?'LOAD_TIME',?'LAST_TO_END','FLIGHT_COUNT','SEG_KM_SUM','avg_discount'?]]
airline['FFP_DATE'],?airline['LOAD_TIME']?=?pd.to_datetime(airline['FFP_DATE']),?pd.to_datetime(airline['LOAD_TIME'])
airline['L']?=?(airline['LOAD_TIME']?-?airline['FFP_DATE']).dt.days
airline?=?airline.drop(['FFP_DATE',?'LOAD_TIME'],?axis=1)
airline.columns?=?['R',?'F',?'M',?'C',?'L']
airline.head()

數(shù)據(jù)標(biāo)準(zhǔn)化
from?sklearn.preprocessing?import?StandardScaler
data?=?StandardScaler().fit_transform(airline)
data[:5,?:]
array([[-0.94493902,?14.03402401,?26.76115699,??1.29554188,??1.43579256],
???????[-0.91188564,??9.07321595,?13.12686436,??2.86817777,??1.30723219],
???????[-0.88985006,??8.71887252,?12.65348144,??2.88095186,??1.32846234],
???????[-0.41608504,??0.78157962,?12.54062193,??1.99471546,??0.65853304],
???????[-0.92290343,??9.92364019,?13.89873597,??1.34433641,??0.3860794?]])
聚類分析
from?sklearn.cluster?import?KMeans
#?構(gòu)建模型,隨機(jī)種子設(shè)為123
kmeans_model?=?KMeans(n_clusters=5,?n_jobs=4,?random_state=123)
#?模型訓(xùn)練
fit_kmeans?=?kmeans_model.fit(data)
#?查看聚類結(jié)果
kmeans_cc?=?kmeans_model.cluster_centers_
kmeans_cc
#?樣本的類別標(biāo)簽
kmeans_lable?=?kmeans_model.labels_
#?統(tǒng)計(jì)不同類別樣本數(shù)目
pd.Series(kmeans_model.labels_).value_counts()
cluster_center?=?pd.DataFrame(kmeans_model.cluster_centers_,?columns=['ZR','ZF','ZM','ZC','ZL'])
#?cluster_center.index?=?pd.DataFrame(kmeans_model.labels_).drop_duplicates().iloc[:,0]
cluster_center

聚類分析可視化(雷達(dá)圖)
#標(biāo)簽
labels?=?cluster_center.columns?
#數(shù)據(jù)個(gè)數(shù)
k?=?5?
plot_data?=?kmeans_model.cluster_centers_
#指定顏色
color?=?['b',?'g',?'r',?'c',?'y']?
angles?=?np.linspace(0,?2*np.pi,?k,?endpoint=False)
#?閉合
plot_data?=?np.concatenate((plot_data,?plot_data[:,[0]]),?axis=1)?
#?閉合
angles?=?np.concatenate((angles,?[angles[0]]))?
fig?=?plt.figure(figsize=(8,6))
#polar參數(shù)
ax?=?fig.add_subplot(111,?polar=True)?
for?i?in?range(len(plot_data)):
??ax.plot(angles,?plot_data[i],?'o-',?color?=?color[i],?label?=?u'客戶群'+str(i),?linewidth=2)#?畫線
ax.set_rgrids(np.arange(0.01,?3.5,?0.5),?np.arange(-1,?2.5,?0.5),?fontproperties="SimHei")
ax.set_thetagrids(angles?*?180/np.pi,?labels,?fontproperties="SimHei")
plt.legend(loc?=?4)
plt.show()

注:以上案例數(shù)據(jù)集來(lái)源于《Python數(shù)據(jù)分析與挖掘?qū)崙?zhàn)(第2版)》,但是整體分析流程有調(diào)整。可以互相查閱學(xué)習(xí)。
需要了解可以關(guān)注公眾號(hào),在對(duì)話框回復(fù)“0103”獲取數(shù)據(jù)集 + 代碼。
