网站制作与管理技术...,网站建设 运维 管理包括,如何注册域名网站,微信显示wordpress电商-广告投放效果分析#xff08;KMeans聚类、数据分析#xff09; 文章目录 电商-广告投放效果分析#xff08;KMeans聚类、数据分析#xff09;项目介绍数据数据维度概况数据13个维度介绍 导入库#xff0c;加载数据数据审查相关性分析数据处理建立模型聚类结果特征分析…电商-广告投放效果分析KMeans聚类、数据分析 文章目录 电商-广告投放效果分析KMeans聚类、数据分析项目介绍数据数据维度概况数据13个维度介绍 导入库加载数据数据审查相关性分析数据处理建立模型聚类结果特征分析与展示数据结论 项目介绍
数据
假如公司投放广告的渠道很多每个渠道的客户性质也可能不同比如在优酷视频投广告和今日头条投放广告效果可能会有差异。现在需要对广告效果分析实现有针对性的广告效果测量和优化工作。
本案例通过各类广告渠道90天内额日均UV平均注册率、平均搜索率、访问深度、平均停留时长、订单转化率、投放时间、素材类型、广告类型、合作方式、广告尺寸和广告卖点等特征将渠道分类找出每类渠道的重点特征为加下来的业务讨论和数据分析提供支持。
数据维度概况
除了渠道唯一标识共12个维度889行有缺失值有异常值。
数据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’ ‘900120’ 390270’ 13、广告卖点打折、满减、满赠、秒杀、直降、满返
导入库加载数据
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模块
%matplotlib inline
## 设置属性防止中文乱码
mpl.rcParams[font.sans-serif] [uSimHei]
mpl.rcParams[axes.unicode_minus] False以上是加载库的国际惯例OneHotEncoder是独热编码如果一个类别特征有n个类别将该变量按照类别分裂成N维新变量包含则标记为1否则为0用N维特征表示原来的特征。
raw_data pd.read_csv(r./ad_performance.csv)
raw_data.head()raw_data pd.read_csv(r./ad_performance.csv)这行代码使用Pandas库的read_csv()函数读取名为 “ad_performance.csv” 的CSV文件中的数据并将数据加载到名为 raw_data 的DataFrame对象中。r./ad_performance.csv 是CSV文件的路径。r 表示使用原始字符串防止反斜杠被转义。 raw_data.head()这行代码使用DataFrame对象的head()方法查看DataFrame的前几行数据默认显示前5行。这有助于了解数据的结构和格式。
数据审查
# 查看基本状态
raw_data.head(2) # 打印输出前2条数据
raw_data.info()# 打印数据类型分布
raw_data.describe().round(2).T # 打印原始数据基本描述性信息
# 缺失值审查
na_cols raw_data.isnull().any(axis0) # 查看每一列是否具有缺失值
na_cols
raw_data.isnull().sum().sort_values(ascendingFalse)# 查看具有缺失值的行总记录数raw_data.head(2)这行代码打印输出了数据的前两条记录以便快速查看数据的样式和结构。 raw_data.info()这行代码打印输出了数据集中每一列的数据类型以及非空值的数量这有助于了解数据的完整性和类型。 raw_data.describe().round(2).T这行代码打印了数据集的基本描述性信息包括计数、均值、标准差、最小值、25%分位数、中位数、75%分位数和最大值。.round(2)方法用于将描述性统计结果保留两位小数.T方法用于转置结果以便查看。 na_cols raw_data.isnull().any(axis0)这行代码查看了每一列是否具有缺失值。raw_data.isnull()会生成一个布尔值的DataFrame表示每个单元格是否为缺失值.any(axis0)会沿着列方向(axis0)判断是否有至少一个True即是否存在缺失值。 na_cols这行代码会输出一个布尔型Series其中索引是DataFrame的列名值为True表示该列存在缺失值值为False表示该列没有缺失值。 raw_data.isnull().sum().sort_values(ascendingFalse)这行代码查看了具有缺失值的列并统计了每列的缺失值数量并按照缺失值数量降序排列。.isnull()用于判断每个单元格是否为缺失值.sum()对每列的缺失值数量进行求和.sort_values(ascendingFalse)将结果按照缺失值数量进行降序排序。
相关性分析
# 相关性分析
raw_data.corr(numeric_onlyTrue).round(2).T # 打印原始数据相关性信息
# 相关性可视化展示
import seaborn as sns
corr raw_data.corr().round(2)
sns.heatmap(corr,cmapReds,annot True)raw_data.corr(numeric_onlyTrue).round(2).T这行代码计算了原始数据中数值型列之间的相关性并将结果打印输出。.corr(numeric_onlyTrue)表示只考虑数值型列之间的相关性.round(2)将相关性系数保留两位小数.T方法用于转置结果以便查看。 import seaborn as sns这行代码导入了Seaborn库用于绘制数据可视化图形。 corr raw_data.corr().round(2)这行代码计算了原始数据的相关性矩阵并将相关性系数保留两位小数。 sns.heatmap(corr,cmapReds,annot True)这行代码绘制了一个热力图来展示相关性矩阵。heatmap()函数用于绘制热力图corr是相关性矩阵cmapReds指定了颜色映射为红色调annot True表示在图中显示相关性系数的数值。 数据处理
数据了解的差不多了我们开始时处理数据把常规数据通过清洗、转换、规约、聚合、抽样等方式变成机器学习可以识别或者提升准确度的数据。
# 1 删除平均平均停留时间列
raw_data2 raw_data.drop([平均停留时间],axis1)raw_data2 raw_data.drop([平均停留时间],axis1)这行代码使用了DataFrame的drop()方法通过指定平均停留时间列和axis1参数从原始数据中删除了’平均停留时间’列。删除操作不会在原始数据上进行而是返回一个新的DataFrame对象即raw_data2其中不包含被删除的列。
类别变量的独热编码
# 类别变量取值
cols[素材类型,广告类型,合作方式,广告尺寸,广告卖点]
for x in cols:dataraw_data2[x].unique()print(变量【{0}】的取值有\n{1}.format(x,data))print(-·*20)cols[素材类型,广告类型,合作方式,广告尺寸,广告卖点]定义了一个列表 cols其中包含了需要统计取值的类别变量的列名。 for x in cols:这是一个循环语句用于遍历列表 cols 中的每个元素。 dataraw_data2[x].unique()这行代码获取了 DataFrame 中列 x循环变量的唯一取值即去除重复后的取值列表。 print(变量【{0}】的取值有\n{1}.format(x,data))这行代码使用字符串格式化打印输出了变量 x 的取值列表。其中 {0} 和 {1} 分别被循环变量 x 和取值列表 data 所替代。 print(-·*20)这行代码打印了一条分隔线用于区分不同类别变量的取值统计结果。
# 字符串分类独热编码处理
cols [素材类型,广告类型,合作方式,广告尺寸,广告卖点]
model_ohe OneHotEncoder(sparseFalse) # 建立OneHotEncode对象
ohe_matrix model_ohe.fit_transform(raw_data2[cols]) # 直接转换
print(ohe_matrix[:2])cols [素材类型,广告类型,合作方式,广告尺寸,广告卖点]定义了一个列表 cols其中包含了需要进行独热编码处理的字符串分类变量的列名。 model_ohe OneHotEncoder(sparseFalse)这行代码创建了一个OneHotEncoder对象 model_ohe用于进行独热编码处理。sparseFalse参数表示不生成稀疏矩阵而是直接生成密集矩阵。 ohe_matrix model_ohe.fit_transform(raw_data2[cols])这行代码对原始数据集 raw_data2 中的 cols 列进行独热编码处理得到编码后的矩阵。fit_transform()方法将字符串分类变量转换为独热编码表示。 print(ohe_matrix[:2])这行代码打印输出了独热编码后的矩阵的前两行。这样做是为了查看编码结果是否符合预期。
# 用pandas的方法
ohe_matrix1pd.get_dummies(raw_data2[cols])
ohe_matrix1.head(5)ohe_matrix1pd.get_dummies(raw_data2[cols])这行代码调用了 Pandas 的 get_dummies() 方法对原始数据集 raw_data2 中的 cols 列进行独热编码处理。get_dummies() 方法会将指定列中的每个分类值都转换为一个独热编码的列并生成一个新的 DataFrame 对象 ohe_matrix1。 ohe_matrix1.head(5)这行代码打印输出了独热编码后的结果的前 5 行。这样做是为了查看编码结果是否符合预期。
# 数据标准化
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))sacle_matrix raw_data2.iloc[:, 1:7]这行代码从原始数据集 raw_data2 中提取了需要进行标准化处理的数据子集。iloc[:, 1:7]表示选取所有行以及第 2 到第 7 列的数据Python中索引是从0开始的。 model_scaler MinMaxScaler()这行代码创建了一个MinMaxScaler对象 model_scaler用于进行最小-最大缩放标准化处理。 data_scaled model_scaler.fit_transform(sacle_matrix)这行代码使用 fit_transform() 方法对提取的数据子集 sacle_matrix 进行标准化处理得到标准化后的结果。MinMaxScaler会将每列的数据缩放到指定的范围默认为[0, 1]并返回一个标准化后的NumPy数组 data_scaled。 print(data_scaled.round(2))这行代码打印输出了标准化后的数据使用 round(2) 方法保留两位小数。这样做是为了查看标准化后的数据是否符合预期。
# # 合并所有维度
X np.hstack((data_scaled, ohe_matrix))np.hstack((data_scaled, ohe_matrix))这行代码使用了 NumPy 的 hstack() 函数将两个矩阵水平堆叠在一起。data_scaled 是经过标准化处理后的特征矩阵ohe_matrix 是经过独热编码处理后的特征矩阵。水平堆叠意味着将两个矩阵按列连接起来。 X np.hstack((data_scaled, ohe_matrix))这行代码将水平堆叠后的矩阵赋值给变量 X这样就得到了合并后的特征矩阵 X其中包含了标准化处理后的数据和独热编码处理后的数据。
建立模型
# 通过平均轮廓系数检验得到最佳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))总体思想评价指标还是怎么聚才能使得簇内距离足够小簇与簇之间平均距离足够大来评判。 score_list list()创建一个空列表用于存储每个K值下模型的平均轮廓系数。 silhouette_int -1初始化平均轮廓系数的阈值为-1。 for n_clusters in range(2, 8):循环遍历从2到7的整数这些整数代表了KMeans聚类模型的聚类数。 model_kmeans KMeans(n_clustersn_clusters)创建一个KMeans聚类模型对象其中n_clusters参数表示要创建的聚类数。 labels_tmp model_kmeans.fit_predict(X)利用输入数据 X 对KMeans模型进行训练并预测每个样本的聚类标签。 silhouette_tmp silhouette_score(X, labels_tmp)计算当前K值下的平均轮廓系数用来衡量聚类效果的好坏。 if silhouette_tmp silhouette_int:如果当前K值下的平均轮廓系数大于之前的最高值。 best_k n_clusters更新最佳的K值为当前K值。 silhouette_int silhouette_tmp更新最高的平均轮廓系数为当前轮廓系数。 best_kmeans model_kmeans保存当前最佳的KMeans聚类模型对象。 cluster_labels_k labels_tmp保存当前最佳的聚类标签。 score_list.append([n_clusters, silhouette_tmp])将当前K值和对应的平均轮廓系数追加到列表score_list中。 print({:*^60}.format(K值对应的轮廓系数:))打印输出一个60个星号的分割线。 print(np.array(score_list))打印输出所有K值及其对应的平均轮廓系数。 print(最优的K值是:{0} \n对应的轮廓系数是:{1}.format(best_k, silhouette_int))打印输出最佳的K值和对应的平均轮廓系数。
聚类结果特征分析与展示
通过上面模型我们其实给每个观测样本打了个标签clusters即他属于4类中的哪一类
# 将原始数据与聚类标签整合
cluster_labels pd.DataFrame(cluster_labels_k, columns[clusters]) # 获得训练集下的标签信息
merge_data pd.concat((raw_data2, cluster_labels), axis1) # 将原始处理过的数据跟聚类标签整合
merge_data.head()cluster_labels pd.DataFrame(cluster_labels_k, columns[clusters])这行代码创建了一个名为 cluster_labels 的DataFrame对象其中包含了聚类模型预测得到的聚类标签信息。cluster_labels_k 是之前代码中得到的聚类标签数据columns[clusters] 指定了DataFrame的列名为’clusters’。 merge_data pd.concat((raw_data2, cluster_labels), axis1)这行代码使用了 Pandas 的 concat() 函数将原始处理过的数据 raw_data2 与聚类标签数据 cluster_labels 沿着列方向(axis1)进行合并。合并后的结果赋值给变量 merge_data。 merge_data.head()这行代码打印输出了合并后的DataFrame的前几行数据。这样做是为了查看整合后的数据是否符合预期。
然后看看每个类别下的样本数量和占比情况
# 计算每个聚类类别下的样本量和样本占比
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)clustering_count pd.DataFrame(merge_data[渠道代号].groupby(merge_data[clusters]).count()).T.rename({渠道代号: counts})这行代码首先使用 Pandas 的 groupby() 函数按照聚类标签 clusters 对 渠道代号 这一列进行分组然后对每个组计算该列的计数。结果是一个包含每个聚类类别下的样本量的 Series 对象。接着使用 pd.DataFrame() 将其转换为 DataFrame并进行转置.T操作以使行和列对调。最后使用 rename() 函数将列名 渠道代号 更改为 counts以反映其含义。 clustering_ratio (clustering_count / len(merge_data)).round(2).rename({counts: percentage})这行代码计算每个聚类类别的样本量占比。首先将每个聚类类别的样本量除以总样本量然后使用 round(2) 方法将结果保留两位小数。接着使用 rename() 函数将列名 counts 更改为 percentage以反映其含义。 print(clustering_count)打印输出每个聚类类别下的样本量。 print(#*30)打印输出一个包含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_setcluster_features []创建一个空列表用于存储每个聚类类别内部最显著的特征值。 for line in range(best_k):循环遍历每个聚类类别的索引。 label_data merge_data[merge_data[clusters] line]获取特定聚类类别的数据即通过筛选出clusters列值等于当前索引值的行。 part1_data label_data.iloc[:, 1:7]和part2_data label_data.iloc[:, 7:-1]分别提取数值型特征和字符串型特征的数据子集。 part1_desc part1_data.describe().round(3)和part2_desc part2_data.describe(includeall)分别计算数值型特征和字符串型特征的描述性统计信息。 merge_data1 part1_desc.iloc[2, :]和merge_data2 part2_desc.iloc[2, :]分别获取数值型特征和字符串型特征的均值数值型特征或最频繁值字符串型特征。 merge_line pd.concat((merge_data1, merge_data2), axis0)将数值型特征和字符串型特征沿行方向合并为一个Series对象。 cluster_features.append(merge_line)将每个类别下的数据特征追加到列表cluster_features中。 cluster_pd pd.DataFrame(cluster_features).T将列表cluster_features转换为DataFrame对象转置以便特征信息按列排列。 all_cluster_set pd.concat((clustering_count, clustering_ratio, cluster_pd),axis0)将聚类类别的样本量、样本占比和主要特征值信息沿着行方向合并为一个DataFrame对象all_cluster_set。 print({:*^60}.format(每个类别主要的特征:))打印输出一个包含60个星号的标题行用于分隔不同部分的输出。 print(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)num_sets cluster_pd.iloc[:6, :].T.astype(np.float64)这行代码从合并后的聚类类别特征数据 cluster_pd 中提取了前6行即数值型特征的转置并将其转换为np.float64类型的数据。这样做是为了将数据按列排列方便后续处理。 num_sets_max_min model_scaler.fit_transform(num_sets)这行代码利用之前创建的 MinMaxScaler 对象 model_scaler 对数值型特征数据进行标准化处理。使用 fit_transform() 方法对数据进行标准化将数据缩放到[0, 1]的范围内。 print(num_sets)打印输出原始的数值型特征数据用于查看。 print(-*20)打印输出一行分隔线。 print(num_sets_max_min)打印输出标准化后的数值型特征数据用于查看。
# 画图
fig plt.figure(figsize(7,7)) # 建立画布
ax fig.add_subplot(111, polarTrue) # 增加子网格注意polar参数
labels np.array(merge_data1.index) # 设置要展示的数据标签
cor_list [g, r, y, b] # 定义不同类别的颜色
angles np.linspace(0, 2 * np.pi, len(labels), endpointFalse) # 计算各个区间的角度
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-, ccor_list[i], label第%d类渠道%(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)) # 设置图例位置fig plt.figure(figsize(7,7))创建一个大小为7x7的画布。 ax fig.add_subplot(111, polarTrue)在画布上添加一个极坐标子网格参数polarTrue表示使用极坐标。 labels np.array(merge_data1.index)将聚类类别的特征名称作为要展示的数据标签。 cor_list [g, r, y, b]定义不同类别的颜色。 angles np.linspace(0, 2 * np.pi, len(labels), endpointFalse)计算各个特征区间的角度。 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-, ccor_list[i], label第%d类渠道%(i))绘制雷达图表示各个特征在当前聚类类别下的数值使用不同颜色区分不同类别。 ax.fill(angles, data,alpha0.8)填充雷达图的闭合区域以加强可视化效果。 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))设置图例的位置将图例放在图的右上角。 数据结论
从案例结果来看所有的渠道被分为4各类别每个类别的样本量分别为154、313、349 、73对应占比分别为17%、35%、39%、8%。
通过雷达图可以清楚的知道
类别1索引为2类的渠道 这类广告媒体除了访问深度和投放时间较高其他属性较低因此这类广告媒体效果质量较差并且占到39%因此这类是主题渠道之一。 业务部门要考虑他的实际投放价值。
类别2索引为1类的渠道 这类广告媒体除了访问深度略差在平均搜索量、日均UV、订单转化率等广告效果指标上表现良好是一类综合效果较好的渠道。 但是日均UV是短板较低。无法给企业带来大量的流量以及新用户这类广告的特质适合用户转化尤其是有关订单的转化提升。
类别3索引为0类的渠道 这类广告媒体的显著特征是日均UV和注册率较高其“引流”和“拉新”效果好可以在广告媒体中定位为引流角色。 符合“广而告之”的诉求适合“拉新”使用。
类别4索引为3类的渠道 这类渠道各方面特征都不明显各个流量质量和流量数量的指标均处于“中等”层次。不突出但是均衡考虑在各场景下可以考虑在这个渠道投放广告。