Supervised learning:目标变量实现存在
Unsupervised learning:目标变量实现不存在
Clustering聚类是一种无监督的学习, 它将相似的对
象归到一个簇中, 将不相似对象归到不同簇中.
创建K个点作为起始质心(通常是随机选择)
当任意一个点的簇分配结果发生改变时
对数据集中的每个数据点
对每个质心
计算质心于数据点之间的距离
将数据点分配到距其最近的簇
对每一个簇,计算簇中所有点的均值并将均值作为质心
python代码
def loadDataSet(fileName):
"""
从文件加载数据集
:param fileName: 文件名
:return:数据矩阵
"""
dataMat = []
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
# 映射所有的元素为float(浮点型)类型
fltLine = list(map(float, curLine))
dataMat.append(fltLine)
return dataMat
def distEclud(vecA, vecB):
"""
计算两个向量的欧式距离
:param vecA: A向量
:param vecB: B向量
:return: 量向量之间的欧式距离
"""
return np.sqrt(sum(np.power(vecA - vecB, 2)))
def randCent(dataSet, k):
"""
构建一个包含k个随机质心的集合
:param dataSet: 数据集
:param k: 质心数量
:return: 质心集合
"""
col = np.shape(dataSet)[1] # 列的数量
centroids = np.mat(np.zeros((k, col))) # 创建K个质心矩阵
# 创建随机簇质心,并且在每一维的边界内
for j in range(col):
minJ = min(dataSet[:, j]) # 最小值
rangeJ = float(max(dataSet[:, j]) - minJ) # 范围 = 最大值 - 最小值
centroids[:, j] = np.mat(minJ + rangeJ * np.random.rand(k, 1)) # 随机生成
return centroids
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
"""
k_means聚类算法
:param dataSet:数据集
:param k:质心数量
:param distMeas:距离计算方法,默认欧氏距离distEclud()
:param createCent:获得k个质心的方法,默认随机获取randCent()
:return:k个聚类,聚类结果及误差
"""
row = np.shape(dataSet)[0] # 行数
clusterAssment = np.mat(np.zeros((row, 2))) # 创建一个与dataSet行数一样,但是有两列的矩阵,用来保存簇分配结果
centroids = createCent(dataSet, k) # 创建k个随机质心
clusterChanged = True # 聚类结果是否发生变化的布尔类型
while clusterChanged: # 只要聚类结果一直发生变化,就一直执行聚类算法,直至所有数据点聚类结果不变化
clusterChanged = False # 聚类结果变化布尔类型置为false
for i in range(row): # 循环每一个数据点并分配到最近的质心中去
# 初始化最小距离最正无穷;最小距离对应索引为-1
minDist = np.inf
minIndex = -1
for j in range(k): # 循环k个类的质心
distJI = distMeas(centroids[j, :], dataSet[i, :]) # 计算数据点到质心的欧氏距离
if distJI < minDist: # 如果距离比minDist(最小距离)还小,更新minDist(最小距离)和质心的索引(index)
# 当前距离定为当前最小距离;最小距离对应索引对应为j(第j个类)
minDist = distJI
minIndex = j
if clusterAssment[i, 0] != minIndex: # 当前聚类结果中第i个样本的聚类结果发生变化:布尔类型置为true,继续聚类算法
clusterChanged = True # 簇改变
clusterAssment[i, :] = minIndex, minDist ** 2 # 更新当前变化样本的聚类结果和平方误差
print(centroids)
for cent in range(k): # 更新质心
ptsInClust = dataSet[np.nonzero(clusterAssment[:, 0].A == cent)[0]] # 将数据集中所有属于当前质心类的样本通过条件过滤筛选出来
centroids[cent, :] = np.mean(ptsInClust, axis=0) # 将质心修改为簇
# 返回k个聚类,聚类结果及误差
return np.mat(centroids), clusterAssment
将所有点看成一个簇
当簇数目小于k时
对于每一个簇
计算总误差
在给定的簇上面进行K-均值聚类(k=2)
计算将该簇一分为二之后的总误差
选择使得误差最小的那个簇进行划分操作
def bikmeans(dataSet, k, distMeas=distEclud):
"""
二分K-均值聚类算法
:param dataSet:数据集
:param k:质心数量
:param distMeas:距离计算方法,默认欧氏距离distEclud()
:return:
"""
numSamples = dataSet.shape[0] # 获得数据集的样本数
clusterAssment = np.mat(np.zeros((numSamples, 2))) # 初始化一个元素值0的(numSamples,2)矩阵
centroid0 = np.mean(dataSet, axis=0).tolist()[0] # 列表中的第一个列表元素:即全部数据每个属性
centList = [centroid0] # 当前聚类列表为将数据集聚为一类
for j in range(numSamples): # 遍历每个数据集样本
clusterAssment[j, 1] = (distMeas(np.mat(centroid0), dataSet[j, :])) ** 2 # 计算当前聚为一类时各个数据点距离质心的平方距离
while len(centList) < k: # 循环,直至二分k-均值达到k类为止
lowestSSE = np.inf # 将当前最小平方误差置为正无穷
for i in range(len(centList)): # 遍历当前每个聚类
# 通过数组过滤筛选出属于第i类的数据集合
ptsInCurrCluster = dataSet[np.nonzero(clusterAssment[:, 0].A == i)[0], :]
# 对该类利用二分k-均值算法进行划分,返回划分后结果,及误差
centroidMat, splitClusAss = kMeans(ptsInCurrCluster, 2, distMeas)
sseSplit = sum(splitClusAss[:, 1]) # 计算该类划分后两个类的误差平方和
# 计算数据集中不属于该类的数据的误差平方和
sseNotSplit = sum(clusterAssment[np.nonzero(clusterAssment[:, 0].A != i)[0], 1])
print("sseSplit,and notSplit:", sseSplit, sseNotSplit)
if (sseSplit + sseNotSplit) < lowestSSE: # 划分第i类后总误差小于当前最小总误差
bestCentToSplit = i # 第i类作为本次划分类
bestNewCents = centroidMat # 第i类划分后得到的两个质心向量
bestClustAss = splitClusAss.copy() # 复制第i类中数据点的聚类结果即误差值
lowestSSE = sseSplit + sseNotSplit # 将划分第i类后的总误差作为当前最小误差
# 数组过滤筛选出本次2-均值聚类划分后类编号为1数据点,将这些数据点类编号变为当前类个数+1,作为新的一个聚类
bestClustAss[np.nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)
# 同理,将划分数据集中类编号为0的数据点的类编号仍置为被划分的类编号,使类编号连续不出现空缺
bestClustAss[np.nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
print("the bestCentToSplit is:", bestCentToSplit)
print("the len of bestClustAss is :", len(bestClustAss))
centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0] # 更新质心列表中的变化后的质心向量
centList.append(bestNewCents[1, :].tolist()[0]) # 添加新的类的质心向量
# 更新clusterAssment列表中参与2-均值聚类数据点变化后的分类编号,及数据该类的误差平方
clusterAssment[np.nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :] = bestClustAss
return np.mat(centList), clusterAssment
python代码:
def distSLC(vecA, vecB):
"""
球面距离计算及簇绘图函数
:param vecA:
:param vecB:
:return:
"""
a = np.sin(vecA[0, 1] * np.pi / 180) * np.sin(vecB[0, 1] * np.pi / 180)
b = np.cos(vecA[0, 1] * np.pi / 180) * np.cos(vecB[0, 1] * np.pi / 180) * np.cos(
np.pi * (vecB[0, 0] - vecA[0, 0]) / 180)
return np.arccos(a + b) * 6371.0
def clusterClubs(numClust=5):
"""
:param numClust:
:return:
"""
datList = []
for line in open('places.txt').readlines(): # 解析文本数据中的每一行中的数据特征值
lineArr = line.split('\t')
datList.append(([float(lineArr[4]), float(lineArr[3])]))
dataMat = np.mat(datList)
# 利用2-均值聚类算法进行聚类
myCentroids, clustAssing = bikmeans(dataMat, numClust, distMeas=distSLC)
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
scatterMarkers = ['s', 'o', '^', '8', 'p', 'd', 'v', 'h', '>', '<']
axprops = dict(xticks=[], yticks=[])
ax0 = fig.add_axes(rect, label='ax0', **axprops)
# imgP = plt.imread('portland.png')
# ax0.imshow(imgP)
ax1 = fig.add_axes(rect, label='ax1', frameon=False)
for i in range(numClust):
ptsInCurrCluster = dataMat[np.nonzero(clustAssing[:, 0].A == i)[0], :]
markerStyle = scatterMarkers[i % len(scatterMarkers)]
ax1.scatter(ptsInCurrCluster[:, 0].flatten().A[0], ptsInCurrCluster[:, 1].flatten().A[0],
marker=markerStyle, s=90)
ax1.scatter(myCentroids[:, 0].flatten().A[0], myCentroids[:, 1].flatten().A[0], marker='+', s=300)
plt.show()
如何选择聚类个数?
该方法十分直观,但很难用数学公式量化,随意性较大学术界提出了一种数学上更为严谨的方法决定聚类个数:轮廓分析silhoutte analysis(定量分析聚类质量)
应用示例
凝聚聚类:许多基于相同原则构建的聚类算法。
由于算法的工作原理,凝聚算法不能对新数据点做出预测
层次聚类与树状图:
DBSCAN