• [机器学习笔记]K-means聚类——进行广告效果分析


    本文是我学习机器学习的一篇学习笔记,
    主要参考文章:运用K-Means聚类分析广告效果 感谢哈~
    本人学习过程中遇到一些问题,且每个人的理解和代码习惯不同,故在此重新梳理了数据分析的主要流程,并对代码重新做了整理。特在此做下记录。


    前言

    某企业,其主要的宣传获客手段是通过各个广告渠道投放广告,通过广告将用户引流至自己的企业官网。用户可以在该企业官网浏览及购买其产品及服务。
    但是广告的渠道非常多,哪些渠道效果好,哪些效果不好,需要对广告效果进行分析,针对性地做广告效果测量和优化工作。
    现有一份各类广告渠道90天内日均UV(独立访问量)、平均注册率、平均搜索率、访问深度、平均停留时长、订单转化率、投放时间、素材类型、广告类型、合作方式、广告尺寸和广告卖点等特征。
    在这里插入图片描述

    由于该企业广告渠道众多,我们希望将众多渠道按照重点特征分类,此时就可以使用聚类算法进行分类处理。请你通过聚类算法,将渠道分类,找出每类渠道的重点特征,为业务讨论和数据分析提供支持。
    字段说明如下:
    在这里插入图片描述
    在这里插入图片描述


    一、引用类库

    本案例主要使用Pandas、NumPy、Matplotlib、SKlearn模块进行数据分析。

    #################1、引用类库#################
    import numpy as np
    import pandas as pd
    pd.set_option('display.max_columns', None)                       # 显示所有列
    pd.set_option('display.unicode.ambiguous_as_wide', True)         # 设置各列对齐
    pd.set_option('display.unicode.east_asian_width', True)         
    pd.set_option('display.width', 180)                              # 设置打印宽度
    import matplotlib as mpl                                        # 可视化库
    mpl.rcParams['font.sans-serif'] = [u'SimHei']                    # 防止图像出现中文乱码
    mpl.rcParams['axes.unicode_minus'] = False
    import matplotlib.pyplot as plt
    import seaborn as sns
    from sklearn.preprocessing import LabelEncoder
    from sklearn.preprocessing import MinMaxScaler                  # 数据归一工具
    import warnings
    warnings.filterwarnings('ignore')                                # 忽视警告
    from sklearn.cluster import KMeans                              # KMeans库
    from sklearn.metrics import silhouette_score                    # 导入轮廓系数指标
    print("类库引用成功!")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果:
    在这里插入图片描述

    二、读入数据

    这里我们读入数据

    df = pd.read_csv('./ad_performance.csv')
    df.head()
    
    • 1
    • 2

    在这里插入图片描述

    三、数据描述性统计

    3.1 查看原始数据

    print("\n查看原始数据")
    df.head(2)  # 打印输出前2条数据
    
    • 1
    • 2

    在这里插入图片描述

    3.2 打印原始数据摘要

    print("\n打印原始数据摘要")
    df.info()
    
    • 1
    • 2

    在这里插入图片描述

    3.3 打印原始数据基本描述性信息

    print("\n打印原始数据基本描述性信息")
    df.describe().round(2).T  # 打印数据类型分布
    
    • 1
    • 2

    在这里插入图片描述

    3.4 缺失值审查

    print("\n缺失值审查")
    df.isnull().any(axis=0) # 查看每一列是否具有缺失值
    
    • 1
    • 2

    在这里插入图片描述
    【平均停留时间】存在缺失值。

    3.5 查看具有缺失值的行数

    print("\n查看具有缺失值的行数")
    df.isnull().sum().sort_values(ascending=False)
    
    • 1
    • 2

    在这里插入图片描述

    3.6 各列间的相关性分析

    print("\n各列间的相关性分析:")
    df2 = df.drop(['Unnamed: 0'], axis=1)
    corr = df2.corr().round(2)
    print(corr.T) # 打印原始数据相关性信息
    print("\n打印热力图,相关性可视化展示")
    sns.heatmap(corr,cmap='Reds',annot = True)
    plt.show()
    print("热力图打印完毕")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    可以看到【平均停留时间】和【访问深度】相关性为0.72,可以删掉一列。其中【平均停留时间】与其他各列的相关性之和相比于【访问深度】更高,故我们可以删掉【平均停留时间】这一列。而刚好【平均停留时间】也存在2行缺失值。

    四、数据预处理

    4.1 删除相关性较强的列

    print("\n删除相关性较强的列")
    df2 = df.drop(['平均停留时间'],axis=1)
    df2.head()
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    4.2 对字符型列进行独热编码

    print("接下来我们字符型列进行独热编码\n")
    print("查看字符型列取值")
    cols_str_type = ["素材类型","广告类型","合作方式","广告尺寸","广告卖点"]
    for x in cols_str_type:
        data=df2[x].unique()
        print("列【{0}】的取值有:\n{1}".format(x,data))
        print("-·"*20)
    print("\n字符串分类独热编码处理")
    ohe_matrix = OneHotEncoder(sparse=False).fit_transform(df2[cols_str_type])  # 建立OneHotEncode对象
    pd.DataFrame(ohe_matrix[:2]).head()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    4.3 数据归一化

    print("\n数据归一化")
    df2.info()
    cols_num_type = ['日均UV','平均注册率','平均搜索量','访问深度','订单转化率','投放总时间'] # 数值型列
    sacle_matrix = df2.loc[:, cols_num_type]  # 获得要转换的矩阵
    data_scaled = MinMaxScaler().fit_transform(sacle_matrix)  # MinMaxScaler标准化处理
    pd.DataFrame(data_scaled.round(2)).head()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    4.4 合并独热编码和归一化的列

    print("\n合并所有维度,得到新矩阵:X")
    X = np.hstack((data_scaled, ohe_matrix))
    pd.DataFrame(X.round(2)).head()
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    数据处理完,就可以带入模型进行训练了。

    五、建模调优

    本步骤我们对上一步数据预处理后的数据进行建模,并找出最优k值(即判断:聚类成几个类别最合适)。

    # 通过平均轮廓系数检验得到最佳KMeans聚类模型
    score_list = list()  # 用来存储每个K下模型的平局轮廓系数
    silhouette_int = -1  # 初始化的平均轮廓系数阀值
    for n_clusters in range(2, 8):  # 遍历从2到8几个有限组
        model_kmeans = KMeans(n_clusters=n_clusters)  # 建立聚类模型对象
        labels_tmp = model_kmeans.fit_predict(X)  # 训练聚类模型
        silhouette_tmp = silhouette_score(X, labels_tmp)  # 得到每个K下的平均轮廓系数
        if silhouette_tmp > silhouette_int:  # 如果平均轮廓系数更高
            best_k = n_clusters  # 保存K将最好的K存储下来
            silhouette_int = silhouette_tmp  # 保存平均轮廓得分
            best_kmeans = model_kmeans  # 保存模型实例对象
            cluster_labels_k = labels_tmp  # 保存聚类标签
        score_list.append([n_clusters, silhouette_tmp])  # 将每次K及其得分追加到列表
    print('\n{:*^60}'.format('K值对应的轮廓系数:'))
    print(np.array(score_list))  # 打印输出所有K下的详细得分
    print('最优的K值是:{0} \n对应的轮廓系数是:{1}'.format(best_k, silhouette_int))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    六、聚类结果分析

    6.1 将原始数据与聚类标签整合

    这一步我们将原始数据 df 和 聚类标签列 clusters 合并到一起,相当于为每一行数据打上聚类标签。

    print("\n将原始数据与聚类标签整合:")
    cluster_labels = pd.DataFrame(cluster_labels_k, columns=['clusters'])  # 获得训练集下的标签信息
    merge_data = pd.concat((df, cluster_labels), axis=1)  # 将原始处理过的数据跟聚类标签整合
    merge_data.head()
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    6.2 查看每个聚类类别下的样本量和样本占比

    print("\n查看每个聚类类别下的样本量和样本占比:")
    column_0 = merge_data.columns[0]
    clustering_count = pd.DataFrame(merge_data[column_0].groupby(merge_data['clusters']).count()).T.rename({column_0: 'counts'})  # 计算每个聚类类别的样本量
    clustering_ratio = (clustering_count / len(merge_data)).round(2).rename({'counts': 'percentage'})  # 计算每个聚类类别的样本量占比
    print(clustering_count)
    print("#"*30)
    print(clustering_ratio)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    所有的渠道被分为4各类别,
    每个类别的样本量分别为:73、349、313、154
    对应占比分别为:8%、39%、35%、17%

    6.3 计算各个聚类类别内部最显著特征值

    print("\n计算各个聚类类别内部最显著特征值:")
    
    cluster_features = []  # 空列表,用于存储最终合并后的所有特征信息
    for line in range(best_k):  # 读取每个类索引
        label_data = merge_data[merge_data['clusters'] == line]  # 获得特定聚类类别的数据
        part1_data = label_data.loc[:, cols_num_type] # 获得数值型数据特征
        part1_desc = part1_data.describe().round(3)  # 得到数值型特征的描述性统计信息
        merge_data1 = part1_desc.iloc[2, :]  # 得到数值型特征的均值
        part2_data = label_data.loc[:, cols_str_type]  # 获得字符串型数据特征
        part2_desc = part2_data.describe(include='all')  # 获得字符串型数据特征的描述性统计信息
        merge_data2 = part2_desc.iloc[2, :]  # 获得字符串型数据特征的最频繁值
        merge_line = pd.concat((merge_data1, merge_data2), axis=0)  # 将数值型和字符串型典型特征沿行合并
        cluster_features.append(merge_line)  # 将每个类别下的数据特征追加到列表
    #  输出完整的类别特征信息
    cluster_pd = pd.DataFrame(cluster_features).T  # 将列表转化为矩阵
    print('\n{:*^60}'.format('每个类别主要的特征:'))
    all_cluster_set = pd.concat((clustering_count, clustering_ratio, cluster_pd),axis=0)  # 将每个聚类类别的所有信息合并
    all_cluster_set
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    6.4 绘制雷达图,以分析各聚类类别的特征

    print("\n图形化输出:")
    print("\n各类别数据预处理")
    num_sets = cluster_pd.loc[cols_num_type, :].T.astype(np.float64)  # 获取要展示的数据(数值型列)
    num_sets_max_min = MinMaxScaler().fit_transform(num_sets)  # 获得标准化后的数据
    print("num_sets:",pd.DataFrame(num_sets))
    print("num_sets_max_min:", pd.DataFrame(num_sets_max_min.round(2)))
    
    # 画图
    colors=list(mcolors.TABLEAU_COLORS.keys()) # matplotlib 绘图颜色自动选择
    fig = plt.figure(figsize=(6,6))  # 建立画布
    ax = fig.add_subplot(111, polar=True)  # 增加子网格,注意polar参数
    labels = np.array(merge_data1.index)  # 设置要展示的数据标签
    angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False)  # 计算各个区间的角度
    angles1 = angles;
    angles = np.concatenate((angles, [angles[0]]))  # 建立相同首尾字段以便于闭合
    # 画雷达图
    for i in range(len(num_sets)):  # 循环每个类别
        data_tmp = num_sets_max_min[i, :]  # 获得对应类数据
        data = np.concatenate((data_tmp, [data_tmp[0]]))  # 建立相同首尾字段以便于闭合
        ax.plot(angles, data, 'o-', c=colors[i], label="第%d聚类簇"%(i))  # 画线
        ax.fill(angles, data,alpha=0.5)
    # 设置图像显示格式
    ax.set_thetagrids(angles1 * 180 / np.pi, labels, fontproperties="Microsoft YaHei")  # 设置极坐标轴
    ax.set_title("各聚类类别显著特征对比", fontproperties="Microsoft YaHei")  # 设置标题放置
    ax.set_rlim(-0.2, 1.2)  # 设置坐标轴尺度范围
    plt.legend(loc="upper right" ,bbox_to_anchor=(1.2,1.0))  # 设置图例位置
    plt.show()
    
    • 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

    在这里插入图片描述
    聚类0:
    这类渠道各方面特征都不明显,各个流量质量和流量数量的指标均处于“中等”层次。不突出但是均衡,可以考虑在各场景下在这个渠道投放广告。

    聚类1:
    这类广告媒体除了访问深度和投放时间较高,其他属性较低,因此这类广告媒体效果质量较差,并且占到39%,因此这类是主题渠道之一。业务部门要考虑他的实际投放价值。

    聚类2:
    这类广告媒体除了访问深度略差,在平均搜索量、日均UV、订单转化率等广告效果指标上表现良好,是一类综合效果较好的渠道。但是日均UV是短板,较低。无法给企业带来大量的流量以及新用户,这类广告的特质适合用户转化,尤其是有关订单的转化提升。

    聚类3:
    这类广告媒体的显著特征是日均UV和注册率较高,其“引流”和“拉新”效果好,可以在广告媒体中定位为引流角色。符合“广而告之”的诉求,适合“拉新”使用。

  • 相关阅读:
    计算机网络学习实践:模拟静态路由
    Cerebral Cortex:从任务态和静息态脑功能连接预测儿童数学技能
    跨域的十种解决方案详解(总结)
    JS Array 操作方法合集
    BioVendor人俱乐部细胞蛋白(CC16)Elisa试剂盒研究领域
    ESP8266-Arduino编程实例-74HC595位移寄存驱动
    *CTF babyarm内核题目分析
    虚拟机的发展史:从分时系统到容器化
    如临现场的视觉感染力,NBA决赛直播还能这样看?
    进程和线程的区别与联系
  • 原文地址:https://blog.csdn.net/guigenyi/article/details/128064991