1、kmeans
- 步骤一:初始化聚类中心C1,i=1,2…cn 。经典做法是从所有数据点中任取C个点。
- 步骤二:确定隶属矩阵U
- 步骤三:计算价值函数,如果它小于某个确定的阈值,或它相对上次价值函数的该变量小于某个阈值,则算法停止。
- 步骤四:修正聚类中心.
优点:对于大型数据集也是简单高效、时间复杂度、空间复杂度低。
缺点:最重要是数据集大时结果容易局部最优;需要预先设定K值,对最先的K个点选取很敏感;对噪声和离群值非常敏感;只用于numerical类型数据;不能解决非凸(non-convex)数据。1
import copy
import numpy as np
import math
import random
'''
k-means
步骤一:初始化聚类中心C1,i=1,2....cn 。经典做法是从所有数据点中任取C个点。
步骤二:确定隶属矩阵U
步骤三:计算价值函数,如果它小于某个确定的阈值,或它相对上次价值函数的该变量小于某个阈值,则算法停止。
步骤四:修正聚类中心.
'''
class k_Means(object):
def __init__(self, k=2):
'''
:param k:分组数
'''
self.k = k
def distEclud(self, vectorA, vectorB):
'''欧式距离
:param vectorA:
:param vectorB:
:return:distance of vectorA and vectorB
'''
a = (vectorA[0, 0] - vectorB[0, 0]) ** 2
b = (vectorA[0, 1] - vectorB[0, 1]) ** 2
return math.sqrt(a + b)
def testDistEclud(self, vectorA, vectorB):
'''
测试欧氏距离
:return:
'''
vectorA = np.array([vectorA])
vectorB = np.array([vectorB])
return self.distEclud(vectorA, vectorB)
def getData(self):
'''
:return:二维数组,模拟一些随机点,进行kmeans分类
'''
data = np.array([[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [10, 10], [11, 10]])
return data
# 初始化质心
def initCentroids(self, dataset):
'''
:param dataset:被无监督分类的数据集
:param k:用户设定的分为几类
:return:返回质心
'''
k = self.k
numSamples, dim = dataset.shape
# print(numSamples, dim)
# 产生k行 dim列的零矩阵
centroids = np.zeros((k, dim))
# 定义重复列表
repeat_list = []
# 找出每个k的随机质点
'''
1、处理了重复的情况,通过一个repeate_list去解决重复问题导致两个质心是相同的
2、问题出现在array对比是元素对比,必须any或者all进行统一判断是否一样
3、如果完全相同就跳过重新生成随机数。
'''
for i in range(k):
while True:
# flag每次都要初始化,如果有一次生成了两个相同的质心,flag变为0 要重新来一次,只有变回1 再去判断第二次生成的是否一样才可以跳出。
flag = 1
index = int(random.uniform(0, numSamples)) # 服从均匀分布0-数据量的随机数
for repeatElement in repeat_list:
# print(repeatElement)
# print(dataset[index, :])
# print(all(repeatElement == dataset[index, :]))
if all(repeatElement == dataset[index, :]):
flag = 0
break
if flag:
# print(dataset[index, :])
centroids[i, :] = dataset[index, :]
# print(centroids[i, :])
repeat_list.append(dataset[index, :])
break
else:
continue
return centroids
def kMeans(self, dataset, centroids):
'''
:param dataset:初始数据集
:param centroids:初始化的中心
:return:列表,其中包含k簇(array)
'''
step = 0
dataset_copy = copy.deepcopy(dataset)
# print(dataset_copy, "数据集", type(dataset_copy))
# print(centroids, "质心", type(centroids))
# ##在深copy的数组中去掉两个质心。(弃用)
# removeIndex = []
# for i in range(len(centroids)):
# xList = np.where(dataset == centroids[i])[0].tolist()
# for i in range(len(xList)):
# if xList.count(xList[i]) > 1:
# removeIndex.append(xList[i])
# removeIndex = list(set(removeIndex))
# # print(removeIndex)
# dataset_copy = np.delete(dataset_copy, removeIndex, axis=0)
# # print(dataset_copy, "deepcopy")
# 删除质心后的array距离
# print(centroids[0])
# print(dataset_copy[0])
print("----------迭代开始-----------")
# print(centroids, "一开始的质心")
while True:
km = [[] for i in range(self.k)]
for i in range(len(dataset_copy)):
# 计算到所有质心的距离
_see = [self.testDistEclud(dataset_copy[i], centroids[j]) for j in range(self.k)]
# 找出最近的簇
min_index = _see.index(min(_see))
# 把最近的并入对应簇
km[min_index].append(i)
# print(km)
# print(dataset_copy[km[0][0]])
# 更换质心
step += 1
k_new = []
for i in range(self.k):
# print("---------------")
a = [dataset_copy[km[i][j]] for j in range(len(km[i]))]
# 绝对不能把质心加入到列表
# a.append(centroids[i])
_centroids = sum(a) / len(a)
# print(_centroids)
k_new.append(_centroids)
k_new = np.array(k_new)
# print(k_new)
# 更新质心
print("newcentroids", k_new)
# print(centroids, "centroids")
if ~(k_new == centroids).all():
centroids = k_new
print("---------------->>>>>>>>>更换质心")
# print(centroids)
else:
print("------------------------>>>>>>>迭代结束")
print("迭代次数", step)
# print(km)
result_km = []
for i in range(self.k):
a = [dataset_copy[km[i][j]] for j in range(len(km[i]))]
result_km.append(np.array(a))
# [dataset_copy[km[i][j]] for i in range(self.k) for j in range(len(km[i]))] 有空试试二维列表生成
return result_km
if __name__ == '__main__':
# 定义分类数
init_k = 2
# 初始化对象
k1 = k_Means()
# 初始化分类数
k1.k = init_k
# 获取样例数据
data = k1.getData()
# 初始化质心
centroids = k1.initCentroids(data)
# print("初始化的多个质心为", centroids)
# print(centroids.shape)
# # 观察每个类别初始化的质心
# for i in range(centroids.shape[0]):
# print("第" + str(i + 1) + "个类别的初始质心:", centroids[i])
# print(k1.testDistEclud(centroids[0], centroids[1]))
result = k1.kMeans(data, centroids)
for item in range(len(result)):
print("第" + str(item + 1) + "簇")
print(result[item])
2、FCM
FCM算法融合了模糊理论的精髓,相较于k-means的硬聚类,模糊c提供了更加灵活的聚类结果。因为大部分情况下,数据集中的对象不能划分成为明显分离的类,指派一个对象到一个特定的类有些生硬,也可能会出错。因此FCM对每个对象和每个类赋予一个权值即隶属度,指明对象属于该类的程度。当然,基于概率的方法也可以给出这样的权值,但是有时候我们很难确定一个合适的统计模型,因此使用具有自然地、非概率特性的FCM算法就是一个比较好的选择。该方法由Dunn在1973年提出,并由Bezdek在1981年改进,在模式识别被频繁使用。2 3
- 初始化模糊矩阵
- 更新类中心点
- 更新隶属度
- 得到聚类结果
from pylab import *
from numpy import *
import numpy as np
import operator
import math
import random
from sklearn.datasets import load_iris
import pandas as pd
import os
from collections import defaultdict
def readData(filename):
df_full = pd.read_csv(filename)
# 列出所有列
columns = list(df_full.columns)
# 特征值
features = columns[:len(columns) - 1]
df = df_full[features]
return df
class myFcm(object):
def __init__(self, k=3, MAX_ITER=100, n=2, m=2.00, num_attr=4):
# 分类数
self.k = k
# 最大迭代数
self.MAX_ITER = MAX_ITER
# 样本数
self.n = n
# 模糊参数(加权指数)
self.m = m
# 初始化模糊矩阵U
def initializeMembershipMatrix(self):
'''
步骤 1:用值在 0,1 间的随机数初始化隶属矩阵 U
理解:每个数都有属于三类的概率
'''
membership_mat = list()
for i in range(self.n):
random_num_list = [random.random() for i in range(self.k)]
summation = sum(random_num_list)
temp_list = [x / summation for x in random_num_list] # 首先归一化
membership_mat.append(temp_list)
# print(membership_mat)
return membership_mat
# 计算类中心点
def calculateClusterCenter(self, membership_mat):
'''
步骤 2:用式(6.12)计算 c 个聚类中心 c
return:返回三个维度的中心
'''
cluster_mem_val = zip(*membership_mat)
cluster_centers = list()
# 将n个数的三个聚类隶属度zip为三个列表
cluster_mem_val_list = list(cluster_mem_val)
# print(cluster_mem_val_list)
# 分别对三个类别 的隶属度操作
for j in range(k):
# 分别拿出
x = cluster_mem_val_list[j]
# 做uij的m次方操作
xraised = [e ** m for e in x]
# 分母求和
denominator = sum(xraised)
temp_num = list()
# 取出每一行的数据
for i in range(n):
# 去除某一行的数据
data_point = list(df.iloc[i])
# 6.12 分子——相乘
prod = [xraised[i] * val for val in data_point]
temp_num.append(prod)
# 分子并不是一个数 而是一个向量,向量应该每个维度都进行求和再去和分母做商
numerator = map(sum, zip(*temp_num))
center = [z / denominator for z in numerator] # 每一维都要计算。
cluster_centers.append(center)
# print(cluster_centers)
return cluster_centers
# 更新隶属度
def updateMembershipValue(self, membership_mat, cluster_centers):
# p = float(2/(m-1))
data = []
for i in range(n):
# 取出文件中的每一行数据
x = list(df.iloc[i])
data.append(x)
# operator.sub 减法
# 求每行数据与三个中心点的距离 ord默认是平方和开跟
distances = [np.linalg.norm(list(map(operator.sub, x, cluster_centers[j]))) for j in range(self.k)]
for j in range(self.k):
# 6.13 求最新隶属度
den = sum([math.pow(float(distances[j] / distances[c]), 2 / (self.m - 1)) for c in range(self.k)])
membership_mat[i][j] = float(1 / den)
return membership_mat, data
# 得到聚类结果
def getClusters(self, membership_mat):
cluster_labels = list()
for i in range(self.n):
max_val, idx = max((val, idx) for (idx, val) in enumerate(membership_mat[i]))
cluster_labels.append(idx)
return cluster_labels
def fuzzyCMeansClustering(self):
# 主程序
# 初始化模糊矩阵
membership_mat = self.initializeMembershipMatrix()
# 迭代次数记录
curr = 0
while curr <= MAX_ITER: # 最大迭代次数
# 更新类中心点
cluster_centers = self.calculateClusterCenter(membership_mat)
# 更新隶属度
membership_mat, data = self.updateMembershipValue(membership_mat, cluster_centers)
# 得到聚类结果
cluster_labels = self.getClusters(membership_mat)
curr += 1
return cluster_labels, cluster_centers, data, membership_mat, curr
if __name__ == '__main__':
# 读取数据
filename = "iris.csv"
df = readData(filename)
# 初始化一些参数
# 分类数
k = 3
# 最大迭代数
MAX_ITER = 100
# 样本数
n = len(df) # the number of row
# 模糊参数
m = 2.00
# 维度
num_attr = len(df.columns) - 1
myfcm = myFcm(k=k, MAX_ITER=MAX_ITER, n=n, m=m, num_attr=num_attr)
labels, centers, data, membership, curr = myfcm.fuzzyCMeansClustering()
value_cnt = {}
for lable in labels:
# get(value, num)函数的作用是获取字典中value对应的键值, num=0指示初始值大小。
value_cnt[lable] = value_cnt.get(lable, 0) + 1
print(value_cnt)
print(curr, "次迭代")
mainlist = defaultdict(list)
for k, va in [(v, i) for i, v in enumerate(labels)]:
mainlist[k].append(va)
for i in mainlist:
print("第"+str(i)+"类:"+str(mainlist[i]))
3、代码
GitHub:https://github.com/littlepaike/algorithmLearning.git