K-Means聚类分析广告投放效果的改进(数据标准化、最佳K值的确定)

业务场景:
甲公司投放广告的渠道很多,每个渠道的客户性质也可能不同,比如在优酷视频投广告和今日头条投放广告,效果可能会有差异。现在需要对广告效果分析实现有针对性的广告效果测量和优化工作。

1 导入相关库及数据

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
#忽略警告
import warnings 
warnings.filterwarnings("ignore")
# 设置图像细节
plt.rcParams["font.sans-serif"] = "SimHei"   #将pyplot字体转化成中文
plt.rcParams['font.size'] = 12  # 设置字体大小
plt.rcParams['axes.unicode_minus'] = False # 设置正常显示负号
df_1 = pd.read_csv(r"C:\Users\A\Desktop\python\上课用\数据挖掘案例\ad_performance (1).csv")

2.数据探索与清洗

#2.1查看数据概览、前五项、类型
df_1.info()
print(df_1.head())
type(df_1)
print(df_1.describe().round(2))

(1)数据共889行记录,12个维度(第一列为序号),数据量较小,可以用聚类分析。

(2)日均UV到投放时间为数值型数据,后5项为分类型数据。

日均UV的数据波动非常大,说明了不同渠道间的特征差异非常明显。

 (3)数值型数据度量单位不同,需要做标准化处理

平均注册率、平均搜索量、订单转化率的多个统计量(例如最小值、25%分位数等)都为0,需要关注其原因:打印输出过程中仅保留了2位小数,而这几个统计量的数据本身就非常小。

#2.2缺失值处理
#(1)查看缺失值
print(df_1.isnull().any(axis=0))
#(2)计算 DataFrame 中每一列的缺失值数量
df_1.isnull().sum()

 平均停留时间存在2项缺失值,聚类分析对缺失值敏感,需要做进一步处理

#(3)缺失值可视化
import missingno as msno
msno.bar(df_1)#查看数据集数据完整性

此处用缺失值可视化工具查看缺失值分布情况(本案例效果不明显)

#(4)提取缺失值信息
df_1[df_1.isnull().values==True]

#(5)缺失值填充&删除
# 相关性分析
df_1.corr().round(2)
import seaborn as sns 
corr = df_1.corr().round(2)
sns.heatmap(corr,cmap='Reds',annot = True)

 用热力图进行维度间的相关性分析:

(1)“访问深度”和“平均停留时间”相关性比较高,相关性高说明两个变量在建立模型的时候,作用是一样或者效果是一样的,可以考虑组合或者删除其一。

(2)如果维度较多,或者维度间相关性较强,要考虑使用降维。

# (6) 删除缺失值(平均停留时间列与访问深度高度相关,保留完整字段)
df_2 = df_1.drop(['平均停留时间'],axis=1)
#2.3 判断重复值
df_2d = df_2.duplicated().sum
print(df_2d)

没有重复值

#2.4 判断离群值 (离群值对聚类效果影响很大)
import seaborn as sns
sns.boxplot(x=df_2['访问深度'])

 此处省略其他维度绘图过程

# 提取相关数据判断是否为离群值
df_1[df_1['访问深度']>20] #停留时间判断合理

#3数据转换
#(1)分类变量编码:二分类可以转化为0-1变量,多分类变量如果是等距有序的同样可以直接运算,若是无序的或非等距的则需要进行编码处理,如转化为哑变量或利用one_hot编码处理
# 类别变量取值
cols=["素材类型","广告类型","合作方式","广告尺寸","广告卖点"]
for x in cols:
    data=df_2[x].unique() #去重取值
    print("变量{0}的取值有:\n{1}".format(x,data))

  数据类型包含文本、数值型,不存在顺序意义。

#one-hot编码
ohe_matrix=pd.get_dummies(df_2[cols])
ohe_matrix.head()

 (2)数值型数据标准化

对于StandardScaler、MinMaxScaler,通常情况下优先采用StandardScaler,使用MinMaxScaler可能会导致信息的损失。

方法一:MinMaxScaler

#数据标准化(MinMaxScaler)
sacle_matrix=df_2.iloc[:, 2:8]  # 获得要转换的矩阵
model_scaler=MinMaxScaler()  # 建立MinMaxScaler模型对象,(0,1)区间的字段较多,方便统一比较
data_scaled=model_scaler.fit_transform(sacle_matrix)  # MinMaxScaler标准化处理
print(data_scaled.round(2))

方法二: StandardScaler

#合并所有字段
X=np.hstack((data_scaled, ohe_matrix))

 3 建立模型

K值选择方法有肘部法则、拍脑袋法、gap statistic法、轮廓系数等

(1)MinMaxScaler数据 

# 通过手肘法得到最佳KMeans聚类模型
from scipy.spatial.distance import cdist
distortions = []
K = range(2,8)
for k in K:
    kmeanModel = KMeans(n_clusters=k).fit(X)
    kmeanModel.fit(X)
    distortions.append(sum(np.min(cdist(X, kmeanModel.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])
 
# 手肘图
plt.plot(K, distortions, 'bx-')
plt.xlabel('k')
plt.ylabel('Distortion')
plt.title('The Elbow Method showing the optimal k')
plt.show()

 

 此处的拐点为3,但SSE相对较高

#3 建立模型
# 通过平均轮廓系数检验得到最佳KMeans聚类模型
from sklearn import datasets, metrics
X=np.hstack((data_scaled, ohe_matrix))
Scores = [] # 用来存储每个K下模型的平局轮廓系数
for i in range(2,8):  # 遍历从2到5几个有限组
    model_kmeans=KMeans(n_clusters=i)  # 建立聚类模型对象
    m1=model_kmeans.fit(X)  # 训练聚类模型
    Scores.append(metrics.silhouette_score(X, m1.labels_ , metric='euclidean'))   
j = range(2,8)
plt.xlabel('k')
plt.ylabel('轮廓系数')
plt.plot(j,Scores,'o-')
plt.show()

如果平均轮廓得分值小于0,意味着聚类效果不佳;如果值大于0且小于0.5,那么说明聚类效果一般;如果值大于0.5,则说明聚类效果比较好。本案例在K=4时,得分大于0.5,说明效果较好。

其次,用于聚类分析的K值的确定不会太大。如果值太大,那么聚类效果可能不明显,因为大量信息都会被分散到各个小类之中,从而导致数据的碎片化。另外,还需要根据分析需求确定,如果用来做数据分析,K太大将导致可能无法找到各个群的核心差异特征。

 结合手肘图,确定K值=4。

#4 聚类结果特征分析与展示
# 将原始数据与聚类标签整合
model_kmeans=KMeans(n_clusters=4)  # 建立聚类模型对象
labels_tmp=model_kmeans.fit_predict(X)
cluster_labels_k=labels_tmp  # 保存聚类标签
cluster_labels=pd.DataFrame(cluster_labels_k, columns=['clusters'])  # 获得训练集下的标签信息
merge_data=pd.concat((df_2, cluster_labels), axis=1)  # 将原始处理过的数据跟聚类标签整合
merge_data.head()

(2)StandardScaler数据

#数据标准化(StandardScaler)
sacle_matrix=df_2.iloc[:, 2:8]  # 获得要转换的矩阵
model_scaler=StandardScaler()  # 建立StandardScaler模型对象
data_scaled=model_scaler.fit_transform(sacle_matrix)  # StandardScaler标准化处理
print(data_scaled.round(2))

 同样运用手肘法和轮廓系数法确定最佳K值

 拐点出现在K=5

 轮廓系数图:最高值出现在K值=7,其次是K=5。结合手肘图,应该确定最佳K值为5。

但是轮廓系数<0.5,聚类效果不佳。

# (2)计算每个聚类类别下的样本量和样本占比
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)

(1) MinMaxScaler数据 

不同类别间需要具有明显的差异性特征,并且类别间的样本量大体分布均衡。 

 (2) StandardScaler数据

 类别间的样本量大体分布不均衡,第5类样本量只有2个。

因此,本案例后续沿用 MinMaxScaler数据 进行后续分析。

# (3)计算各个聚类类别内部最显著特征值
cluster_features=[]  # 空列表,用于存储最终合并后的所有特征信息
for i in range(best_k):  # 读取每个类索引
    label_data=merge_data[merge_data['clusters'] == i]
    part1_data=label_data.iloc[:,2:8]  # 获得数值型数据特征
    part1_desc=part1_data.describe().round(4)  # 得到数值型特征的描述性统计信息
    merge_data1=part1_desc.iloc[1, :]  # 得到数值型特征的均值
    
    part2_data=label_data.iloc[:,8:-1]  # 获得字符串型数据特征
    part2_desc=part2_data.describe(include='all')  # 获得字符串型数据特征的描述性统计信息
    merge_data2=part2_desc.iloc[3, :]  # 获得字符串型数据特征的最频繁值
    
    merge_i=pd.concat((merge_data1, merge_data2), axis=0)  # 将数值型和字符串型典型特征沿行合并
    cluster_features.append(merge_i)  # 将每个类别下的数据特征追加到列表
#(4)  输出完整的类别特征信息
cluster_pd=pd.DataFrame(cluster_features).T  # 将列表转化为矩阵
all_cluster_set=pd.concat((clustering_count, clustering_ratio, cluster_pd),axis=0)  # 将每个聚类类别的所有信息合并
all_cluster_set

 

 从上面的各类别特征中可能很难直观地发现不同类别的显著性特征,后面通过雷达图的形式对各个聚类类别的数值型特征做对比展示。

4 图形化输出

# 4 图形化输出
#(1)各类别数据预处理
num_sets=cluster_pd.iloc[:6, :].T.astype(np.float64)  # 获取要展示的数据
num_sets_max_min=model_scaler.fit_transform(num_sets).round(4)  # 获得标准化后的数据
print(num_sets)
print('~'*60)
print(num_sets_max_min)

 由于不同特征的数据量级差异很大,因此需要先对其做标准化处理,这样才能使得不同数据间具有对比的可能性。

# 画图
fig = plt.figure(figsize=(6,6))  # 建立画布
ax = fig.add_subplot(111, polar=True)  # 增加子网格,注意polar参数
labels = np.array(merge_data1.index)  # 设置要展示的数据标签
cor_list = ['g', 'r', 'y', 'b']  # 定义不同类别的颜色
angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False)  # 计算各个区间的角度
angles = np.concatenate((angles, [angles[0]]))  # 建立相同首尾字段以便于闭合
labels=np.concatenate((labels,[labels[0]]))  #对labels进行闭合
# 画雷达图
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=cor_list[i], label="第%d类渠道"%(i))  # 画线
    ax.fill(angles, data,alpha=2.5)
# 设置图像显示格式
ax.set_thetagrids(angles * 180 / np.pi, labels, fontproperties="SimHei")  # 设置极坐标轴
ax.set_title("各聚类类别显著特征对比", fontproperties="SimHei")  # 设置标题放置
ax.set_rlim(-0.2, 1.2)  # 设置坐标轴尺度范围
plt.legend(loc="upper right" ,bbox_to_anchor=(1.2,1.0))  # 设置图例位置
plt.savefig("./渠道广告聚类特征分布图.jpg")  # 保存图片到本地

 

 5 分析报告

(1)描述性阐述

从案例结果来看,所有的渠道被分为4个类别,每个类别的样本量分别为73、349、313、154,对应的占比分别为8%、39%、35%、17。第1个类别样本量较少,最多的是第2个类别。

结合雷达图,可以清晰地看出不同类别广告媒体的特征倾向:

聚类1(索引值为0,绿色线条):日均UV和搜索量较为突出,其他各方面的特征都不明显,各个流量质量和流量数量的指标均处于“中等偏下”的层次,其引流效果显著。

聚类2(索引值为1,红色线条):这类广告媒体除了访问深度表现优异,其他各方面的特征都是最低,并且由于其占比达到39%,因此这是广告媒体的主体渠道之一。

聚类3(索引值为2,黄色线条):这类广告媒体的显著特征是平均搜索量、订单转化率、投放总时间最高,但平均注册率最低,说明其渠道客户潜力很高。

聚类4(索引值为3,蓝色线条):这类广告媒体的显著特征是平均注册率最高,订单转化率和访问深度也处于较高水平,综合效果最佳。

(2)数据结论

聚类1:适合大规模的广告宣传和引流时使用,增加网站的点击量和话题热度。

聚类2:除了访问深度与其他渠道接近外,各方面表现均比较一般,因此需要业务部门重点考虑其投放的实际价值。

聚类3:主要特点是转化效果较高,但短板是平均注册率最低,因此该类媒体无法为企业带来持续稳定的流量以及复购率可能较低。这类广告的特质适合用户转化,尤其是有关订单转化的提升。

聚类4:该渠道广告在投放总时间较少的情况下,注册率、订单转化效果较好,能为企业带来较好的收益,适合各种场景下的投放需求。

(3)应用建议

1)聚类1:可以作为在每次大型促销活动时引流的骨干,因为这更符合广告本身在全部推送媒体中的角色定位。对于这类媒体的单位流量成本需要重点关注,实际投放过程中可以满减为促销点,通过较大尺寸(如308×338)的广告类型引导流量点击。

2)聚类2:在资金不足或优化广告结构时,由于其目前投入最高,应重点考虑其取舍问题。

3)聚类3:在预算充足的情况下,优先做投放选择。在增加预算的基础上,重点从媒体人群匹配、广告执行(例如打折卖点、中大型图片roi和cpm类合作方式)等方面做优化。产品部门要关注该类渠道用户画像中的年龄、学历等,注册UI界面及相关环节是否繁琐并针对问题优化。

4)聚类4:对于整体广告效果有支撑价值,在目前其样本量较低的情况下,建议优先增加投入预算。可以用来提升全局广告的订单质量效果,因此是一类非常关键的质量渠道。如果和聚类3组合投入使用,将会弥补聚类3注册转化率不足的缺点。

  • 13
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值