随着流量成本的提高,高质量的流量媒体越来越分散,相信以精细化运营为基础导向的流量运营工作,必然会增加对数据的依赖程度。如何降低流量成本、增加流量规模、提高广告转化效果成为企业的重要诉求。
本案例来自宋天龙老师的《python数据分析与数据化运营》第7章,主要介绍如何通过平均轮廓系数确定KMeans的最佳k值,将广告渠道数据划分类别,用雷达图展示各类别的特征差异,并对各类别广告渠道的广告效果进行深入分析。
一、案例背景某企业由于投放的广告渠道比较多,需要对其做广告效果分析以实现有针对性的广告效果测量和优化工作。
在开展研究之前的基本假设条件:广告渠道的范畴是什么?具体包括哪些渠道?——所有站外标记的广告类渠道
数据集时间选择哪个时间段?——最近90天的数据
数据集选择哪些维度和指标?——渠道代号、日均uv、平均注册率、平均搜索量、访问深度、平均停留时间、订单转化率、投放总时间、素材类型、广告类型、合作方式、广告尺寸、广告卖点。
专题分析要解决什么问题?——将广告分类并找出其重要特征,为接下来的业务探讨和数据分析提供支持。
某企业广告渠道分布(来自网络)
本文的主要应用技术:一是如何基于最优数据尺度确定最佳k值,该技术实现的思路是:最优的聚类类别划分从数据特征上看是类内距离最小化的同时类间的距离最大化,可以使用平均轮廓系数作为指标评估,通过枚举每个k计算平均轮廓系数并得到最优值。
二是通过极坐标系的设置方式输出雷达图。
二、案例数据
数据来自某企业的营销部门,该数据基于营销数据、网站分析系统数据和运营系统数据结合起来。数据概况如下:维度数:除渠道唯一标记外,共有12个维度
数据记录数:889
是否有na值:有
是否有异常值:有
13个字段的详细说明:渠道代号:业务方统一命名规划的唯一渠道标志
日均uv:每天的平均独立访客,从一个渠道中带来的一个访客,即使一天中到达多次都统计为1次。
平均注册率:日均注册的用户数/平均每天的访问量
平均搜索量:平均每个访问的搜索次数
访问深度:总页面浏览量/平均每天的访问量
平均停留时间:总停留时间/平均每天的访问量
订单转化率:总订单数量/平均每天的访问量
投放总时间:每个广告媒介在站外投放的天数
素材类型:广告素材包括jpg,gif,swf,sp
广告类型:广告投放类型,包括banner, tips、横幅、通栏、暂停以及不确定(不知道是何种形式)
合作方式:包括roi, cpc, cpm和cpd
广告尺寸:每个广告投放的尺寸大小,包括140x40、308x388、450x300、600x90、480x360、960x126、900x120、390x270
广告卖点:广告素材主要的卖点诉求信息,包括打折、满减、满赠、秒杀、直降、满返。
三、python实现
'''步骤1 导入库'''
import pandas as pd
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from sklearn.preprocessing import MinMaxScaler
from sklearn.cluster import KMeans
from sklearn import metrics
import matplotlib.pyplot as plt
'''步骤2 读取数据'''
raw_data = pd.read_table('ad_performance.txt', delimiter='\t')
'''步骤3 读取数据'''
raw_data.head(2) #查看前2条数据
pd.DataFrame(raw_data.dtypes).T #查看数据类型
pd.DataFrame(raw_data.isnull().sum()).T #查看缺失值
raw_data.describe().round(2).T #查看数据描述性统计信息
raw_data.corr().round(2).T #使用corr()做相关性分析数据描述性统计信息日均UV的波动非常大,说明了不同渠道间特征差异非常明显。(业务确认:由于广告的流量型数据,很多广告的流量爆发明显,因此渠道间缺失有非常大的差异性,这些差异性应该保留,不能作为异常值处理)
平均停留时间的有效数据只有887,比其他数据少2条 (业务确认:该字段由于统计缺失导致数据丢失,可以使用均值法做填充)
平均注册率、平均搜索量、订单转化率的多个统计值(例如最小值、25%分位数等)都为0,看似数据不太正常。(再次数据验证:这是由于在打印输出过程中保留了2位小数,而这几个统计量的数据本身就非常小,将其通过round(3)保留3位小数后就能正常显示)
'''步骤4 数据预处理'''
#1 缺失值替换
data_fillna = raw_data.fillna(raw_data['平均停留时间'].mean()) #用均值替换缺失值
#2 字符串分类转整数类型
#定义要转换的数据
conver_cols = ['素材类型','广告类型','合作方式','广告尺寸','广告卖点']
convert_matrix = data_fillna[conver_cols]
lines = data_fillna.shape[0]
dict_list = []
unique_list = []
#获得所有列的唯一值列表
for col_name in conver_cols:
cols_unique_value = data_fillna[col_name].unique().tolist()
unique_list.append(cols_unique_value)
#将每条记录的具体值跟其在唯一值列表中的索引做映射
for line_index in range(lines):
each_record = convert_matrix.iloc[line_index]
for each_index, each_data in enumerate(each_record):
list_value = unique_list[each_index]
each_record[each_index] = list_value.index(each_data)
each_dict = dict(zip(conver_cols, each_record))
dict_list.append(each_dict)
#使用DictVectorizer将字符串转换为整数
model_dvtransform = DictVectorizer(sparse=False, dtype=np.int64)
data_dictvec = model_dvtransform.fit_transform(dict_list)
#3 数据标准化
scale_matrix = data_fillna.iloc[:, 1:8] #获得要转换的举证
minmax_scaler = MinMaxScaler() #
data_scaled = minmax_scaler.fit_transform(scale_matrix)
#4 合并所有输入维度
X = np.hstack((data_scaled, data_dictvec))预处理后的数据
'''步骤5 通过平均轮廓系数检验得到最佳KMeans聚类模型'''
score_list = list() #用来存储每个k下模型的平均轮廓系数
silhouette_int = -1 #初始化的平均轮廓阀值
for n_clusters in range(2, 10): #遍历从2-10几个有限组
model_kmeans = KMeans(n_clusters=n_clusters, random_state=0) # 建立聚类模型对象
cluster_labels_tmp = model_kmeans.fit_predict(X) #训练聚类模型
silhouette_tmp = metrics.silhouette_score(X, cluster_labels_tmp)#得到每个k下的平均轮廓系数
if silhouette_tmp > silhouette_int:#如果平均轮廓系数更高
best_k = n_clusters #将最好的k存储下来
silhouette_int = silhouette_tmp #将最好的平均轮廓得分存储下来
best_kmeans = model_kmeans #将最好的模型存储下来
cluster_labels_k = cluster_labels_tmp #将最好的聚类标签存储下来
score_list.append([n_clusters, silhouette_tmp])
print('{:-^60}'.format('K value and silhouette summary:'))
print(np.array(score_list)) #打印输出k下的详细得分
print('Best K is:{0} with average silhouette of {1}'.format(best_k, silhouette_int.round(4)))
结果显示了不同k下的平均轮廓得分。就经验看,如果平均轮廓得分值小于0,意味着聚类效果不佳;如果值大于0且小于0.5,说明聚类效果一般;如果值大于0.5,则说明聚类效果比较好。本案例中,k=4时,得分0.5697,说明效果较好。
'''步骤6 针对聚类结果的特征分析'''
#6.1 将原始数据与聚类标签整合
cluster_labels = pd.DataFrame(cluster_labels_k, columns=['clusters']) #获得训练集下的标签信息
merge_data = pd.concat((data_fillna, cluster_labels), axis=1) #将原始处理过的数据跟聚类标签整合
#6.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'})
#6.3 计算各个聚类类别内部最显著特征值
cluster_features = [] #存储最终合并后的所有特征信息
for line in range(best_k): #读取每个类索引
label_data = merge_data[merge_data['clusters'] == line] #获得特定类的数据
part1_data = label_data.iloc[:, 1:8] #获得数值型数据特征
part1_desc = part1_data.describe().round(3) #描述性统计信息
merge_data1 = part1_desc.iloc[2, :] #得到数值型特征的均值
part2_data = label_data.iloc[:, 8:-1] #获得字符型数据特征
part2_desc = part2_data.describe(include='all') #获得描述统计信息
merge_data2 = part2_data.iloc[2, :] #获得字符型特征的最频繁值
merge_line = pd.concat((merge_data1, merge_data2), axis=0) #按行合并
cluster_features.append(merge_line) #将每个类别下的数据特征追加到列表
#6.4 输出完整的类别特征信息
cluster_pd = pd.DataFrame(cluster_features).T #将列表转化为矩阵
print('{:-^60}'.format('Detailed features for all clusters:'))
all_cluster_set = pd.concat((clustering_count, clustering_ratio, cluster_pd), axis=0)#将每个列别的所有信息合并
print(all_cluster_set)
'''步骤7 各类别显著值特征对比'''
#7.1 各类别数据预处理(标准化)
num_sets = cluster_pd.iloc[:6, :].T.astype(np.float64) #获取要展示的数据
num_sets_max_min = minmax_scaler.fit_transform(num_sets) #获得标准化后的数据
#7.2 画布基本设置
fig = plt.figure(figsize=(7,7))
ax = fig.add_subplot(111, polar=True)
labels = np.array(merge_data1.index[:-1])
color_list = ['r','g','b','c']
angles = np.linspace(0, 2*np.pi, len(labels), endpoint=False)
angles = np.concatenate((angles, [angles[0]]))
#7.3 画雷达图
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=color_list[i], label=i,linewidth=2.5)
ax.set_thetagrids(angles*180/np.pi, labels, fontproperties='SimHei',fontsize=14)
ax.set_title('各聚类类别显著特征对比',fontproperties='SimHei',fontsize=18)
ax.set_rlim(-0.2, 1.2)
plt.legend(loc=0)
#plt.show()各聚类类别显著特征对比
四、数据结论分析
(1)初步分析
从结果看,所有的渠道被分为4个类别,每个类别的样本量分别为411、297、27、154,对应的占比分别46%、33%、3%、17%。第三个类别样本量最少。
结合雷达图,可以清晰地看到不同类别广告媒体的特征倾向:聚类0:各方面的特征都不明显,换句话说就是效果比较平庸,没有明显的优势或短板。但这些“中庸”的广告媒体却构成了整个广告的主体。
聚类1:这类广告媒体在访问深度、平均停留时间、订单转化率集平均搜索量等流量质量的特征上表现较好,除了注册转换率较低外,该类渠道各方面比较均衡。更重要的是该类媒体的数量占据了33%,因此是一类规模较大且综合效果较好的媒体。
聚类2:这类广告媒体跟聚类1非常类似,并且相对聚类2的典型特征表现更好,但综合其只占3%的媒体数量,属于少量的“精英“类渠道。
聚类3:这类渠道跟其他几类渠道有个明显的特征区隔,其日均uv和平均注册率非常突出,证明这是一类“引流”+“拉新”的渠道;而其他的流量质量方面表现却比较差。
(2)深入分析聚类0:该广告渠道各方面表现均比较一般,因此需要业务部门重点考虑其投放的实际价值。
聚类1:这类广告渠道的短板是日均uv和平均注册率,因此该类媒体无法为企业带来大量的流量以及新用户。这类广告的特质适合用户转化,尤其是有关订单转化的提升。
聚类2:这类广告渠道跟聚类1的特征非常类似,也适合做订单转换的提升,并且由于其实际效果更好,因此效果会更加明显。
聚类3:这类渠道更符合广告本身“广而告之“的基础诉求,因此适合在大规模广告宣传和引流时使用,尤其对于新用户的注册转化上的效果非常明显,也适合“拉新”使用。
五、案例应用和部署
对于以上四类广告媒体,需要针对其不同的特征做针对性的运营应用:
对于聚类0的广告媒体,在资金不足或优化广告结构时,重点考虑其取舍问题;
对于聚类1和聚类2的广告媒体,优化应用的方向有两个:一是增加对于注册转化效果的优化效应,重点从人群匹配、注册引导、注册激励等方面加强运营。在该过程中,打折、直降等优惠宣传点可以重点突出,广告素材本身以较大中等广告为主,例如900x120.
二是对于整体广告效果的支撑价值。考虑到其流量规模的局限性,这类渠道更加适合在广告结构中作为一类具有流量质量支撑价值的角色,可以用来提升全局广告的订单质量效果,因此是一类非常关键的质量渠道。尤其是聚类2的广告媒体其质量指标表现非常优异,应该优先考虑投放组合。
对于聚类3的广告媒体,其可以作为在每次大型促销活动时引流的骨干,因为这更符合本身在全部推给媒体中的角色定位。对于这类媒体的单位流量成本需要重点关注,实际投放过程中可以满减为促销点、通过较大尺寸(如308*338)的广告类型做引导流量点击。