K-Means 聚类算法项目案例——广告分析效果

发布时间:2026/7/5 15:12:51
K-Means 聚类算法项目案例——广告分析效果 项目介绍假如你们公司投放广告的渠道很多每个渠道的客户性质也可能不同比如在优酷视频投广告和今日头条投放广告效果可能会有差异。现在需要对广告效果分析实现有针对性的广告效果测量和优化工作。本案例通过各类广告渠道 90 天内额日均UV平均注册率、平均搜索率、访问深度、平均停留时长、订单转化率、投放时间、素材类型、广告类型、合作方式、广告尺寸和广告卖点等特征将渠道分类找出每类渠道的重点特征为加下来的业务讨论和数据分析提供支持。可以先看一下数据文件名为 ad_performance.csv。打开是这个样子的。数据维度概况除了渠道代号是唯一标识共12个维度889行有缺失值有异常值。数据13个维度介绍这里是 13 个字段的字段解释。1、渠道代号渠道唯一标识。2、日均UV每天的独立访问量。3、平均注册率日均注册用户数/平均每日访问量。4、平均搜索量每个访问的搜索量。5、访问深度总页面浏览量/平均每天的访问量。6、平均停留时长总停留时长/平均每天的访问量。7、订单转化率总订单数量/平均每天的访客量。8、投放时间每个广告在外投放的天数。9、素材类型jpg swf gif sp。10、广告类型banner、tips、不确定、横幅、暂停。11、合作方式roi cpc cpm cpd。12、广告尺寸14040 308388 450300 60090 480360 960126 900120390270。13、广告卖点打折、满减、满赠、秒杀、直降、满返操作导入库加载数据将需要的库导入进来。科学计算库numpypandas画图需要的库matplotlibmatplotlib.pyplot归一化和独热编码需要的库from sklearn.preprocessing import MinMaxScaler,OneHotEncoder轮廓系数指标from sklearn.metrics import silhouette_scoreKMeans模块from sklearn.cluster import KMeans以及警告的库需要忽略警告保证代码正常运行。还有设置属性防止中文乱码。import pandas as pd import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt from sklearn.preprocessing import MinMaxScaler,OneHotEncoder from sklearn.metrics import silhouette_score # 导入轮廓系数指标 from sklearn.cluster import KMeans # KMeans模块 import warnings warnings.filterwarnings(ignore) %matplotlib inline ## 设置属性防止中文乱码 mpl.rcParams[font.sans-serif][uSimhei] mpl.rcParams[axes.unicode_minus]False以上是加载库的国际惯例OneHotEncoder 是独热编码如果一个类别特征有 n 个类别将改变量按照类别分裂成 N 维新变量包含则标记为 1否则为 0用N维特征表示原来的特征。接下来读取数据。数据集储存在文件名为 ad_performance.csv的文件里把它读取出来。这里设解码规则为 gbk防止乱码。然后打印出来前几条看一看。raw_data pd.read_csv(ad_performance.csv,encodinggbk) raw_data.head()数据审查看一下数据的基本状态。# 查看基本状态 raw_data.head(2) #打印输出前2条数据使用 info() 函数查看一下数据类型。可以发现索引 1到 6 的字段都是 float 类型投放总时间是 int 类型也就是从索引 1 到 7 的字段都是数值类型的。除了这些剩下的全是 object 类型。后面就都是素材、广告、合作方式什么的对应的都是文本类型的。raw_data.info() #打印数据类型分布原始数据基本描述性信息看一看。describe 这个函数出来之后本身这些数据都是横着去显示的后面 .T 设置成了转置看到的数据就是这个情况字段就竖着排列下来去显示了。这里 count 是非空的计数mean 是平均值std 标准差min 是最小然后 1/41/23/4最大值。可以发现在日均 UV 里面是存在极大值的平均停留时间在 75% 的时候 300 多最大值的时候 4000 多差异相对来说是比较大的。访问深度也是差距有点儿大的其他字段还好。raw_data.describe().round(2).T # 打印原始数据基本描述性信息然后检查一下缺失值。查看每一列是否有缺失值打印出来发现平均停留时间这列有缺失值。# 缺失值审查 na_colsraw_data.isnull().any(axis0) #查看每一列是否具有缺失值 na_cols或者还可以通过查看缺失值行数的办法可以发现平均停留时间这列缺失了 2 行。总行数是 887只缺失了 2 行真的比重十分小。理论上直接删除影响不大。raw_data.isnull().sum().sort_values(ascendingFalse) #查看具有缺失值的行总记录数删除这两行数据。raw_data.dropna(inplaceTrue)把重复值也删掉。raw_data.drop_duplicates(inplaceTrue)执行完这个操作之后再检查一下看看有没有空值需要处理了。运行之后发现全部是 0所以没有空值需要进行处理。都是有值的数据了。raw_data.isnull().mean()之后对变量之间的相关性进行分析。使用 corr 函数计算原始数据的相关性信息并打印出来。# 相关性分析 # numeric_onlyTrue:只取数值列 raw_data.corr(numeric_onlyTrue).round(2).T # 打印原始数据相关性信息使用混淆矩阵将数据相关性信息可视化出来这样更加直观一些。在混淆矩阵里发现有两个 0.72是对称的。可以看一下是平均停留时间和访问深度这两个字段它们俩是存在比较大的相关性的。# 相关性可视化展示 # includenumber选择数据里的数字类型字段 import seaborn as sns corrraw_data.select_dtypes(includenumber).corr().round(2) sns.heatmap(corr,cmapReds,annotTrue) plt.show()通过这个结果可以看出“访问深度”和“平均停留时间”相关性比较高相关性高说明两个变量在建立模型的时候作用是一样或者效果是一样的可以考虑组合或者删除其一。数据处理数据了解的差不多了我们开始时处理数据把常规数据通过清洗、转换、规约、聚合、抽样等方式变成机器学习可以识别或者提升准确度的数据。将平均停留时间列删除。# 1 删除平均停留时间列 raw_data2raw_data.drop([平均停留时间],axis1)由于类别变量是文本格式需要把它转换成数值才能参与运算。使用独热编码对它进行转换。首先对类别变量进行一下取值。看看都有什么值。# 类别变量取值 cols[素材类型,广告类型,合作方式,广告尺寸,广告卖点] for x in cols: dataraw_data2[x].unique() print(变量【{0}】的取值有\n{1}.format(x,data)) print(-·*20)使用独热编码将类别变量转换为数值类型。然后取矩阵前 2 行数据打印出来看看。这里说明一下原始数据有成百上千行广告样本[:2]限制只输出第 1、第 2 条样本的独热编码结果所以控制台只显示两行很长的 0/1 数组如果想打印全部数据直接写print(ohe_matrix)想看前 10 行就写ohe_matrix[:10]。从输出结果可以看到原始的类别变量数据已经全部通过独热编码的方式转换为了数值数据。# 字符串分类独热编码处理 cols [素材类型,广告类型,合作方式,广告尺寸,广告卖点] # 独热编码 model_ohe OneHotEncoder(sparse_outputFalse) # 建立OneHotEncode对象 ohe_matrix model_ohe.fit_transform(raw_data2[cols]) # 直接转换 print(ohe_matrix[:2])补充一下关于独热编码的小知识点独热编码后列数 各变量类别数相加45476 26 列所以打印的每行都有 26 个 0/1 数字每行中只会出现多个 1分别对应这条样本 5 个分类变量各自所属的类别其余位置都是 0sparse_outputFalse 不加的话输出会是稀疏矩阵无法直观看到 0 和 1。还有一种是使用哑变量进行数据转换它也是独热编码这是 pandas 方法的独热编码。最后会把类别变成 True 和 False对应的是 1 和 0。这个输出结果和上面对照一下是可以对应上的。# 用pandas的方法 ohe_matrix1pd.get_dummies(raw_data2[cols]) ohe_matrix1.head(5)对于文本类型就是这么处理。数值就不用处理了因为数值类型字段本身就是数值类型。经过上述的所有处理后现在打印数据出来看一看。解释一下字段日均UV是每天访问量。raw_data2接下来对数值类型的数据进行标准化处理[:,1:7] 取出索引 1、2、3、4、5、6 六列因为平均停留时长这个字段在前面就已经删掉了。取好之后对这六列进行用最大最小化处理来减小数据量纲。处理好之后打印出来看一看。# 数据标准化 sacle_matrix raw_data2.iloc[:, 1:7] # 获得要转换的矩阵 model_scaler MinMaxScaler() # 建立MinMaxScaler模型对象 data_scaled model_scaler.fit_transform(sacle_matrix) # MinMaxScaler标准化处理 print(data_scaled.round(2))数据处理完我们将独热编码的数据和标准化转换后的数据合并。拼接使用的是 hstack 这个函数里面放入独热编码处理好的数据和最大最小标准化后的数据。运行后打印出来看看。# # 合并所有维度 X np.hstack((data_scaled, ohe_matrix)) X合并之后就可以带入模型进行训练了。建立模型先通通过平均轮廓系数检验得到最佳 KMeans 聚类模型。# 通过平均轮廓系数检验得到最佳KMeans聚类模型 score_list list() # 用来存储每个K下模型的平局轮廓系数 silhouette_int -1 # 初始化的平均轮廓系数阀值 for n_clusters in range(2, 8): # 遍历从2到5几个有限组 model_kmeans KMeans(n_clustersn_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({:*^60}.format(K值对应的轮廓系数:)) print(np.array(score_list)) # 打印输出所有K下的详细得分 print(最优的K值是:{0} \n对应的轮廓系数是:{1}.format(best_k, silhouette_int))总体思想评估指标还是怎么聚才能使得簇内距离足够小簇与簇之间平均距离足够大来评判。轮廓系数越大聚类效果越好。聚类结果特征分析与展示通过上面模型我们其实给每个观测样本打了个标签clusters即他属于4类中的哪一类。# 将原始数据与聚类标签整合 cluster_labels pd.DataFrame(cluster_labels_k, columns[clusters]) # 获得训练集下的标签信息 merge_data pd.concat((raw_data2, cluster_labels), axis1) # 将原始处理过的数据跟聚类标签整合 merge_data.head()然后看看每个类别下的样本数量和占比情况。merge_data[clusters].unique()然后计算每个聚类类别下的样本量和样本占比。# 计算每个聚类类别下的样本量和样本占比 clustering_count pd.DataFrame(merge_data[渠道代号].groupby(merge_data[clusters]).count()).T.rename({渠道代号: counts}) # 计算每个聚类类别的样本量 clustering_ratio (clustering_count / len(merge_data)).round(2).rename({counts: percentage}) # 计算每个聚类类别的样本量占比 print(clustering_count) print(#*30) print(clustering_ratio)然后看每个类别内部最显著的特征。# 计算各个聚类类别内部最显著特征值 cluster_features [] # 空列表用于存储最终合并后的所有特征信息 for line in range(best_k): # 读取每个类索引 label_data merge_data[merge_data[clusters] line] # 获得特定类的数据 part1_data label_data.iloc[:, 1:7] # 获得数值型数据特征 part1_desc part1_data.describe().round(3) # 得到数值型特征的描述性统计信息 merge_data1 part1_desc.iloc[2, :] # 得到数值型特征的均值 part2_data label_data.iloc[:, 7:-1] # 获得字符串型数据特征 part2_desc part2_data.describe(includeall) # 获得字符串型数据特征的描述性统计信息 merge_data2 part2_desc.iloc[2, :] # 获得字符串型数据特征的最频繁值 merge_line pd.concat((merge_data1, merge_data2), axis0) # 将数值型和字符串型典型特征沿行合并 cluster_features.append(merge_line) # 将每个类别下的数据特征追加到列表 # 输出完整的类别特征信息 cluster_pd pd.DataFrame(cluster_features).T # 将列表转化为矩阵 print({:*^60}.format(每个类别主要的特征:)) all_cluster_set pd.concat((clustering_count, clustering_ratio, cluster_pd),axis0) # 将每个聚类类别的所有信息合并 all_cluster_set图形化输出。输出之前先对数据进行一下预处理。#各类别数据预处理 num_sets cluster_pd.iloc[:6, :].T.astype(np.float64) # 获取要展示的数据 num_sets_max_min model_scaler.fit_transform(num_sets) # 获得标准化后的数据 print(num_sets) print(-*20) print(num_sets_max_min)最后画雷达图将结果可视化出来。# 画图 fig plt.figure(figsize(7,7)) #建立画布 axfig.add_subplot(111,polarTrue) #增加子网格,注意polar参数.创建一个 极坐标Polar子图 labelsnp.array(merge_data1.index) #设置要展示的数据标签 cor_list[g,r,y,b,orange,purple,pink] #定义不同类别的颜色 cor_list_newcor_list[:best_k] anglesnp.linspace(0,2*np.pi,len(labels),endpointFalse) #计算各个区间的角度,endpointFalse不包含终点 anglesnp.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-,ccor_list_new[i],label第%d类渠道%(i)) #画线 i是类别 ax.fill(angles,data,alpha0.8) #填充 # 设置图像显示格式 print(angles) print(labels) ax.set_thetagrids(angles[0:-1]*180/np.pi,labels,fontpropertiesSimHei) #设置极坐标轴 ax.set_title(各聚类类别显著特征对比,fontpropertiesSimHei) #设置标题放置 ax.set_rlim(-0.2,1.2) #设置坐标轴尺度范围 plt.legend(locupper right,bbox_to_anchor(1.2,1.0)) #设置图例位置 plt.show()得出结论第0类渠道:“投放总时间”和“访问深度”这两个特征表现突出数值高但日均 UV、平均注册率、平均搜索量、订单转化率表现相对比较低的。