[问题背景]
假定有这样的数据集,txt格式,ANSI编码:
YZN,133,108,76
ZHY,96,145,101
WYZ,132,107,60
DHY,100,102,120
CYH,139,99,93
LHY,73,149,81
ZHY,85,148,93
TQP,39,138,85
ZZL,145,112,71
HJC,101,116,118
XZY,99,98,117
每行第一列是学生姓名,第二列是语文成绩,第三列是数学成绩,第四列是英语成绩。
目标是利用KMeans对学生进行聚类,例如聚成3类。
[问题分析]
数据处理loadData()函数的设计在[Python数据处理] 怎样用Python预处理txt文档提取数据中已经讲过。
应用DBSCAN聚类器:
数据预处理grade.txt(需要和该py代码处于同一目录下)
if __name__ == '__main__':
Name, Data = loadData('grade.txt')
设置DBSCAN聚类器所需要的参数eps(邻域半径)和min_samples(最小样本数)
eps = 21
min_samples = 2
DBSCAN聚类的原理请参见我在B站的投稿视频:DBSCAN聚类 动画演示
用DBSCAN方法设置一个eps=21,min_samples=2的聚类器db,同时将预处理好的数据Data传入fit()接口
db = skc.DBSCAN(eps=eps_v, min_samples=min_samples_v).fit(Data)
返回的db中包含了标签labels_,用label承接
label = db.labels_
得到的标签label为:
[ 0 1 0 2 0 1 1 -1 0 2 2]
按照标签对聚类结果分类整理并输出:
outputName = []
noise = []
for i in range(max(label)+1):
outputName.append([])
for i in range(len(Name)):
if(label[i] != -1):
outputName[label[i]].append(Name[i])
else:
noise.append(Name[i])
print(outputName)
print("噪声点:", noise)
其中,因为DBSCAN是不需要预先指定聚类的类数的,所以标签的值有多少种并不能事先确定,一般来说标签为-1是噪声点,而其他值(从0递增)分别对应正常数据点的各个类别。所以标签种数需要利用max(label)确定,而且由于range()是左闭右开的,所以要max(label)+1
标签为-1的归类到噪声点类别中。
输出分类结果:
[['YZN', 'WYZ', 'CYH', 'ZZL'], ['ZHY', 'LHY', 'ZHY'], ['DHY', 'HJC', 'XZY']]
噪声点: ['TQP']
可以看到,DBSCAN分类结果与KMeans的分类结果大致相同,均筛选出了
英语较好的DHY, HJC, XZY;
语文较好的YZN, WYZ, CYH, ZZL;
数学较好的ZHY, LHY, ZHY。
而TQP由于过低的语文成绩39被归到了噪声点中,是DBSCAN聚类结果与KMeans聚类结果唯一的不同。
KMeans聚类的使用可以参见[Python数据挖掘] sklearn-KMeans聚类
相比较而言:
DBSCAN聚类结果稳定,不需要预先指定类别数目,但需要自己调参;
KMeans聚类结果有时不稳定,需要预先指定类别数目,不需要调参。
最后附上DBSCAN使用的完整代码如下:
import sklearn.cluster as skc
def loadData(filePath):
fr = open(filePath, 'r+')
lines = fr.readlines()
retName = []
retData = []
for line in lines:
items = line.strip().split(',')
retName.append(items[0])
retData.append([float(items[i]) for i in range(1,len(items))])
return retName, retData
if __name__ == '__main__':
Name, Data = loadData('grade.txt')
eps = 21
min_samples = 2
db = skc.DBSCAN(eps=eps, min_samples=min_samples).fit(Data)
label = db.labels_
outputName = []
noise = []
for i in range(max(label)+1):
outputName.append([])
for i in range(len(Name)):
if(label[i] != -1):
outputName[label[i]].append(Name[i])
else:
noise.append(Name[i])
print(outputName)
print("噪声点:", noise)