【Python】聚类算法实现-性能度量

机器学习 Python实验


实验内容

一、编程实现变量:a, b, c, d

相关定义:
a,b,c,d相关定义代码部分:

def abcd(set1, set2):
    """
    找出变量a,b,c,d
    :param set1:簇标识集合1
    :param set2:簇标识集合2
    :return:返回a,b,c,d值
    """
    a, b, c, d, = 0, 0, 0, 0
    i = 0
    while i < len(set1):
        j = i + 1
        while j < len(set1):
            if set1[i] == set1[j] and set2[i] == set2[j]:
                a += 1
            elif set1[i] == set1[j] and set2[i] != set2[j]:
                b += 1
            elif set1[i] != set1[j] and set2[i] == set2[j]:
                c += 1
            elif set1[i] != set1[j] and set2[i] != set2[j]:
                d += 1
            j += 1
        i += 1

    return a, b, c, d

二、Jaccard系数(Jaccard Coefficient, JC)

Jaccard系数公式:
Jaccard系数公式
代码部分:

def jaccard(a, b, c):
    """
    Jaccard系数

    """

    jc = a / (a + b + c)
    return jc

三、常用距离

闵可夫斯基距离(Minkowski distance):
在这里插入图片描述
p=2: 欧氏距离(Euclidean distance).
p=1:曼哈顿距离(Manhattan distance).

代码部分:

def minkowski_distance(vec1, vec2, p):
    """
    闵可夫斯基距离
    :param vec1:样本 1
    :param vec2:样本 2
    :param p:p=1:曼哈顿距离;p=2: 欧氏距离
    :return:返回所求距离值
    """
    vec = []
    i = 0;
    sum = 0
    while i < len(vec1):
        # vec[i] = abs(vec1[i] - vec2[i]) ** p
        sum += abs(vec1[i] - vec2[i]) ** p
        i += 1
    if p == 1:
        return sum
    elif p == 2:
        return sum ** 0.5

四、聚类性能度量

在这里插入图片描述代码部分:

  • 平均距离
def avg_dist(c):
    """
    簇C内样本间的平均距离
    :param c:簇C
    :return:样本间的平均距离
    """
    i = 0
    sum = 0
    while i < len(c):
        j = i
        while j < len(c):
            sum += minkowski_distance(c[i], c[j], 2)
            j += 1
        i += 1
    avg_ = (2 / len(c) * (len(c) - 1)) * sum
    return avg_
  • 簇C内样本间的最远距离
def max_dist(c):
    """
    簇C内样本间的最远距离
    :param c: 簇C
    :return: 簇间样本最远距离
    """
    i = 0
    max_ = 0
    while i < len(c):
        j = i
        while j < len(c):
            temp = minkowski_distance(c[i], c[j], 2)
            if temp > max_:
                max_ = temp
            j += 1
        i += 1
    return max_
  • 簇c1与c2最近样本间的距离
def min_dist(c1, c2):
    """
    簇c1与c2最近样本间的距离
    :param c1:簇c1
    :param c2:簇c2
    :return:簇c1与c2最近样本间的距离
    """
    i, j = 0, 0
    min_ = 10  # 取一个较大数值避免初始值过小无法更新到最小距离的新数据
    while i < len(c1):
        while j < len(c2):
            temp = minkowski_distance(c1[i], c2[j], 2)
            if temp < min_:
                min_ = temp
            j += 1
        i += 1
    return min_
  • 两个向量相加
def sum_vec(vec1, vec2):
    """
    两个向量相加
    :param vec1: 向量1
    :param vec2: 向量2
    :return: 返回向量
    """
    vec = []
    i = 0
    while i < len(vec1):
        temp = round(vec1[i] + vec2[i], 2)
        vec.append(temp)
        i += 1
    return vec
  • 簇C1和簇C2两个簇的中心点的距离
def cen_vec(c):
    """
    簇C的中心点
    :param c: 簇C
    :return: 返回中心点的向量
    """
    # u = c[0]
    i = 0
    temp = [0, 0, 0, 0]
    while i < len(c):
        temp = sum_vec(c[i], temp)
        i += 1
    for i in range(len(temp)):
        t = temp[i] / len(c)
        temp[i] = t
    u = temp
    return u


def cen_dist(u1, u2):
    """
    簇C1和簇C2两个簇的中心点的距离
    :param u1: 簇C1的中心点
    :param u2: 簇C2的中心点
    :return: 返回距离
    """
    return minkowski_distance(u1, u2, 2)

五、DB指数(Davies-Bouldin Index, DBI)

公式:

在这里插入图片描述代码部分:

def DaviesBouldin(X, labels):
    """
    计算DB指数
    :param X: 数据集
    :param labels: 聚类标签
    :return: 返回DBI
    """
    n_cluster = len(np.bincount(labels))  # 簇的个数
    cluster_k = [X[labels == k] for k in range(n_cluster)]  # 对应簇的数据集
    centroids = [np.mean(k, axis=0) for k in cluster_k]  # 按照标签列,已经分好了集群,那么每个集群的数据的平均值就是质心所在的位置
    # 求S 簇类中数据到质心欧氏距离和的平均距离
    S = [np.mean([euclidean(p, centroids[i]) for p in k]) for i, k in enumerate(cluster_k)]
    Ri = []
    for i in range(n_cluster):
        Rij = []
        # 计算Rij
        for j in range(n_cluster):
            if j != i:
                r = (S[i] + S[j]) / euclidean(centroids[i], centroids[j])
                Rij.append(r)
        # 求Ri
        Ri.append(max(Rij))
    # 求dbi
    dbi = np.mean(Ri)
    return dbi

六、Value Difference Metric, VDM(处理无序属性):

在这里插入图片描述代码部分:

def vdm(X, labels, u, a, b, p=2):
    """
    处理无序属性
    :param X:数据集
    :param labels:簇标签
    :param p:默认为2
    :param u:属性u的位置
    :param a:在属性u的取值a
    :param b:在属性u的取值b
    :return:返回vdm
    """
    u = u - 1
    n_cluster = len(np.bincount(labels))  # 簇的个数
    cluster_k = [X[labels == k] for k in range(n_cluster)]  # 对应簇的数据集
    ma, mb, = 0, 0
    for i in range(len(X)):

        if X[i][u] == a:
            ma += 1
        if X[i][u] == b:
            mb += 1
    # print("\n数据集在属性u上对a,b取值上的样本数:", ma, mb)
    v = []
    for i in range(n_cluster):
        mai = 0
        mbi = 0
        j = 0
        while j < len(cluster_k[i]):
            if cluster_k[i][j][u] == a:
                mai += 1
            if cluster_k[i][j][u] == b:
                mbi += 1
            j += 1
        v.append(abs(mai / ma - mbi / mb) ** p)
        # print("在簇标签 ", i, "的簇中在属性u上对a,b取值的样本数:", mai, mbi)
    vdm = 0
    for k in range(len(v)):
        vdm += v[k]
    return vdm

七、MinkovDMp(处理混合属性)

公式:
在这里插入图片描述代码部分:

def MinkovDM(X, labels, x1, x2, nc, seq, p):
    """
    处理混合属性
    :param X: 数据集
    :param labels: 划分好的簇标签
    :param x1: 样本1
    :param x2: 样本2
    :param nc: 连续属性和离散属性的分界
    :param seq:seq为1或2,表示先离散后连续或反之
    :param p:距离选择,默认欧式距离
    :return:
    """
    sum1, sum2 = 0, 0
    if seq == 1:
        u = 0
        while u < nc:
            sum1 = sum1 + vdm(X, labels, u + 1, x1[u], x2[u], p)
            u += 1
        while u < len(x1):
            sum2 = sum2 + abs(x1[u] - x2[u]) ** p
            u += 1
    elif seq == 2:
        u = 0
        while u < nc:
            sum2 = sum2 + abs(x1[u] - x2[u]) ** p
            u += 1
        while u < len(x1):
            sum1 = sum1 + vdm(X, labels, u + 1, x1[u], x2[u], p)
            u += 1
    mvdm = (sum1 + sum2) ** (1/p)
    return mvdm

八、归一化

在这里插入图片描述
代码部分:

def autoNorm(dataSet):
    """
    归一化
    :param dataSet: 数据集
    :return:
    """
    minVals = dataSet.min(0)  # 取每一列的最小值
    maxVals = dataSet.max(0)  # 取每一列的最大值
    ranges = maxVals - minVals
    normDataSet = np.zeros(np.shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet / np.tile(ranges, (m, 1))
    return normDataSet, ranges, minVals

验证

变量a,b,c,d

先设定划分好的簇标识集合
label1 = [0, 0, 0, 1, 1, 1]  # 划分好的簇标识集合1
label2 = [1, 1, 3, 3, 5, 5]  # 划分好的簇标识集合2

运行

 a, b, c, d = abcd(label1, label2)

结果:

变量a,b,c,d:  2 4 1 8

Jaccard系数

根据上述获得的结果,运行函数

print("jaccard系数:", jaccard(a, b, c))

结果:

jaccard系数: 0.2857142857142857

常用距离

先设定好两个样本:

 x1 = [0.2, 0.25, 0.58, 0.48]  # 样本1
 x2 = [0.3, 0.45, 0.28, 0.18]  # 样本2

调用函数:

print("\n欧氏距离:", minkowski_distance(x1, x2, 1))
print("曼哈顿距离:", minkowski_distance(x1, x2, 2))

结果:

欧氏距离: 0.8999999999999999
曼哈顿距离: 0.4795831523312719

性能度量

假定划分好了两个簇

C1 = [[0.2, 0.25, 0.58, 0.48], [0.3, 0.64, 0.28, 0.18],[0.18, 0.45, 0.38, 0.18],[0.15, 0.55, 0.40, 0.30]]
C2 = [[0.6, 0.78, 0.98, 0.76], [0.55, 0.75, 0.68, 0.70],[0.77, 0.65, 0.58, 0.48],[0.77, 0.55, 0.40, 0.87]]

调用相关函数:

print("簇C内样本间的平均距离: ",avg_dist(C1))
print("簇C内样本间的最远距离: ",max_dist(C1))
print("簇C1和簇C2样本间最近距离: ", min_dist(C1, C2))
print("簇C1的中心点: ",cen_vec(C1))
print("簇C2的中心点: ",cen_vec(C2))
print("簇C1和簇C2的中心点间的距离: ",cen_dist(cen_vec(C1),cen_vec(C2)))

结果:


簇C内样本间的平均距离:  3.066449950245757
簇C内样本间的最远距离:  0.5848931526355903
簇C1和簇C2样本间最近距离:  0.6564297372910524

簇C1的中心点:  [0.2075, 0.4725, 0.41, 0.285]
簇C2的中心点:  [0.6725, 0.6825, 0.66, 0.7025]

簇C1和簇C2的中心点间的距离:  0.7050753505831842

采用iris数据集验证

①导入数据集

    # 使用iris数据集验证
    iris = load_iris()
    # X = iris.data[:]  # #表示我们取特征空间中的四个维度
    # X = iris.data[:, 2:]  ##表示我们只取特征空间中的后两个维度
    X = iris.data[:, 2:]  ##表示我们只取特征空间中的前两个维度
    # print(X)

②聚类

	estimator = KMeans(n_clusters=3)  # 构造聚类器
    estimator.fit(X)  # 聚类
    label_pred = estimator.labels_  # 获取聚类标签
    labels = list(set(label_pred))

③输出对应簇的质心,簇中数据到质心的欧式距离,DB指数

n_cluster = len(np.bincount(label_pred))  # 质心的数量(簇的个数)
    cluster_k = [X[label_pred == k] for k in range(n_cluster)]  # 标签对应的数据
    centroids = [np.mean(k, axis=0) for k in cluster_k]  # 质心所在的位置
    S = [np.mean([euclidean(p, centroids[i]) for p in k]) for i, k in enumerate(cluster_k)]  # 簇中数据到质心的距离
    for k in range(len(labels)):
        print("\n簇标签", k)
        print("内置函数计算出来的质心:", centroids[k])
        print("自编函数计算的质心:", cen_vec(cluster_k[k]))
        print("簇中数据到质心的欧式距离:", S[k])
    print("\nDB 指数:", DaviesBouldin(X, label_pred))

④聚类结果:

簇标签 0
内置函数计算出来的质心: [1.462 0.246]
自编函数计算的质心: [1.462, 0.24600000000000002]
簇中数据到质心的欧式距离: 0.16809670151957523

簇标签 1
内置函数计算出来的质心: [5.59583333 2.0375    ]
自编函数计算的质心: [5.595833333333334, 2.0375]
簇中数据到质心的欧式距离: 0.5186381094063833

簇标签 2
内置函数计算出来的质心: [4.26923077 1.34230769]
自编函数计算的质心: [4.269230769230769, 1.3423076923076922]
簇中数据到质心的欧式距离: 0.42337511774304964

DB 指数: 0.484729922604757

数据分布图聚类结果

处理无序属性

我们假设iris数据集属性是无序的
运行代码:
表示在iris数据集上对第2个属性上两个离散值0.2 与 1.4之间的VDM距离

print("\nvdm:", vdm(X, label_pred, 2, 0.2, 1.4, 2))

结果:

vdm: 1.78125

处理混合属性

我们假设出两个样本,第一个属性取值是连续的,第二个是离散的,假定他们属于划分后的iris数据集

	x3 = [1.4, 0.2]
    x4 = [1.5, 1.4]

运行代码:

  print("\nMinkovDM:",MinkovDM(X,label_pred,x3,x4,1,2,2))

结果:

MinkovDM: 1.3383758814324174

归一化

运行代码:

    group = X
    print("\n原数据:\n", X)
    newgroup, _, _ = autoNorm(group)
    print("\n归一化后的数据:\n", newgroup)

结果:

 原数据:
 [[1.4 0.2]
 [1.4 0.2]
 [1.3 0.2]
 [1.5 0.2]
 [1.4 0.2]
 [1.7 0.4]
 [1.4 0.3]
 [1.5 0.2]
 [1.4 0.2]
 [1.5 0.1]
 [1.5 0.2]
 [1.6 0.2]
 [1.4 0.1]
 [1.1 0.1]
 归一化后的数据:
 [[0.06779661 0.04166667]
 [0.06779661 0.04166667]
 [0.05084746 0.04166667]
 [0.08474576 0.04166667]
 [0.06779661 0.04166667]
 [0.11864407 0.125     ]
 [0.06779661 0.08333333]
 [0.08474576 0.04166667]
 [0.06779661 0.04166667]
 [0.08474576 0.        ]
 [0.08474576 0.04166667]
 [0.10169492 0.04166667]
 [0.06779661 0.        ]
 [0.01694915 0.        ]
 [0.03389831 0.04166667]
 [0.08474576 0.125     ]

总结

提示:这里对文章进行总结:

本次实验一部分为自己编写函数,一部分使用了内置函数,使用数据集验证时选取iris数据集可能会有不当,函数还有待优化。

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值