Python-K-Means聚类分析广告效果

Step 0. 项目介绍

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

本案例,通过各类广告渠道90天内额日均UV,平均注册率、平均搜索率、访问深度、平均停留时长、订单转化率、投放时间、素材类型、广告类型、合作方式、广告尺寸和广告卖点等特征,将渠道分类,找出每类渠道的重点特征,为加下来的业务讨论和数据分析提供支持。

Step 1. 导入模块

In [23]:

import pandas as pd
import numpy as np 
import plotly.graph_objects as go
import plotly.express as px

from sklearn.preprocessing import MinMaxScaler,OneHotEncoder 
from sklearn.metrics import silhouette_score # 导入轮廓系数指标
from sklearn.cluster import KMeans # KMeans模块

import warnings
from pandas.core.common import SettingWithCopyWarning

warnings.filterwarnings('ignore', category=SettingWithCopyWarning)

Step 2. 数据概览

In [3]:

raw_data = pd.read_csv(r'/home/kesci/input/guanggao2482/ad_performance.csv')
raw_data.head()

Out[3]:

Unnamed: 0渠道代号日均UV平均注册率平均搜索量访问深度平均停留时间订单转化率投放总时间素材类型广告类型合作方式广告尺寸广告卖点
00A2033.690.00710.02142.3071419.770.025820jpgbannerroi140*40打折
11A387178.700.00400.03242.0489157.940.003019jpgbannercpc140*40满减
22A38891.770.00220.05301.8771357.930.00264jpgbannercpc140*40满减
33A3891.090.00740.33824.2426364.070.015310jpgbannercpc140*40满减
44A3903.370.00280.17402.1934313.340.000730jpgbannercpc140*40满减

In [27]:

print("——" * 10)
print('数据集存在重复值个数:')
print(raw_data.duplicated().sum())
print("——" * 10)
print('数据集缺失值情况:')
print(raw_data.isna().sum())
print("——" * 10)
print('数据集各字段类型:')
print(raw_data.dtypes)
print("——" * 10)
print('数据总体概览:')
print(raw_data.info())
————————————————————
数据集存在重复值个数:
0
————————————————————
数据集缺失值情况:
渠道代号      0
日均UV      0
平均注册率     0
平均搜索量     0
访问深度      0
平均停留时间    2
订单转化率     0
投放总时间     0
素材类型      0
广告类型      0
合作方式      0
广告尺寸      0
广告卖点      0
dtype: int64
————————————————————
数据集各字段类型:
渠道代号       object
日均UV      float64
平均注册率     float64
平均搜索量     float64
访问深度      float64
平均停留时间    float64
订单转化率     float64
投放总时间       int64
素材类型       object
广告类型       object
合作方式       object
广告尺寸       object
广告卖点       object
dtype: object
————————————————————
数据总体概览:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 889 entries, 0 to 888
Data columns (total 13 columns):
渠道代号      889 non-null object
日均UV      889 non-null float64
平均注册率     889 non-null float64
平均搜索量     889 non-null float64
访问深度      889 non-null float64
平均停留时间    887 non-null float64
订单转化率     889 non-null float64
投放总时间     889 non-null int64
素材类型      889 non-null object
广告类型      889 non-null object
合作方式      889 non-null object
广告尺寸      889 non-null object
广告卖点      889 non-null object
dtypes: float64(6), int64(1), object(6)
memory usage: 90.4+ KB
None

数据维度概况
除了渠道唯一标识,共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'

Step 3. 数据处理

Step 3.1 删除多余列

In [4]:

raw_data = raw_data.drop('Unnamed: 0', axis=1)

In [5]:

raw_data.head(5)

Out[5]:

渠道代号日均UV平均注册率平均搜索量访问深度平均停留时间订单转化率投放总时间素材类型广告类型合作方式广告尺寸广告卖点
0A2033.690.00710.02142.3071419.770.025820jpgbannerroi140*40打折
1A387178.700.00400.03242.0489157.940.003019jpgbannercpc140*40满减
2A38891.770.00220.05301.8771357.930.00264jpgbannercpc140*40满减
3A3891.090.00740.33824.2426364.070.015310jpgbannercpc140*40满减
4A3903.370.00280.17402.1934313.340.000730jpgbannercpc140*40满减

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

In [6]:

raw_data.describe().round(2).T # 打印原始数据基本描述性信息

Out[6]:

countmeanstdmin25%50%75%max
日均UV889.0540.851634.410.066.18114.18466.8725294.77
平均注册率889.00.000.000.000.000.000.000.04
平均搜索量889.00.030.110.000.000.000.011.04
访问深度889.02.173.801.001.391.792.2298.98
平均停留时间887.0262.67224.361.64126.02236.55357.984450.83
订单转化率889.00.000.010.000.000.000.000.22
投放总时间889.016.058.511.009.0016.0024.0030.00

Step 3.3 相关性分析

In [7]:

# 相关性分析
corr_matrix = raw_data.corr().round(2).T # 打印原始数据相关性信息
corr_matrix

Out[7]:

日均UV平均注册率平均搜索量访问深度平均停留时间订单转化率投放总时间
日均UV1.00-0.05-0.07-0.020.04-0.05-0.04
平均注册率-0.051.000.240.110.220.32-0.01
平均搜索量-0.070.241.000.060.170.13-0.03
访问深度-0.020.110.061.000.720.160.06
平均停留时间0.040.220.170.721.000.250.05
订单转化率-0.050.320.130.160.251.00-0.00
投放总时间-0.04-0.01-0.030.060.05-0.001.00

Step 4. 相关性热力图

日均UV平均注册率平均搜索量访问深度平均停留时间订单转化率投放总时间日均UV平均注册率平均搜索量访问深度平均停留时间订单转化率投放总时间−1−0.500.51相关性相关性热力图特征特征

Step 5. 数据再处理

Step 5.1 删除平均平均停留时间列

In [9]:

# 1 删除平均平均停留时间列
raw_data2 = raw_data.drop(['平均停留时间'],axis=1)

Step 5.2 类别变量取值

In [10]:

# 类别变量取值
cols=["素材类型","广告类型","合作方式","广告尺寸","广告卖点"]
for x in cols:
    data=raw_data2[x].unique()
    print("变量【{0}】的取值有:\n{1}".format(x,data))
    print("-·"*20)
变量【素材类型】的取值有:
['jpg' 'swf' 'gif' 'sp']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【广告类型】的取值有:
['banner' 'tips' '不确定' '横幅' '暂停']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【合作方式】的取值有:
['roi' 'cpc' 'cpm' 'cpd']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【广告尺寸】的取值有:
['140*40' '308*388' '450*300' '600*90' '480*360' '960*126' '900*120'
 '390*270']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【广告卖点】的取值有:
['打折' '满减' '满赠' '秒杀' '直降' '满返']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·

Step 5.3 字符串分类独热编码处理

In [11]:

# 字符串分类独热编码处理
cols = ['素材类型','广告类型','合作方式','广告尺寸','广告卖点'] 
model_ohe = OneHotEncoder(sparse=False)  # 建立OneHotEncode对象
ohe_matrix = model_ohe.fit_transform(raw_data2[cols])  # 直接转换
print(ohe_matrix[:2])
[[0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.
  0. 0. 0.]
 [0. 1. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
  0. 0. 0.]]

In [12]:

# 用pandas的方法
ohe_matrix1=pd.get_dummies(raw_data2[cols])
ohe_matrix1.head(5)

Out[12]:

素材类型_gif素材类型_jpg素材类型_sp素材类型_swf广告类型_banner广告类型_tips广告类型_不确定广告类型_暂停广告类型_横幅合作方式_cpc...广告尺寸_480*360广告尺寸_600*90广告尺寸_900*120广告尺寸_960*126广告卖点_打折广告卖点_满减广告卖点_满赠广告卖点_满返广告卖点_直降广告卖点_秒杀
00100100000...0000100000
10100100001...0000010000
20100100001...0000010000
30100100001...0000010000
40100100001...0000010000

5 rows × 27 columns

Step 5.4 数据标准化

In [13]:

# 数据标准化
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))
[[0.   0.18 0.02 0.01 0.12 0.66]
 [0.01 0.1  0.03 0.01 0.01 0.62]
 [0.   0.06 0.05 0.01 0.01 0.1 ]
 ...
 [0.01 0.01 0.   0.   0.   0.72]
 [0.05 0.   0.   0.   0.   0.31]
 [0.   0.   0.   0.53 0.   0.62]]

Step 5.5 合并所有维度

In [14]:

# # 合并所有维度
X = np.hstack((data_scaled, ohe_matrix))

Step 6. 建立KMeans聚类模型

In [15]:

# 通过平均轮廓系数检验得到最佳KMeans聚类模型
score_list = list()  # 用来存储每个K下模型的平局轮廓系数
silhouette_int = -1  # 初始化的平均轮廓系数阀值
for n_clusters in range(2, 8):  # 遍历从2到5几个有限组
    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('{:*^60}'.format('K值对应的轮廓系数:'))
print(np.array(score_list))  # 打印输出所有K下的详细得分
print('最优的K值是:{0} \n对应的轮廓系数是:{1}'.format(best_k, silhouette_int))
*************************K值对应的轮廓系数:*************************
[[2.         0.38655493]
 [3.         0.45757883]
 [4.         0.50209812]
 [5.         0.4800359 ]
 [6.         0.47761127]
 [7.         0.50079838]]
最优的K值是:4 
对应的轮廓系数是:0.5020981194788054

总体思想(评价指标)还是怎么聚才能使得簇内距离足够小,簇与簇之间平均距离足够大来评判。

Step 7. 聚类结果特征分析与展示

通过上面模型,我们其实给每个观测(样本)打了个标签clusters,即他属于4类中的哪一类:

In [16]:

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

Out[16]:

渠道代号日均UV平均注册率平均搜索量访问深度订单转化率投放总时间素材类型广告类型合作方式广告尺寸广告卖点clusters
0A2033.690.00710.02142.30710.025820jpgbannerroi140*40打折2
1A387178.700.00400.03242.04890.003019jpgbannercpc140*40满减2
2A38891.770.00220.05301.87710.00264jpgbannercpc140*40满减2
3A3891.090.00740.33824.24260.015310jpgbannercpc140*40满减2
4A3903.370.00280.17402.19340.000730jpgbannercpc140*40满减2

Step 7.1 每个类别下的样本数量和占比情况

In [17]:

# 计算每个聚类类别下的样本量和样本占比
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)
clusters    0    1    2   3
counts    313  349  154  73
##############################
clusters       0     1     2     3
percentage  0.35  0.39  0.17  0.08

Step 7.2 每个类别内部最显著的特征

In [18]:

# 计算各个聚类类别内部最显著特征值
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(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('{:*^60}'.format('每个类别主要的特征:'))
all_cluster_set = pd.concat((clustering_count, clustering_ratio, cluster_pd),axis=0)  # 将每个聚类类别的所有信息合并
all_cluster_set
*************************每个类别主要的特征:*************************

Out[18]:

0123
counts31334915473
percentage0.350.390.170.08
日均UV1390.01933.0152717.421904.37
平均注册率0.0030.0030.0050.003
平均搜索量0.1520.0640.0510.106
访问深度1.1685.9160.9470.943
订单转化率0.0170.0060.0070.009
投放总时间8.1998.778.5298.217
素材类型swfjpgjpgswf
广告类型不确定横幅bannertips
合作方式roicpccpccpm
广告尺寸600*90600*90308*388450*300
广告卖点打折直降满减打折

In [19]:

#各类别数据预处理
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)
       日均UV  平均注册率  平均搜索量   访问深度  订单转化率  投放总时间
0  1390.013  0.003  0.152  1.168  0.017  8.199
1   933.015  0.003  0.064  5.916  0.006  8.770
2  2717.419  0.005  0.051  0.947  0.007  8.529
3  1904.371  0.003  0.106  0.943  0.009  8.217
--------------------
[[2.56106801e-01 0.00000000e+00 1.00000000e+00 4.52443193e-02
  1.00000000e+00 0.00000000e+00]
 [0.00000000e+00 0.00000000e+00 1.28712871e-01 1.00000000e+00
  0.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 0.00000000e+00 8.04343455e-04
  9.09090909e-02 5.77933450e-01]
 [5.44358789e-01 0.00000000e+00 5.44554455e-01 0.00000000e+00
  2.72727273e-01 3.15236427e-02]]

In [21]:

labels = np.array(merge_data1.index)  # 设置要展示的数据标签
labels

Out[21]:

array(['日均UV', '平均注册率', '平均搜索量', '访问深度', '订单转化率', '投放总时间'], dtype=object)

Step 7.3 各聚类类别显著特征对比-雷达图

日均UV平均注册率平均搜索量访问深度订单转化率投放总时间−0.200.20.40.60.811.2第0类渠道第1类渠道第2类渠道第3类渠道各聚类类别显著特征对比

Step 8. 数据结论

从案例结果来看,所有的渠道被分为4各类别,每个类别的样本量分别为:154、313、349 、73,对应占比分别为:17%、35%、39%、8%。

通过雷达图可以清楚的知道:

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

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

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

类别4(索引为3类的渠道)
这类渠道各方面特征都不明显,各个流量质量和流量数量的指标均处于“中等”层次。不突出但是均衡,考虑在各场景下可以考虑在这个渠道投放广告。

  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暴躁的秋秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值