最近一直想用聚类来分析一下现在的数据,今天发现了一本《python数据分析与挖掘实战》,其中有个案例是介绍航空公司客户价值的分析,其中用到的聚类方法是K-Means方法,搜索网上相关的内容发现好多博客都有些这个案例,找了一个认为比较好的博客来学习,并用自己的数据进行了聚类。
参考博客:https://blog.csdn.net/a857553315/article/details/79177524
1. 读取数据
import pandas as pd #数据分析
import numpy as np #科学计算
from pandas import Series,DataFrame
//读取数据
columns = ['mac', 'tv_duration', 'movie_duration', 'variety_duration', 'tourism_duration','sports_duration',
'life_duration','game_duration','fashion_duration','entertainment_duration','education_duration',
'doc_duration','children_duration','cartoon_duration','car_duration','baby_duration','MV_duration']
df = pd.read_table("duartion_user.txt")
df.columns=columns
#因为其他几类数据用户看的较少,因此只选择下面的7类内容进行分析
data_train=df[['tv_duration', 'movie_duration', 'variety_duration','sports_duration','doc_duration','children_duration','cartoon_duration']]
data_train.head()
结果为:
2.过滤离群值
本文刚开始没有对数据进行过滤,发现怎么聚类都聚不好,后来发现可能是存在离群值,因此参考:
https://www.jianshu.com/p/b490ac402fe6 中最简单的统计分析,进行了简单处理。
min_data= data_train.mean(axis=0)-2*data_train.std(axis=0)
max_data=data_train.mean(axis=0)+2*data_train.std(axis=0)
data_train=data_train[data_train>min_data]
data_train=data_train[data_train<max_data]
data_train
发现存在NaN值,因此对行存在NaN的值进行过滤
data_train=data_train.dropna()
data_train
发现11行的被过滤掉了。
3.归一化处理
#归一化处理
filter_zscore_data=(data_train - data_train.mean(axis=0))/(data_train.std(axis=0))
4.聚类分析
#中文和负号的正常显示
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
def distEclud(vecA,vecB):
'''
计算两个向量的欧式距离的平方,并返回
'''
return np.sum(np.power(vecA-vecB,2))
def test_Kmeans_nclusters(data_train):
'''
计算不同的k值时,SSE的大小变化
'''
data_train=data_train.values
nums=range(2,10)
SSE=[]
for num in nums:
sse=0
kmodel=KMeans(n_clusters=num,n_jobs=4)
kmodel.fit(data_train)
#簇中心
cluster_center_list=kmodel.cluster_centers_
#个样本属于的簇序号列表
cluster_list=kmodel.labels_.tolist()
for index in range(len(data_train)):
cluster_num=cluster_list[index]
sse+=distEclud(data_train[index],cluster_center_list[cluster_num])
print (u"簇数是",num,u"时;SSE是",sse)
SSE.append(sse)
return nums,SSE
nums,SSE = test_Kmeans_nclusters(filter_zscore_data)
#画图,通过观察SSE与K的取值尝试找出合适的k值
plt.rcParams['font.sans-serif']='SimHei'
plt.rcParams['font.size']=12
plt.rcParams['axes.unicode_minus']= False
#使用ggplot的绘图风格
plt.style.use('ggplot')
##绘图观测SSE与簇个数的关系
fig=plt.figure(figsize=(10,8))
ax= fig.add_subplot(1,1,1)
ax.plot(nums,SSE,marker='+')
ax.set_xlabel("n_clusters",fontsize=18)
ax.set_xlabel("SSE",fontsize=20)
fig.suptitle("KMeans",fontsize=20)
plt.show()
def test_keams(num):
kmodel=KMeans(n_clusters=num,n_jobs=4)
kmodel.fit(filter_zscore_data)
#简单打印结果
r1 = pd.Series(kmodel.labels_).value_counts()#统计各个类别的数目
r2 = pd.DataFrame(kmodel.cluster_centers_)#找出聚类中心
print r1
print r2
#所有簇中心坐标中最大值和最小值
max = r2.values.max()
min= r2.values.min()
r = pd.concat([r2,r1],axis=1) #横向连接(0是纵向),得到聚类中心对应的类别下的数目
r.columns=list(filter_zscore_data.columns)+[u'类别数目'] #重命名表头
fig = plt.figure(figsize=(10,8))
ax= fig.add_subplot(111,polar= True)
center_num=r.values
feature=['tv_duration','movie_duration','variety_duration','sports_duration','doc_duration','children_duration','cartoon_duration']
N =len(feature)
for i,v in enumerate(center_num):
#设置雷达图的角度,用于平分切开一个圆面
angles=np.linspace(0,2*np.pi,N,endpoint=False)
#为了使雷达图一圈封闭起来,需要下面的步骤
center=np.concatenate((v[:-1],[v[0]]))
angles=np.concatenate((angles,[angles[0]]))
# 绘制折线图
ax.plot(angles, center, 'o-', linewidth=2, label = "第%d簇人群,%d人"% (i+1,v[-1]))
# 填充颜色
ax.fill(angles, center, alpha=0.25)
# 添加每个特征的标签
ax.set_thetagrids(angles * 180/np.pi, feature, fontsize=15)
# 设置雷达图的范围
ax.set_ylim(min-0.1, max+0.1)
# 添加标题
plt.title('客户群特征分析图', fontsize=20)
# 添加网格线
ax.grid(True)
# 设置图例
plt.legend(loc='upper right', bbox_to_anchor=(1.3,1.0),ncol=1,fancybox=True,shadow=True)
# 显示图形
plt.show()
test_keams(8)
尝试了聚类次数发现当为8时,效果不错,如图。
当k取值为8时,得到最好的聚类效果,将所有的客户分成8个人群,
其中7个是各个视频类型使用较多的用户,这种往往用户都会相对较少;剩下的就是各个领域都不突出的用户,往往占据大多数。除了第一类,应该都是价值较大的用户,应重点分析