一、数据介绍
实验数据包括1000个用户(编号1-1000)对不同商品(共200件,编号1-200)的评分(范围为1-5),共64154条数据
二、数据分析
数据特点
-
每个用户购买的商品数量不固定,所以并不是对每件商品都有评分。以下是部分统计得到的不同用户购买的商品数量。
可以看到,每个用户购买的数量有多有少。
三、数据处理
-
将每个用户的评分数据转换成一个行向量,这样整个数据集则转换成1000 * 200 的矩阵进行后续的运算
-
由于用户并不是对200件商品均进行购买,所以对于每一个行向量,其中都会包含空值
-
为了能更好的实现聚类,需要对这些空数据进行填充。常用的填充方法有:0值填充、平均数填充、随机数填充和众数填充等
-
每一种填充方式都有其特点和应用场景。针对此次应用场景,我采用的是列向量平均值填充的方式。不同用户对某个特定商品的评分的平均值是能够比较好的反映用户对该商品的满意度,把列向量的平均值作为默认的数据进行填充不会对用户的分类产生大的影响。
-
首先统计各个商品出现的次数,如图:
-
其次统计各个用户对特定商品的评分的总和,如图:
-
评分总和除以商品个数即为该商品的评分平均值。
-
四、实验过程
生成特征矩阵
-
根据实验数据集生成初始特征矩阵,然后再根据计算出的商品的平均值对矩阵中的空数据进行填充。
def creatNewMar(): with open(path, 'r') as f: list = f.readlines() matrix = np.full(shape=(1000, 200), fill_value=0.0) userids = [] for line in list: everyline = line.split(',') userid = int(everyline[0]) item = int(everyline[1]) score = float(everyline[2].replace('\n','')) matrix[userid-1][item-1] = score print(matrix) #填充矩阵 file_name1 = xlrd.open_workbook('pingjun.xls') table1 = file_name1.sheets()[0] nrows1 = table1.nrows idandpingjunzhi = {} k = 0 while k < nrows1: id = table1.row_values(k)[0] pingjunzhi = table1.row_values(k)[1] idandpingjunzhi[id] = pingjunzhi k = k + 1 i = 0 while i < 1000: j = 0 while j < 200: if matrix[i][j] == 0.0: matrix[i][j] = idandpingjunzhi[str(j+1)] j = j + 1 i = i + 1 print('-----------') print(matrix) return matrix
执行结果:
-
对特征矩阵填充完毕以后,还需要对矩阵中的数据进行标准化。常见的标准化方法有:归一化、标准差标准化和稳健标准化。这3种方式python都提供了相应的库函数。本次实验采用的是归一化的标准化方式。
from sklearn import preprocessing #归一化 def nomal(mar): # Max-Min标准化 # 建立MinMaxScaler对象 minmax = preprocessing.MinMaxScaler() # 标准化处理 data_minmax = minmax.fit_transform(mar) print('==============') print(data_minmax) return data_minmax
执行结果:
-
当前1000 * 200 的特征矩阵维度很高,不利于计算,需要进行降维操作。降维可以去除无用的噪声数据,减少计算量。降维中需要注意的是在压缩数据的过程中尽量让信息损失最小化,同时一般选择降至二维或者三维以便后续的可视化。本次作业采用的是PCA主成分分析算法,它是一种线性降维,将高维的数据降至二维。python同样提供了相应的库函数。
from sklearn.decomposition import PCA #PCA降维 def pca(mar): pca = PCA(n_components=2) low_dim_data = pca.fit_transform(mar) print("-------------------") print(low_dim_data) return low_dim_data
运行结果:
高斯混合聚类GMM
-
2维k-means聚类的缺点:
- 类的形状不够灵活,拟合结果与实际相差较大,精度有限。
- 样本属于每一个簇的概率是定性的,只有是与否,不能输出概率值。应用中缺少鲁棒性。
-
高斯混合模型(GMM)可以看做是k-means模型的一个优化。高斯混合模型试图找到多维高斯模型概率分布的混合表示,从而拟合出任意形状的数据分布。
-
代码:通过肘方法得知最佳的聚类个数5,并设置random_state为100
#高斯混合聚类 def gmm(erwei): erwei = erwei[:, ::-1] # 方便画图 gmm = GMM(n_components=5,random_state=100).fit(erwei) # 指定聚类中心个数为5 labels = gmm.predict(erwei) print(labels) #预测分类文件 file_handle = open('pre.txt', mode='a') for i in range(0,len(labels)): txt = str(i+1) + ' ' + str(labels[i]) + '\n' file_handle.write(txt) file_handle.close() #真实分类文件 file_handle = open('rea.txt', mode='a') for i in range(0, len(labels)): if -1 < i < 150: txt = str(i + 1) + ' ' + str(labels[0]) + '\n' file_handle.write(txt) if 149 < i < 430: txt = str(i + 1) + ' ' + str(labels[150]) + '\n' file_handle.write(txt) if 429 < i < 555: txt = str(i + 1) + ' ' + str(labels[430]) + '\n' file_handle.write(txt) if 554 < i < 800: txt = str(i + 1) + ' ' + str(labels[555]) + '\n' file_handle.write(txt) if 799 < i < 1000: txt = str(i + 1) + ' ' + str(labels[800]) + '\n' file_handle.write(txt) file_handle.close() plt.scatter(erwei[:, 0], erwei[:, 1], c=labels, s=50, cmap='viridis') plt.show()
分类结果:一个大小为1000的数组,数值0、1、2、3、4代表该用户的分类。
可视化:
评估分类结果
-
评价指标
-
混淆矩阵:在进行二分类或多分类任务中,混淆矩阵用来表示测试集预测类与实际类的对应关系,矩阵中横坐标表示实际的类,纵坐标表示预测的类。
-
精确率P:混淆矩阵主对角线上元素和除以混淆矩阵所有元素和。公式为:
-
召回率R:某各类 i 的召回率,混淆矩阵中(i,i)所在的元素除以下标为i的横坐标元素和。样本的总体召回率为所有的类的召回率的平均值,公式为:
-
F1-Measure:公式如下:
-
-
编写代码,计算各个评价指标。运行结果如下
五、实验总结
- 平均值的填充应该使用列向量的平均值,也可以求出列向量数据的正态分布,再进行填充。
- k-means聚类的方法,简单易理解,但也包含诸多缺点,实验的准确率只有60%左右。采用高斯混合聚类GMM之后,准确率大大提升。优秀的聚类方法还有BIRCH算法、谱聚类算法等,接下来可以尝试用这些不同的方法进行实验,比较各个算法对本次实验数据集分类结果的优劣。