基于K-prototype算法聚类

k-prototype聚类是一种用于混合数据类型聚类的算法,由Jain和Dubes在1988年提出。它主要用于同时包含连续属性和离散属性的数据集。k-prototype算法可以看作是k-means算法的扩展,它将k-means算法的思想应用于混合数据类型,通过为连续属性和离散属性分别定义距离函数来处理这两种不同类型的数据。

k-prototype算法的基本步骤如下:

第一步:从数据集X中随机选择k个数据对象作为初始的聚类中心;

第二步:根据混合距离的相异度测算公式计算每个点到各聚类中心的距离,将其划分到距离最近的类中,每一次划分结束后,更新聚类中心。

第三步:当数据集中所有的对象都分配到相应的类后,重新计算这些数据对象到当前聚类中心的混合距离,然后更新聚类中心。

第四步:重复步骤三,直到经过新一轮计算之后聚类中心不再发生大的变化为止。

代码实现步骤如下:

python 复制代码
from numba import jit
import pandas as pd
import numpy as np
import random
from collections import Counter

## 定义数值型变量的距离(欧式距离)
def dist(x, y):
    return np.sqrt(sum((x-y)**2))
## 计算分类变量的距离(海明威距离)
def sigma(x, y):
    return len(x) - sum(x == y)

## 区分数值变量和分类变量,并随机生成聚类中心
def findprotos(data, k):
    #data = df
    m, n = data.shape
    # 生成聚类中心的行号
    num = random.sample(range(m), k)
    O = []
    C = []
    for i in range(n):
        try:
            if isinstance(data.iloc[0, i], int) or isinstance(data.iloc[0, i], float) or isinstance(data.iloc[0, i], np.int64):
                O.append(i)
            elif isinstance(data.iloc[0, i], str):
                C.append(i)
            else:
                raise ValueError("the %d column of data is not a number or a string column" % i)
        except TypeError as e:
            print(e)
    # 数值型变量
    O_data = data.iloc[:, O]
    # 分类型变量
    C_data = data.iloc[:, C]
    # 随机数值型数据(聚类中心)
    O_protos = O_data.iloc[num, :]
    # 随机分类型数据(聚类中心)
    C_protos = C_data.iloc[num, :]
    return O, C, O_data, C_data, O_protos, C_protos
## data: 待聚类的数据
## k: 类别数
## max_iters: 最大迭代次数
## 
#gamma = 1
#k = 3
#data = pd.DataFrame(df)

def KPrototypes(data, k, max_iters  ,  gamma ):
    # m: 数据的行数,n:数据的列数
    m, n = data.shape
    # O: 数值型变量的列号;   C:分类型变量的列号
    # O_data: 数值型数据;  C_data:分类型数据
    # O_protos:初始聚类中心的数值型数据; C_protos:聚类中心的 
    O, C, O_data, C_data, O_protos, C_protos = findprotos(data, k)
    cluster = None
    # clusterShip: 按行号存储每个样本的聚类类别
    clusterShip = []
    # 每个聚类类别的样本个数
    clusterCount = {}
    
    sumInCluster = {}
    freqInCluster = {}
    for i in range(m):
        mindistance = float('inf')
        # 此处循环每个点和各个聚类中心的关系
        for j in range(k):
            # 对数值型变量计算欧式距离,对分类型变量计算海明威距离
            # 计算每个点到各个聚类中心的距离,把他聚到距离他最近的类中去
            distance = dist(O_data.iloc[i,:], O_protos.iloc[j,:]) + gamma * sigma(C_data.iloc[i,:], C_protos.iloc[j,:])
            if distance < mindistance:
                mindistance = distance
                cluster = j
        clusterShip.append(cluster)
        if  clusterCount.get(cluster) == None:
            clusterCount[cluster] = 1
        else:
            clusterCount[cluster] += 1
        # 此处循环各个列的和,用来更新各个类的中心
        for j in range(len(O)):
            if sumInCluster.get(cluster) == None:
                sumInCluster[cluster] = [O_data.iloc[i,j]] + [0] * (len(O) - 1)
            else:
                sumInCluster[cluster][j] += O_data.iloc[i,j]
            O_protos.iloc[cluster,j] = sumInCluster[cluster][j] / clusterCount[cluster]
        for j in range(len(C)):
            if freqInCluster.get(cluster) == None:
                freqInCluster[cluster] = [Counter(C_data.iloc[i,j])] + [Counter()] * (len(C) - 1)
            else:
                freqInCluster[cluster][j] += Counter(C_data.iloc[i,j])
            # 出现次数最多的那个值,作为聚类中心
            C_protos.iloc[cluster,j] = freqInCluster[cluster][j].most_common()[0][0]
    max_iters = 10
    for t in range(max_iters):
        for i in range(m):
            mindistance = float('inf')
            for j in range(k):
                distance = dist(O_data.iloc[i,:], O_protos.iloc[j,:]) + gamma * sigma(C_data.iloc[i,:], C_protos.iloc[j,:])
                if distance < mindistance:
                    mindistance = distance
                    cluster = j
            # 重新判断某个点属于哪个类,如果不再属于以前的类,则把这个点的类别更新,且更新类的个数的dict
            if clusterShip[i] != cluster:
                oldCluster = clusterShip[i]
                clusterShip[i] = cluster
                clusterCount[cluster] += 1
                clusterCount[oldCluster] -= 1
                # 把这个点的坐标加到新的类别中,把它的值从之前的类中减掉
                for j in range(len(O)):
                    sumInCluster[cluster][j] += O_data.iloc[i,j]
                    sumInCluster[oldCluster][j] -= O_data.iloc[i,j]
                    O_protos.iloc[cluster,j] = sumInCluster[cluster][j] / clusterCount[cluster]
                    O_protos.iloc[oldCluster, j] = sumInCluster[oldCluster][j] / clusterCount[oldCluster]
                # 查找分类变量的聚类中心
                for j in range(len(C)):
                    freqInCluster[cluster][j] += Counter(C_data.iloc[i,j])
                    freqInCluster[oldCluster][j] -= Counter(C_data.iloc[i,j])
                    C_protos.iloc[cluster,j] = freqInCluster[cluster][j].most_common()[0][0]
                    C_protos.iloc[oldCluster,j] = freqInCluster[oldCluster][j].most_common()[0][0]
    return clusterShip , O_protos , C_protos
相关推荐
-$_$-18 分钟前
【LeetCode 面试经典150题】详细题解之滑动窗口篇
算法·leetcode·面试
Channing Lewis22 分钟前
算法工程化工程师
算法
帅逼码农1 小时前
有限域、伽罗瓦域、扩域、素域、代数扩张、分裂域概念解释
算法·有限域·伽罗瓦域
Jayen H1 小时前
【优选算法】盛最多水的容器
算法
机跃1 小时前
递归算法常见问题(Java)
java·开发语言·算法
<但凡.2 小时前
题海拾贝:蓝桥杯 2020 省AB 乘法表
c++·算法·蓝桥杯
pzx_0012 小时前
【LeetCode】94.二叉树的中序遍历
算法·leetcode·职场和发展
我曾经是个程序员2 小时前
使用C#生成一张1G大小的空白图片
java·算法·c#
芒果de香蕉皮2 小时前
mavlink移植到单片机stm32f103c8t6,实现接收和发送数据
stm32·单片机·嵌入式硬件·算法·无人机
徐子童3 小时前
二分查找算法专题
数据结构·算法