• Kmeans特征降维方法



    一、特征降维方法

    1. 提取主要特征,忽略次要特征【PCA降维】
    2. 合并相似特征【特征合并】

    PCA主成分提取其实还是会忽略掉一些信息,有时候感觉聚类后的结果并不理想,所以如下采用特征合并的方式降维。


    二、数据集介绍

    • 用到的数据集:
      各国发展水平统计信息↓
      https://download.csdn.net/download/weixin_43721000/88480791
    • 字段解释:
      country : 国名
      child_mort : 每1000个婴儿的5年死亡率
      exports : 人均商品和服务出口,以人均国内生产总值的百分比给出
      health : 人均卫生支出总额,以人均国内生产总值的百分比给出
      imports : 人均商品和服务进口,以人均国内生产总值的百分比给出
      Income : 人均净收入
      Inflation : 国内生产总值年增长率的测算(通货膨胀率)
      life_expec : 如果按照目前的死亡率模式,新生儿的平均寿命是多少年
      total_fer : 如果目前的年龄生育率保持不变,每个妇女生育的孩子数量
      gdpp : 人均国内生产总值,计算方法是国内生产总值除以总人口
    • 任务类型:
      对所有国家发展水平聚类,确定待援助国家,涵盖算法:K-Means、DBSCAN、Hierarchical

    三、聚类问题及实现方法

    • 该问题主要是根据各国自身特征确定一份待援助国家列表
    • 具体做法是:
      1. 特征标准化
      2. 合并降维
      3. kmeans聚类
      4. 找出待援助国家

    四、代码

    import time
    
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    pd.options.display.float_format = '{:.2f}'.format
    import warnings
    warnings.filterwarnings('ignore')
    
    from sklearn.cluster import KMeans
    from sklearn.metrics import silhouette_score
    from sklearn.preprocessing import MinMaxScaler, StandardScaler
    from mpl_toolkits.mplot3d import Axes3D
    import numpy as np
    import plotly.express as px
    import plotly
    # pip install kaleido   # 绘制世界地图时的依赖包
    
    
    class ShowClusterDistribution(object):
        @classmethod
        def kmeans(cls, df: pd.DataFrame, data_columns: list, class_column: str, centroids: np.array):
            '''
            绘制聚类分布图
            支持2维和3维数据
            :param df:              特征缩放后的训练数据和标签组成的df
            :param data_columns:    训练数据的列名数组
            :param class_column:    标签列名
            :param centroids:       质心点坐标
            :return:
            '''
            # 质心颜色
            centroid_color = ['black']
            # 各个聚类颜色
            clusters_color = ['red', 'green', 'blue', 'orange', 'yellow']
    
            clusters = set(df[class_column].tolist())
    
            dimension = len(data_columns)       # 数据维度
    
            # 二维训练数据
            if dimension == 2:
    
                for idx, cluster in enumerate(clusters):
                    df_class = df[df['Class'] == cluster]
                    x = np.array(df_class[data_columns[0]])
                    y = np.array(df_class[data_columns[1]])
                    plt.scatter(x, y, s=100, c=clusters_color[idx], label=f'Cluster{idx + 1}')
    
                plt.xlabel(data_columns[0])
                plt.ylabel(data_columns[1])
    
                plt.scatter(centroids[:, 0], centroids[:, 1], s=100, c=centroid_color, label='Centroids')
                plt.title('Clusters Distribution')
                plt.legend()
                plt.show()
    
            # 三维训练数据
            elif dimension == 3:
                fig = plt.figure()
                # ax = Axes3D(fig)
                ax = Axes3D(fig, auto_add_to_figure=False)
                fig.add_axes(ax)
                # 质心标记点
                ax.scatter(centroids[:, 0], centroids[:, 1], centroids[:, 2], marker="X", color=centroid_color)
                # 三类数据绘制
                for idx, cluster in enumerate(clusters):
                    df_class = df[df['Class'] == cluster]
                    x = np.array(df_class[data_columns[0]])
                    y = np.array(df_class[data_columns[1]])
                    z = np.array(df_class[data_columns[2]])
                    ax.scatter(x, y, z, c=clusters_color[idx])
    
                plt.title('Clusters Distribution')
                ax.set_xlabel(data_columns[0])
                ax.set_ylabel(data_columns[1])
                ax.set_zlabel(data_columns[2])
                plt.show()
    
    
    def show_elbow_and_silhouette_score(data_values):
        '''
        1.计算Elbow Method
        2.计算Silhouette Score Method
        3.绘图
        :return:
        '''
        sse = {}
        sil = []
        kmax = 10
        fig = plt.subplots(nrows=1, ncols=2, figsize=(15, 5))
    
        # Elbow Method :
        plt.subplot(1, 2, 1)
        for k in range(1, 10):
            kmeans = KMeans(n_clusters=k, max_iter=1000).fit(data_values)
            sse[k] = kmeans.inertia_  # Inertia: Sum of distances of samples to their closest cluster center
        sns.lineplot(x=list(sse.keys()), y=list(sse.values()))
        plt.title('Elbow Method')
        plt.xlabel("k : Number of cluster")
        plt.ylabel("Sum of Squared Error")
        plt.grid()
    
        # Silhouette Score Method
        plt.subplot(1, 2, 2)
        for k in range(2, kmax + 1):
            kmeans = KMeans(n_clusters=k).fit(data_values)
            labels = kmeans.labels_
            sil.append(silhouette_score(data_values, labels, metric='euclidean'))
        sns.lineplot(x=range(2, kmax + 1), y=sil)
        plt.title('Silhouette Score Method')
        plt.xlabel("k : Number of cluster")
        plt.ylabel("Silhouette Score")
        plt.grid()
    
        plt.show()
    
    
    if __name__ == '__main__':
    
    
        # 读取数据
        data = pd.read_csv('./data/Country-data.csv')
        print(data.head())
        #                country  child_mort  exports  ...  life_expec  total_fer   gdpp
        # 0          Afghanistan       90.20    10.00  ...       56.20       5.82    553
        # 1              Albania       16.60    28.00  ...       76.30       1.65   4090
        # 2              Algeria       27.30    38.40  ...       76.50       2.89   4460
        # 3               Angola      119.00    62.30  ...       60.10       6.16   3530
        # 4  Antigua and Barbuda       10.30    45.50  ...       76.80       2.13  12200
    
        # 查看每个特征的分布
        km_columns = ['child_mort', 'health', 'life_expec', 'total_fer', 'imports', 'exports', 'income', 'inflation', 'gdpp']
        fig, ax = plt.subplots(nrows=3, ncols=3, figsize=(10, 5))
        for i in range(len(km_columns)):
            plt.subplot(3, 3, i + 1)
            sns.histplot(data[km_columns[i]], color='#FF781F')
            # title = 'Distribution     : ' + numerical_features[i]
            # plt.title(title)
        plt.show()
        # 【图像见文章结尾处 图01】
        # 数据基本为高斯分布
    
    
        # 数据降维
        # 将较为细分的领域数据合并
        # health <== child_mort, health, life_expec, total_fer
        # trade <== imports, exports
        # finance <== income, inflation, gdpp
        # 最终由9个维度降至3维
        # 相关数据都满足高斯分布,先标准化,再合并数据
        ss = StandardScaler(with_mean=0, with_std=1)
        df = pd.DataFrame()
        data_columns = ['Health', 'Trade', 'Finance']
        class_column = 'Class'
        health_array = ss.fit_transform(data[['child_mort', 'health', 'life_expec', 'total_fer']])
        health_array[:, 0] = -health_array[:, 0]                                # 婴儿死亡率造成负影响
        health_array[:, 3] = -health_array[:, 3]                                # 妇女生育数量造成负影响
        df[data_columns[0]] = health_array.sum(axis=1)
        trade_arry = ss.fit_transform(data[['imports', 'exports']])
        trade_arry[:, 0] = -trade_arry[:, 0]                                    # 进口造成负影响
        df[data_columns[1]] = trade_arry.sum(axis=1)
        finance_arry = ss.fit_transform(data[['income', 'inflation', 'gdpp']])
        finance_arry[:, 1] = -finance_arry[:, 1]                                # 通货膨胀造成负影响
        df[data_columns[2]] = finance_arry.sum(axis=1)
        df.insert(loc=0, value=list(data['country']), column='Country')
        print(df.head())
        #                Country  Health  Trade  Finance
        # 0          Afghanistan   15.21   2.23     1.01
        # 1              Albania   12.50   3.04     1.17
        # 2              Algeria   12.74   2.71     2.44
        # 3               Angola   14.86   4.06     2.63
        # 4  Antigua and Barbuda   12.53   4.11     1.80
    
        # 数据归一化
        mms = MinMaxScaler()  # Normalization
        for data_column in data_columns:
            df[data_column] = mms.fit_transform(df[[data_column]])
        print(df.head())
        #                Country  Health  Trade  Finance
        # 0          Afghanistan    0.57   0.15     0.07
        # 1              Albania    0.21   0.21     0.09
        # 2              Algeria    0.24   0.19     0.21
        # 3               Angola    0.52   0.28     0.22
        # 4  Antigua and Barbuda    0.21   0.28     0.15
    
        # 取出归一化之后的各项特征张量
        data_values = df.drop(columns=['Country']).values  # Feature Combination : Health - Trade - Finance
        print(data_values)
        # [[0.6257404  0.13961443 0.07981958]
        #  [0.12745148 0.19990106 0.08875623]
        #  [0.18248518 0.18662177 0.2128085 ]
        #  [0.66138147 0.28305774 0.23694587]
        #      ...       ...        ...
        #  [0.17006974 0.40338563 0.12143593]
        #  [0.39745068 0.17024776 0.22963179]
        #  [0.52690852 0.18140481 0.13499709]]
    
        # 聚类并绘制 elbow 和 silhouette_score 方法的图像
        show_elbow_and_silhouette_score(data_values)
        # 【图像见文章结尾处 图02】
    
        # 聚类为2时较为合适
        # 训练
        model = KMeans(n_clusters=4, max_iter=1000)
        model.fit(data_values)
        cluster = model.cluster_centers_
        centroids = np.array(cluster)
        labels = model.labels_
        df[class_column] = labels
    
        # 绘制聚类结果
        ShowClusterDistribution.kmeans(df=df, data_columns=data_columns, class_column=class_column, centroids=centroids)
        # 【图像见文章结尾处 图03】
    
        # 绘制世界地图
        df['Class'].loc[df['Class'] == 0] = 'Class 1'
        df['Class'].loc[df['Class'] == 1] = 'Class 2'
        df['Class'].loc[df['Class'] == 2] = 'Class 3'
        df['Class'].loc[df['Class'] == 3] = 'Class 4'
    
        fig = px.choropleth(df[['Country', 'Class']],
                            locationmode='country names',
                            locations='Country',
                            title='Needed Help Per Country (World)',
                            color=df['Class'],
                            color_discrete_map={
                                    'Class 1': 'Red',
                                    'Class 2': 'Green',
                                    'Class 3': 'Yellow',
                                    'Class 4': 'Blue'
                                }
                            )
        fig.update_geos(fitbounds="locations", visible=True)
        fig.update_layout(legend_title_text='Labels', legend_title_side='top', title_pad_l=260, title_y=0.86)
        fig.show(engine='kaleido')
        plotly.offline.plot(fig)
        # 【图像见文章结尾处 图04】
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    图01,特征分布图
    图02,Elbow Method 显示 K 值为 2、3、4、5 都可以,Silhouette Score Method 显示 K 值为 2、4、7、10 较合适,综合一下可以选择 2、4,但是因为聚类为 2 的话会产生较多的待援助国家,因此最终选择 K=4
    图03,聚类结果
    图03,聚类结果
    图04,地图分布映射,通过地图映射和数据特征基本可以确定待援助优先级为
    红色>绿色>蓝色>黄色
  • 相关阅读:
    音视频入门基础:AAC专题(6)——FFmpeg源码中解码ADTS格式的AAC的Header的实现
    C/C++内存管理
    基础:BS(Browser/Server)、CS(Client/Server)架构
    数据集 | 基于计算机视觉的医学影像处理数据集
    蓝牙mesh系统开发三 Ble Mesh 配网器 Provisioner
    Java SE 13 新增特性
    通过Nginx Ingress实现灰度发布和蓝绿发布
    java算法学习索引之二叉树问题
    回顾本轮行情,聊聊我的区块链投资逻辑 2021-04-25
    参数估计——《概率论及其数理统计》第七章学习报告(点估计)
  • 原文地址:https://blog.csdn.net/weixin_43721000/article/details/134244536