基于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
相关推荐
大二转专业1 小时前
408算法题leetcode--第24天
考研·算法·leetcode
凭栏落花侧1 小时前
决策树:简单易懂的预测模型
人工智能·算法·决策树·机器学习·信息可视化·数据挖掘·数据分析
hong_zc2 小时前
算法【Java】—— 二叉树的深搜
java·算法
吱吱鼠叔3 小时前
MATLAB计算与建模常见函数:5.曲线拟合
算法·机器学习·matlab
嵌入式AI的盲4 小时前
数组指针和指针数组
数据结构·算法
Indigo_code6 小时前
【数据结构】【顺序表算法】 删除特定值
数据结构·算法
阿史大杯茶7 小时前
Codeforces Round 976 (Div. 2 ABCDE题)视频讲解
数据结构·c++·算法
LluckyYH7 小时前
代码随想录Day 58|拓扑排序、dijkstra算法精讲,题目:软件构建、参加科学大会
算法·深度优先·动态规划·软件构建·图论·dfs
转调8 小时前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
不穿格子衬衫8 小时前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序