from random import randint
class Cluster(object):
def _init_(self):
#簇的所有特征向量
self._fcs = []
# 簇的中心点
self._center = None
def set_center(self, new_center):
"""
设置中心点
:param new_center:
:return:
"""
self._center = new_center
def get_center(self):
return self._center
def add_fc(self, fc):
"""
簇中添加特征向量的场景
:param fc:
:return:
"""
self._fcs.append(fc)
def updata_center(self):
"""
根据簇中所有特征向量更新中心点
:return:
"""
#特征向量中特征的数量
feature_num = len(self._fcs[0])
# 特征向量的数量
fc_num = len(self._fcs)
for i in range(feature_num):
#开始计算当前特征在所有特征向量上的总和
current_sum = 0
for fc in self._fcs:
current_sum += fc[i]
#将中心点对应特征的值更新为均值
self._center[i] = current_sum / fc_num
def distance(self, fc):
"""
计算簇的中心点到特征向量fc的距离
:param fc:
:return:
"""
length = len(fc)
distance = 0
for i in range(length):
distance += (self._center[i] - fc[i])**2
return sqrt(distance)
class KMeans(object):
def _init_(self, k):
self._k = k
self._clusters = [Cluster() for i in range(self._k)]
def _select_k_indexs(self, total_num):
"""
选择k个索引,选出来的索引用于从数据集中随机挑选k个簇的中心点
:param total_num:
:return:
"""
k_indexes = []
while len(k_indexes) < self._k:
#生成一个随机的索引
index = randint(0, total_num - 1)
#如果当前的索引不在索引list里,把它加到k_indexes中
if index not in k_indexes:
k_indexes.append(index)
return k_indexes
def _init_clusters(self, dataset):
"""
c初始化kmeans的k个c簇
:param dataset:
:return:
"""
k_indexes = self._select_k_indexs(self._k, len(dataset))
for i in range(self._k):
self._clusters[i].set_center(dataset[k_indexes[i]])
def train(self, dataset, iter_times):
"""
:param dataset:
:param iter_times: 迭代次数
:return:
"""
#先初始化所有的k个簇
self._init_clusters(dataset)
#特征向量的个数
fc_num = len(dataset)
#进行iter_times次迭代
for i in range(iter_times):
#开始循环数据集中的数据
for j in range(fc_num):
#当前的特征向量
current_fc = dataset[j]
#记录当前特征向量的归属cluster的索引为0,最小距离为和第一个簇的距离
cluster_index = 0
min_dist = self._clusters[0].distance(current_fc)
#计算当前特征向量和所有簇的距离
for k in range(1, self._k):
current_dist = self._clusters[k].distance(current_fc)
#这里使用了打擂台算法,求的最小距离,节省了求出所有距离之后进行排序的过程
if current_dist < min_dist:
cluster_index = k
min_dist = current_dist
#所有k个距离计算完之后把当前的特征向量加入到指定的簇中
self._clusters[cluster_index].add_fc(current_fc)
#数据集中所有数据都迭代完之后开始更新每个簇的中心点
for cluster in self._clusters:
cluster.updata_center()
def get_clusters(self):
return self._clusters