1.下载数据集,导入工程
本文附有三组聚类数据集,均为txt格式,其中带有_cl标签的文件是label,另外一个则是数据集(点的坐标)。
2.读入txt文件,并做相应的处理
fileName_data = 'cth3.txt'
fileName_label = 'cth3_cl.txt'
X, y = loadData(fileName_data, fileName_label)
def loadData(fileName_data, fileName_label):
dataset = []
labelset = [] # 标签
with open(fileName_label) as txtData:
lines = txtData.readlines()
for line in lines:
lineData = line.strip().split() # 分割数据,每一行为一个数据
labelset.append(lineData)
labelset = np.array(labelset) #这里将数据由List转为数组
with open(fileName_data) as txtData:
lines = txtData.readlines()
for line in lines:
lineData = line.strip().split()
dataset.append(lineData)
dataset = np.array(dataset)
return dataset, labelset
设置断点运行以上程序,发现读取到的data X和label y如下所示:
可以发现每个数字都是字符串格式,而不是整型或者浮点数,这不利于之后的数据处理,因此考虑将X,y中所有的数据类型改为int(在该数据集中只存在整型,如果存在小数则需考虑将数据类型改为float)
X = X.astype('int64')
y = y.astype('int64')
运行以上两行代码,得到如下结果:
这样就得到了数据类型为数组的数据集X和标签集y,可以画一张图来看看数据点的分布
# 数据可视化
colValue = ['r', 'y', 'g', 'b'] # 创建颜色数组
plt.figure()
plot_scatter(X, y, colValue)
plt.title("expected results")
plt.show()
def plot_scatter(data, label, colValue):
for point, lbl in zip(data, label):
colorIndex = lbl # 用样本点的聚类标签区分点的颜色
x = point[0] # 横坐标
y = point[1] # 纵坐标
plt.scatter(x, y, marker='o', color=colValue[colorIndex - 1], label=colorIndex)
运行得到下图,可见属于不同簇的数据点颜色不同
3.对数据进行密度聚类(DBSCAN)并评估其性能
# 调用库函数进行DBSCAN聚类
cluster = DBSCAN(eps=10, min_samples=5).fit(X) # 这两个超参数需要反复调试,以达到最佳性能
pre_labels = cluster.labels_
num0fclusters = len(set(pre_labels)) - (1 if -1 in pre_labels else 0) # 查看密度聚类将数据聚成了几类(-1是噪声点,需要去除)
print("predicted number of clusters:", num0fclusters)
plt.figure()
plot_scatter(X, pre_labels, colValue)
plt.title("DBSCAN predicted results")
plt.show()
# 性能衡量
ARI = metrics.adjusted_rand_score(y, pre_labels)
print('DBSCAN ARI: %.2f' % ARI)
运行结果如下图
通过ARI指标以及图片比对,易知聚类结果与预期结果完全符合。通过试验发现层次聚类也能实现100%的准确度,而K-means聚类则不行。下图为K-means聚类算法得到的结果。
4.可能遇到的问题
1)在调用ARI = metrics.adjusted_rand_score(y, pre_labels)时报错。
ValueError: labels_true must be 1D: shape is (1146,1)
说明y的维度不对,我们处理数据后所得的y是(1146,1)维的,而该函数则需要(1146,)这样的。观察调试器中y的格式,发现y中每个元素都被中括号包围,是数组,而不是一个纯粹的整数,因此稍加处理,可以得到正确维度的y。
y = preprocess(y)
def preprocess(y):
new = []
for y1 in y:
new.append(y1[0])
new = np.array(new)
return new
可以发现现在y的维度为(1146,),每个元素都是一个整数。这样ARI函数就可以正常运行。
2)如何对如K-means算法的超参数进行优化,选择合适的超参数
另开一篇文章介绍简单的超参数优化步骤