背景
某企业由于投放的广告渠道比较多,需要对其做广告效果分析以实现有针对性的广告效果测量和优化工作。
基本预设条件:
- 广告渠道的范畴是什么?具体包括哪些渠道?——所有站外标记的广告类渠道(以ad_开头)。
- 数据集时间选择哪个时间段?——最近90天的数据。
- 数据集选择哪些维度和指标?——渠道代号、日均UV、平均注册率、平均搜索量、访问深度、平均停留时间、订单转化率、投放总时间、素材类型、广告类型、合作方式、广告尺寸、广告卖点。
- 专题分析要解决什么问题?——将广告分类并找出其重点特征,为接下来的业务讨论和数据分析提供支持。
数据来源
案例数据来自某企业的营销部门数据,该数据基于营销数据、网站分析系统数据和运营系统数据总结而来。以下是数据概况:
- 维度数:除了渠道唯一标记之外,共12个维度
- 数据记录数:889
- 是否有NA值:有
- 是否有异常值:有
以下是本数据集的13个字段的详细说明:
- 渠道代号:业务方统一命名规划的唯一渠道标志
- 日均UV :每天的平均独立访客,从一个渠道中带来的一个访客即使一天中到达多次都统计为1次
- 平均注册率:日均注册的用户数量/平均每天的访问量
- 平均搜索量:平均每个访问的搜索次数
- 访问深度:总页面浏览量/平均每天的访问量
- 平均停留时间:总停留时间/平均每天的访问量
- 订单转化率:总订单数量/平均每天的访问量
- 投放总时间:每个广告媒介在站外投放的天数
- 素材类型:广告素材类型,包括jpg、gif、swf、sp
- 广告类型:广告投放类型,包括banner、tips、横幅、通栏、暂停以及不确定(不知道到底是何种形式)
- 合作方式:广告合作方式,包括roi、cpc、cpm和cpd
- 广告尺寸:每个广告投放的尺寸大小,包括140*40、308*388、450*300、600*90、480*360、960*126、900*120、390*270
- 广告卖点:广告素材上主要的卖点诉求信息,包括打折、满减、满赠、秒杀、直降、满返
分析过程
步骤1 导入库
import sys
reload(sys)
sys.setdefaultencoding(‘utf-8’)
import pandas as pd
import numpy as np
from sklearn.feature_extraction import DictVectorizer # 字符串分类转整数分类库
from sklearn.preprocessing import MinMaxScaler # MinMaxScaler库
from sklearn.cluster import KMeans # KMeans模块
from sklearn import metrics # 导入sklearn效果评估模块
import matplotlib.pyplot as plt # 图形库
步骤2 读取数据,使用pandas的read_table方法读取txt文件,分隔符为TAB。
raw_data = pd.read_table(‘ad_performance.txt’, delimiter=’t’)
步骤3 数据审查和校验,该步骤包括多个环节:查看前2条数据、查看数据类型分布、查看缺失值、查看数据描述性统计信息、相关性分析等。
使用head(2)方法查看前2条数据
print (‘{:*^60}’.format(‘Data overview:’))
print (raw_data.head(2)) # 打印输出前2条数据
从返回结果看,原始数据能正常识别且没有异常信息,前2条数据如下:
***********************Data overview:***********************
渠道代号 日均UV 平均注册率 平均搜索量 访问深度 平均停留时间 订单转化率 投放总时间 素材类型 广告类型
0 A203 3.69 0.0071 0.0214 2.3071 419.77 0.0258 20.0 jpg banner
1 A387 178.70 0.0040 0.0324 2.0489 157.94 0.0030 19.0 jpg banner
合作方式 广告尺寸 广告卖点
0 roi 140*40 打折
1 cpc 140*40 满减
使用dtypes方法查看数据类型分布,由于行比较多,因此这里使用pd.DataFrame方法将其结果转换为数据框,然后通过.T做转置。
print (‘{:*^60}’.format(‘Data dtypes:’))
print (pd.DataFrame(raw_data.dtypes).T) # 打印数据类型分布
从数据类型结果来看,各个字段类型都能别正确识别,结果如下:
************************Data dtypes:************************
渠道代号 日均UV 平均注册率 平均搜索量 访问深度 平均停留时间 订单转化率 投放总时间
0 object float64 float64 float64 float64 float64 float64 float64
素材类型 广告类型 合作方式 广告尺寸 广告卖点
0 object object object object object
使用isnull().sum()查看所有字段中具有缺失值数据的统计量,同样使用pd.DataFrame将结果转换为数据框,然后通过.T做转置。
print (‘{:*^60}’.format(‘ NA counts:’))
print (pd.DataFrame(raw_data.isnull().sum()).T) # 查看缺失值情况
从结果中发现,在“平均停留时间”字段中有2个缺失值。结果如下:
************************ NA counts:*************************
渠道代号 日均UV 平均注册率 平均搜索量 访问深度 平均停留时间 订单转化率 投放总时间 素材类型 广告类型 合作方式
0 0 0 0 0 0 2 0 0 0 0 0
广告尺寸 广告卖点
0 0 0
使用describe()查看数据描述性统计信息,并使用round(2)保留2位小数,最后使用.T将其转置。
print (‘{:*^60}’.format(‘Data DESC:’))
print (raw_data.describe().round(2).T) # 打印原始数据基本描述性信息
如下的描述性统计结果中,反映了3个信息点:
- 日均UV的数据波动非常大,说明了不同渠道间的特征差异非常明显。
- 平均停留时间的有效数据(非空数据)只有887,比其他数据少2条,这也印证了上述缺失值统计结果。
- 平均注册率、平均搜索量、订单转化率的多个统计量(例如最小值、25%分位数等)都为0,看似数据不太正常。
*************************Data DESC:*************************
count mean std min 25% 50% 75% max
日均UV 889.0 540.85 1634.41 0.06 6.18 114.18 466.87 25294.77
平均注册率 889.0 0.00 0.00 0.00 0.00 0.00 0.00 0.04
平均搜索量 889.0 0.03 0.11 0.00 0.00 0.00 0.01 1.04
访问深度 889.0 2.17 3.80 1.00 1.39 1.79 2.22 98.98
平均停留时间 887.0 262.67 224.36 1.64 126.02 236.55 357.98 4450.83
订单转化率 889.0 0.00 0.01 0.00 0.00 0.00 0.00 0.22
投放总时间 889.0 16.05 8.51 1.00 9.00 16.00 24.00 30.00
使用corr()方法做相关性分析
print (‘{:*^60}’.format(‘Correlation analysis:’))
print (raw_data.corr().round(2).T) # 打印原始数据相关性信息
通过相关性结果分析,12个特征中平均停留时间和访问深度的相关系数为0.72,这两个指标具有较高的相关性,但特征也不是非常明显;其他特征之间的相关性关系都不突出。
步骤4 数据预处理,本步骤主要涉及到缺失值替换、字符串分类转换为整数分类、数据标准化、数据合并等操作。
步骤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]) # 将每次K及其得分追加到列表
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值下详细信息以及最后K值和最优评论轮廓得分,返回数据如下:
**************K value and silhouette summary:***************
[[ 2. 0.46692821]
[ 3. 0.54904646]
[ 4. 0.56968547]
[ 5. 0.48186604]
[ 6. 0.45477667]
[ 7. 0.48204261]
[ 8. 0.50447223]
[ 9. 0.52697493]]
Best K is:4 with average silhouette of 0.5697
上述结果显示了不同K下的平均轮廓得分。就经验看,如果平均轮廓得分值小于0,意味着聚类效果不佳;如果值大约0且小于0.5,那么说明聚类效果一般;如果值大于0.5,则说明聚类效果比较好。本案例在K=4时,得分为0.5697,说明效果较好。
步骤6 针对聚类结果的特征分析。
part1 将原始数据与聚类标签整合
cluster_labels = pd.DataFrame(cluster_labels_k, columns=[‘clusters’]) # 获得训练集下的标签信息
merge_data = pd.concat((data_fillna, cluster_labels), axis=1) # 将原始处理过的数据跟聚类标签整合
part2 计算每个聚类类别下的样本量和样本占比
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’}) # 计算每个聚类类别的样本量占比
part3 计算各个聚类类别内部最显著特征值
cluster_features = [] # 空列表,用于存储最终合并后的所有特征信息
for line in range(best_k): # 读取每个类索引
label_data = merge_data[merge_data[‘clusters’] == line] # 获得特定类的数据
part1_data = label_data.ix[:, 1:8] # 获得数值型数据特征
part1_desc = part1_data.describe().round(3) # 得到数值型特征的描述性统计信息
merge_data1 = part1_desc.ix[2, :] # 得到数值型特征的均值
part2_data = label_data.ix[:, 8:-1] # 获得字符串型数据特征
part2_desc = part2_data.describe(include=’all’) # 获得字符串型数据特征的描述性统计信息
merge_data2 = part2_desc.ix[2, :] # 获得字符串型数据特征的最频繁值
merge_line = pd.concat((merge_data1, merge_data2), axis=0) # 将数值型和字符串型典型特征沿行合并
cluster_features.append(merge_line) # 将每个类别下的数据特征追加到列表
part4 输出完整的类别特征信息。
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)
打印输出如下结果:
************Detailed features for all clusters:*************
clusters 0 1 2 3
counts 411 297 27 154
percentage 0.46 0.33 0.03 0.17
日均UV 1369.81 1194.69 1263.03 2718.7
平均注册率 0.003 0.003 0.003 0.005
平均搜索量 0.082 0.144 0.151 0.051
访问深度 0.918 5.728 9.8 0.948
平均停留时间 165.094 285.992 374.689 104.14
订单转化率 0.009 0.016 0.017 0.007
投放总时间 8.462 8.57 7.996 8.569
素材类型 swf jpg swf jpg
广告类型 不确定 不确定 通栏 banner
合作方式 cpc cpc cpc cpc
广告尺寸 600*90 600*90 900*120 308*388
广告卖点 打折 直降 打折 满减
步骤7 各类别显著数值特征对比。最后显示图像如下:
数据结论
从案例结果来看,所有的渠道被分为4个类别,每个类别的样本量分别为411、297、27、154,对应的占比分别为46%、33%、3%、17%。第三个类别样本量较少。
不同类别广告媒体的特征倾向:
聚类1(索引值为0):各方面的特征都不明显,换句话说就是效果比较平庸,没有明显的优势或短板。但这些“中庸”的广告媒体却构成了整个广告的主体。
聚类2(索引值为1):这类广告媒体在访问深度、平均停留时间、订单转化率以及平均搜索量等流量质量的特征上的表现较好,除了注册转化率较低外,该类渠道各方面比较均衡。更重要的是该类媒体的数量占据了33%的数量,因此是一类规模较大且综合效果较好的媒体。
聚类3(索引值为2):这类广告媒体跟聚类2非常类似,并且相对聚类2的典型特征表现更好,但综合其只有3%的媒体数量,属于少量的“精英”类渠道。
聚类4(索引值为3):这类渠道跟其他几类渠道有个明显的特征区隔,其日均UV和平均注册率非常突出,证明这是一类“引流”+“拉新”的渠道;而其他的流量质量方面的表现却比较差。
深入分析
综合初步分析的结果,再结合输出各渠道详细数据:
聚类1的广告渠道各方面表现均比较一般,因此需要业务部门重点考虑其投放的实际价值。
聚类2的广告渠道的短板是日均UV和平均注册率,因此该类媒体无法为企业带来大量的流量以及新用户。这类广告的特质适合用户转化,尤其是有关订单转化的提升。
聚类3的广告渠道跟聚类2的特质非常类似,也适合做订单转化的提升,并且由于其实际效果更好,因此效果会更佳明显。
聚类4的广告渠道更佳符合广告本身“广而告之”的基础诉求,因此适合在大规模的广告宣传和引流时使用,尤其对于新用户的注册转化上的效果非常明显,也适合“拉新”使用。
本案例选自《Python数据分析与数据化运营》(宋天龙),非常具有实用价值和学习价值。