一、 项目背景
大数据时代的背景下,广告投放成为了互联网各个行业中运营推广的主流。对于电商行业来说,广告投放的效果,取决于广告投放后为平台带来了多少转化。要有转化,首先就要有流量(点击),因此,如何对广告进行精准投放,提高广告的点击率,进而实现精准营销就显得尤为重要。
下面我们以阿里巴巴提供的淘宝展示广告点击率数据集为例进行分析。
二、数据来源
https://tianchi.aliyun.com/dataset/dataDetail?dataId=56.
Ali_Display_Ad_Click是阿里巴巴提供的一个淘宝展示广告点击率数据集。
三、数据简介
1.原始样本骨架raw_sample:
从淘宝网站中随机抽样了114万用户8天内的广告展示/点击日志(2600万条记录),构成原始的样本骨架。
字段说明如下:
字段 | 说明 |
---|---|
user_id | 脱敏过的用户ID |
adgroup_id | 脱敏过的广告单元ID |
time_stamp | 时间戳 |
pid | 资源位 |
noclk | 为1代表没有点击;为0代表点击 |
clk | 为0代表没有点击;为1代表点击 |
由于数据量较大,本文中只截取其中前200W条数据进行分析。
2.用户基本信息表user_profile:
本数据集涵盖了raw_sample中全部用户的基本信息。字段说明如下:
字段 | 说明 |
---|---|
user_id | 脱敏过的用户ID |
cms_segid | 微群ID |
cms_group_id | cms_group_id |
final_gender_code | 性别 1:男,2:女 |
age_level | 年龄层次 |
pvalue_level | 消费档次,1:低档,2:中档,3:高档 |
shopping_level | 购物深度,1:浅度,2:中度,3:深度 |
occupation | 是否大学生 ,1:是,0:否 |
new_user_class_level | 城市层级 |
3.广告基本信息表ad_feature:
本数据集涵盖了raw_sample中全部广告的基本信息。字段说明如下:
字段 | 说明 |
---|---|
adgroup_id | 脱敏过的广告ID |
cate_id | 脱敏过的商品类目ID |
campaign_id | 脱敏过的广告计划ID |
customer_id | 脱敏过的广告主ID |
brand | 脱敏过的品牌ID |
price | 宝贝的价格 |
其中一个广告ID对应一个商品(宝贝),一个宝贝属于一个类目,一个宝贝属于一个品牌。
四、数据预处理
4.1 数据导入
1. 导入类库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set_style('darkgrid') # 设置网格类型,默认
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 用来正常显示负号
plt.rcParams['axes.unicode_minus'] = False
2. 导入数据集
# 用户基本信息表
user_data = pd.read_csv('D:/淘宝展示广告点击率预估数据集/user_profile.csv') # 读取数据
print(user_data.shape) # (1061768, 9):说明该表中一共有1061768行、9列
user_data.head() # 输出数据前5行
# 广告基本信息表
ads = pd.read_csv('D:/淘宝展示广告点击率预估数据集/ad_feature.csv')
print(ads.shape) # (846811, 6):说明该表中一共有846811行、6列
ads.head()
# 从原始样本骨架数据raw_sample中选取200W数据作为样本数据
raw_sample = pd.read_csv('D:/淘宝展示广告点击率预估数据集/raw_sample.csv',nrows=2000000)
# 将截取的前200W行数写入sample_data.csv文件中
raw_sample.to_csv('D:/淘宝展示广告点击率预估数据集/sample_data.csv',index=False,sep=',')
dataset = pd.read_csv('D:/淘宝展示广告点击率预估数据集/sample_data.csv') # 读取数据
print(dataset.shape) # (2000000, 6):说明该表中一共有2000000行、6列
dataset.head()
从上到下依次为用户基本信息表user_data、广告基本信息表ads、样本数据表dataset,如下图所示:
4.2 缺失值处理
1. 缺失值检测
# 获取用户基本信息表user_data每列的缺失值占比
user_data_null = user_data.isnull().sum()/len(user_data)*100
user_data_null = user_data_null.drop(user_data_null[user_data_null==0].index).sort_values(ascending=False) # 将缺失值占比从高到低排序
missing_data = pd.DataFrame({'Missing Ratio(%)':user_data_null})
print(f'user_data含有缺失值的属性个数:{len(user_data_null)}')
print(missing_data)
print('-' * 60)
# 获取广告基本信息表ads每列的缺失值占比
ads_null = ads.isnull().sum()/len(ads)*100
ads_null = ads_null.drop(ads_null[ads_null==0].index).sort_values(ascending=False) # 将缺失值占比从高到低排序
missing_data = pd.DataFrame({'Missing Ratio(%)':ads_null})
print(f'ads含有缺失值的属性个数:{len(ads_null)}')
print(missing_data)
print('-' * 60)
# 获取样本数据表dataset每列的缺失值占比
dataset_null = dataset.isnull().sum()/len(dataset)*100
dataset_null = dataset_null.drop(dataset_null[dataset_null==0].index).sort_values(ascending=False) # 将缺失值占比从高到低排序
missing_data = pd.DataFrame({'Missing Ratio(%)':dataset_null})
print(f'dataset含有缺失值的属性个数:{len(dataset_null)}')
print(missing_data)
分析获取三个表中每个表中有缺失值的属性个数以及该属性中的缺失值占比,输出结果如下图所示:
由结果明显可知:
用户基本信息表user_data有2个属性含缺失值:pvalue_level(缺失54.24%)、new_user_class_level(缺失32.49%);
广告基本信息表ads有1个属性含缺失值:brand(缺失29.09%);
样本数据表dataset不含缺失值。
所以只需对 user_data、ads 两个表进行缺失值填充。
2. 缺失值填充
缺失值填充说明:
1.user_data表
‘pvalue_level’(消费档次:1低2中3高):通过KNN算法(基于K个最近邻的填充算法)进行预测填充;
'new_user_class_level '(城市层次):该属性值为分类属性,对其进行众数填充。
2.ads表
‘brand’(品牌ID):由于该数据为id类数据,填充其上下条数据的值,在本文中使用的是填充上一条数据的值。
# 对 'new_user_class_level '进行众数填充
from sklearn.impute import SimpleImputer # 导入缺失值处理所需的库impute.SimpleImputer
# 使用reshape(-1,1)对数据升维,原本是一维,但在sklearn当中的特征矩阵必须是二维的
city_level = user_data.loc[:,'new_user_class_level '].values.reshape(-1,1) # loc:对索引名进行切片
si = SimpleImputer(strategy = 'most_frequent') # 实例化,使用众数填补
user_data.loc[:,'new_user_class_level '] = si.fit_transform(city_level) # fit_transform一步训练导出结果
user_data.info()
输入结果如下图,可观察到 'new_user_class_level ’ 列中已经不含缺失值:
# 调整数据集user_data的列顺序:将'pvalue_level'列调至最后一列
columns = ['userid', 'cms_segid', 'cms_group_id', 'final_gender_code', 'age_level',
'shopping_level', 'occupation', 'new_user_class_level ','pvalue_level']
user_data = user_data[columns]
"""
按'pvalue_level'列值是否为空对数据集user_data进行分离
"""
# 'pvalue_level'列值为空
pvalue_null = user_data.loc[user_data['pvalue_level'].isnull().values == True]
# 'pvalue_level'列值为空
pvalue_nonull = user_data.loc[user_data['pvalue_level'].isnull().values == False]
"""
对数据集pvalue_null和pvalue_nonull
分离训练集和测试集
X_train_user:'pvalue_level'列以外的,且'pvalue_level'列值不为0的部分
y_train_user:'pvalue_level'列中的,且值不为0
X_test_user:'pvalue_level'列以外的,且值为0
y_test_user:'pvalue_level'列中的,且值为0
"""
X_train_user,y_train_user = pvalue_nonull.iloc[:,:-1],pvalue_nonull.iloc[:,-1]
X_test_user,y_test_user = pvalue_null.iloc[:,:-1],pvalue_null.iloc[:,-1]
# 运用K最近邻(k-Nearest Neighbor,KNN)分类算法对'pvalue_level'列预测
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=3,weights='distance') # 根据消费档次1浅2中3深分为3类,weights='distance'表示用欧氏距离进行相似度衡量
knn.fit(X_train_user,y_train_user) # 训练数据集
y_test_user = knn.predict(X_test_user) # 导出结果
y_test_user # 输出结果为:array([2., 2., 2., ..., 1., 2., 2.])
"""
对KNN算法预测的数据进行整理合并填充,将整理后的数据写入新表user
"""
y_test_user = pd.DataFrame(y_test_user) # 将y_test_user由array转换成DataFrame结构
y_test_user.columns = ['pvalue_level'] # 修改y_test_user的列名为'pvalue_level'
X_test_user.reset_index(drop=True,inplace=True) # 重置X_test_user索引
# X_test_user.drop('index',axis=1,inplace=True) # 删除上述操作生成的原index索引列
pvalue_null = pd.concat([X_test_user,y_test_user],axis=1) # 横向合并X_test_user,y_test_user两个表
user = pd.concat([pvalue_nonull,pvalue_null],ignore_index=False) # 纵向合并pvalue_nonull,pvalue_null表为一个user新表,并重置索引值
user.to_csv('D:/淘宝展示广告点击率预估数据集/user.csv',index=False,sep=',') # 将user表数据写入user.csv文件中
user = pd.read_csv('D:/淘宝展示广告点击率预估数据集/user.csv') # 读取user数据
user.info()
输出结果如下,可以观察到user数据集已经不含缺失值:
# 用前一个数据对'brand'缺失的数据进行填充
ads.fillna(method='pad',inplace=True)
ads.info()
4.3 数据合并
将dataset、user、ads三个表以表dataset为骨架,分别基于主键userid、adgroup_id进行合并封装,合并为新表data,以便同时构建用户特征 、广告特征来预测分析用户对广告点击概率。
"""
合并表user、ads、dataset为ads_user_dataset
将合并后的数据写入data.csv中并读取
"""
print(f'dataset表的维度:{dataset.shape}')
print(f'user表的维度:{user.shape}')
print(f'ads表的维度:{ads.shape}')
# 修改表dataset中列名user为userid,以便后面基于主键连接
dataset.rename(columns={'user':'userid'},inplace=True)
dataset.head()
# 将数据集dataset与用户基本信息表user合并,基于主键userid,how='right'表示以右边表为基准连接
user_dataset = pd.merge(user,dataset,on='userid',how='right')
print(f'user_dataset表的维度:{user_dataset.shape}')
# 将数据集与广告基本信息表ads合并,基于主键adgroup_id,how='right'表示以右边表为基准连接
ads_user_dataset = pd.merge(ads,user_dataset,on='adgroup_id',how='right')
print(f'ads_user_dataset表的维度:{ads_user_dataset.shape}')
ads_user_dataset.to_csv('D:/淘宝展示广告点击率预估数据集/data.csv',index=False,sep=',')
data = pd.read_csv('D:/淘宝展示广告点击率预估数据集/data.csv')
合并后各表的数据维度如下图所示:
将用户特征数据user、广告特征数据ads以及样本数据dataset进行合并后,再对合并后的数据集data进行缺失值分析,代码如下:
# 获取data每列的缺失值占比
data_null = data.isnull().sum()/len(dataset)*100
data_null = data_null.drop(data_null[data_null==0].index).sort_values(ascending=False) # 将缺失值占比从高到低排序
missing_data = pd.DataFrame({'Missing Ratio(%)':data_null})
print(f'dataset含有缺失值的属性个数:{len(data_null)}')
print(missing_data)
根据输出结果可知:data中共含有8个属性的缺失值,且这8个含缺失值的属性全都为用户特征数据集user中的属性,同时这8个属性的缺失值占比均为5.81%。据此我们可以推断出应该是在样本数据data存在,但在用户特征表user中不存在这些缺失值的userid。
因此这些含有缺失值的数据对我们的分析没有太大意义,且缺失占比较小,直接将含有缺失值的行删除即可。
代码如下:
# 删除data中含有缺失值的行,直接在原数据上操作
data.dropna(axis=0, how='any',inplace=True)
再次输入上面获取data每列的缺失值占比
的代码块,查看缺失值的处理情况,结果如下图:
输入data.shape
查看删除缺失值后data的数据维度,得到结果:(1883809, 19)
4.4 时间戳数据处理
原数据提供的是时间戳形式,我们需要将其转换为日期和时间形式以便分析,从转换后的数据中分别提取日期、时间、小时组成三个新的特征列。
# 将数据中的时间戳形式转换为日期和时间形式
import datetime
import time
data['time_stamp']=pd.to_datetime(data['time_stamp'],unit='s')
data['time_stamp']
# 从转换后的数据中分别提取:日期、时间、小时,组成新的列
data['date'] = data['time_stamp'].dt.date
data['time'] = data['time_stamp'].dt.time
data['hour'] = data['time_stamp'].dt.hour
# 调整数据集data的列顺序:将'data'、'time'、'hour'这三列数据调至'time_stamp'列后
columns = ['adgroup_id', 'cate_id', 'campaign_id', 'customer', 'brand', 'price',
'userid', 'cms_segid', 'cms_group_id', 'final_gender_code', 'age_level',
'shopping_level', 'occupation', 'new_user_class_level ', 'pvalue_level',
'time_stamp', 'date', 'time', 'hour', 'pid', 'nonclk', 'clk']
data = data[columns]
4.5 删除不需要的列
# 删除列'time_stamp'、'time'以及'nonclk'
data = data.drop(['time_stamp','time','nonclk'],axis=1)
五、分析目标
提高广告的点击率以实现精准营销
六、分析过程
本文中画图主要基于Pyecharts库实现
由于以下分析画图代码过长,将其放置在文末
6.1 分析广告点击率的整体现状
1. 分析整体广告点击占比
根据数据显示,广告的整体点击量仅占展示量的4.94%,即广告的点击率为4.94%,可见很多广告在展示过程中并没有吸引到用户或者说没有投放到有需要的用户群体。
2. 分析几天中广告点击的变化情况
根据数据显示,几天中广告的整体点击率水平在5%
左右浮动,没有太大的波动。2017-05-05这一天广告的展示量与点击量很低,猜测有可能是因为数据采样不均匀导致,后续会重新进行验证分析。
6.2 分析用户的产品偏好
分析筛选受用户欢迎的广告商品
我们根据【广告被点击次数】进行分层,进而对现有的广告进行价值分析:
<10:测试广告是否有价值以及商品对用户是否有吸引力阶段;
10-20:进一步确认商品对用户有一定的吸引力,之前的点击并非偶然情况;
20-50:确定商品确实对用户有一定的吸引力,进一步加大广告投放力度,测试广告是否能否为商品带来转化;
50-100:投放的广告能为商品的转化带来一定的效果;
>=100:投放的广告能为商品的转化带来一定不错的效果。
![]() | ![]() |
---|
结论:①随着广告点击次数的上升,广告的点击率也在逐步上升,即广告价值与广告点击率呈正比关系。
②低价值广告的占比达到整体广告的98.34%之多,而高价值广告仅占整体广告的0.08%,这说明在测试广告价值阶段广告费用被高度浪费。
我们现阶段要分析筛选出【广告点击次数>=100】,加大力度进行投放,同时进一步对其主要的受众群体进行分析,定位其投放的用户方向,实现用更少的广告费用获取更大的商品转化。
6.3 分析一天24小时中用户的活跃时间
结论:
① 凌晨0点-7点、正午12点-14点这两个时间段是广告投放点击的高峰,傍晚16点-20点这个时间段是广告投放点击的低峰;
② 凌晨0点-7点这段时间是连续的高点击率时间,可以在这个时间段加大广告的投放力度;
③ 正午12点-14点这个时间段正好是人们的午饭午休时间,所以刷手机点击广告的概率也会增高;
④ 其中17点、19点这两个时间点的广告点击率相对较高,这个时间点正好是下班高峰期,人们在下班路上正好刷手机点击广告,建议在18点-19点这个时间段可以适当的加大广告投放力度;
⑤ 晚上20:00-0:00期间虽然广告展示量逐渐增加,但是广告的点击率却没有随之增加,反而降低了,猜测是用户如果没有必须且明确购买的需求,大部分用户在这段时间可能更倾向于陪伴家人、煲剧、看电影等休闲节目。
6.4 分析广告在不同资源位上的点击情况
结论:
广告在资源位430548_1007上展示量较多,但是相对而言,资源位430539_1007上的广告点击率相对更高,建议可以加大在资源位430539_1007上的广告投放力度。
6.5 分析广告的受众群体特征
1. 分析整体的广告受众群体特征
① 广告展示的用户约60%主要集中在微群0,且点击率达到5%;其他有些微群id虽然点击率也很高,但是数据量较小,不足以充分说明;
② 用户的男女比例约为1:2,且女性用户的点击率达到5.03%,男性用户的点击率只有4.81%,猜想是女性用户的购物欲会更强一些,建议可以加大对女性用户的广告投放;
③ 用户中非大学生用户占绝对主体,且点击率相对大学生用户更高,这符合非大学生用户具有更为独立的经济能力猜想;
④ 年龄层级为3、4、5的占比相对较高,点击率分别为:4.85%、4.91%、5.15%,其他层级的点击率虽然高,但是数据量少,不足以说明;
⑤ 微群群体3、4、5这三个群体中用户占比较高,且点击率分别高达:4.98%、4.97%、5.28%,建议可以加大对微群群体3、4、5的用户投放力度;
⑥ 购物程度为深度的用户占主体,但点击率仅占4.88%,建议对这一部分用户投放其更为感兴趣的广告;
⑦ 消费档次为低、中两档的用户占主体,尤其中档用户居多,且低、中档的用户点击率分别为:4.99%、4.95%,而高档用户点击率仅占4.65%,建议可以加大在消费档次为中档用户中的投放力度;
⑧ 城市层次为2的用户占主体,且相对其他城市层次的用户点击率最高,为4.99%,建议可以着重投放城市层次2的用户。
下面用雷达图展示整体主要的受众群体特征:
2. 根据不同广告商品分析获取不同的受众群体特征
由于广告商品数目过多,就不一一进行分析,我们从查看点击量前10的广告,如下图所示:
我们选取其中广告点击量第一的广告,即:adgroup_id为118317进行主要受众特征分析。
下图是adgroup_id为118317的点击用户特征分布饼图:
结论:
adgroup_id为118317的主要受众特征为:
特征名 | 特征群体 |
---|---|
微群 | 0 |
cms_group | 8>9>7 |
性别 | 男 |
是否大学生 | 否 |
年龄层次 | 2>3>1 |
购物程度 | 深度 |
消费档次 | 中档>低档 |
城市层次 | 2 |
因此,在投放广告118317时建议侧重对具有上述受众特征的用户进行投放。
6.6 基于物品的协同过滤推荐
1. 获取【投放效果不错的广告商品】中相似度高的广告商品
adgroup_id | 与118317的相似度 |
---|---|
8052 | 0.054313 |
79235 | 0.054313 |
42256 | 0.054313 |
29805 | 0.054313 |
280584 | 0.054313 |
169791 | 0.054313 |
170050 | 0.054313 |
203576 | 0.054313 |
19187 | 0.054313 |
239566 | 0.038405 |
2. 根据广告商品的相似度和已知的用户历史行为给用户生成推荐列表
我们从点击广告118317的用户中筛选满足上述受众特征的用户,筛选出来的userid有:
1007255, 359747, 387456, 888900, 267564, 1059836, 922603, 538653
我们分别向这些用户生成推荐列表,则:
387456的推荐列表 | 1007255, 359747, 888900, 267564, 1059836, 922603, 538653 的推荐列表 |
---|---|
![]() | ![]() |
结论:建议向点击过广告118317的用户群体也尝试投放以下几个广告:150272、205887、42256 、203576、79235。
七、分析结论
1、 广告整体的点击率约4.94%,主要是由于低价值广告较多,影响了整体的广告点击率,建议可以从受用户欢迎的商品类目、商品品牌以及广告主、广告计划中筛选要投放的广告商品;
2 、时间维度:
① 凌晨0点-7点是连续的用户点击的活跃时间,建议加大的这个时间段广告投放力度;
② 傍晚17点-19点之间正好是下班高峰期,猜想人们在回家途中刷手机看广告点击的可能性会加大,建议可以稍微增加这个时间段的广告投放力度;
3、资源位430539_1007上的点击率为5.01%,较
430548_1007资源位的点击率高,建议尝试在资源位430539_1007上进行投放;
4、广告整体的受众群体是:
微群0、cms_group为3、女性、非大学生、年龄层次为3/4、购物为深度、消费档次中低档为主、城市层次为2
如果投放时暂时没有受众群体方向,可以先以这个受众方向为主进行投放
5、我们以点击量最高的广告id为118317进行分析:
①主要受众群体为:
男性、非大学生、年龄层次2、购物为深度、消费中档、城市层级2、微群0、cms_group为8。
②推荐相似广告id为:
150272、205887、42256 、203576、79235。
八、代码部分
8.1 整体广告点击占比饼图
# 生成点击数据列表
counts = data.clk.value_counts()
ratio = data.clk.value_counts()/len(data)*100
data_clk = pd.DataFrame({'Count':counts, 'Ratio(%)':ratio})
# 绘制饼图对广告整体点击情况进行可视化
from pyecharts import options as opts
from pyecharts.charts import Pie
b=data['clk']
b=b.value_counts()
b=dict(b)
b['无点击人数']=b.pop(0)
b['点击人数']=b.pop(1)
name=pd.DataFrame(b.keys())
value=pd.DataFrame(b.values())
name=name[0].tolist()
value=value[0].tolist()
words=list(zip(list(name),list(value)))
h=list(zip( list(b.keys()),list(b.values())))
pie = Pie(init_opts=opts.InitOpts(width="500px", height="320px"))
pie.add("",words)
pie.set_global_opts(title_opts=opts.TitleOpts(title="广告整体点击情况",pos_left="40%"),
legend_opts=opts.LegendOpts(pos_top="20%", pos_left="80%"))
pie.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}({d}%)"))
pie.render_notebook()
8.2 几天中广告点击的变化情况(柱形-折线图)
data_date = data.groupby('date')['clk'].agg({
'展示量':'count',
'点击量':sum,
'点击率':np.mean
})
# 建立《2017/05/05至2017/05/13的广告点击情况》的柱形-折线图
from pyecharts import options as opts
from pyecharts.charts import Bar, Line
from pyecharts.faker import Faker
name = data_date.index.tolist()
v1 = data_date.展示量.tolist()
v2 = data_date.点击量.tolist()
v3 = ((data_date.点击率.values)*100).tolist()
v3 = [round(i,2) for i in v3]
bar = (
Bar(init_opts=opts.InitOpts(width="680px", height="420px"))
.add_xaxis(name)
.add_yaxis("展示量", v1)
.add_yaxis("点击量", v2)
.extend_axis(
yaxis=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} %"), interval=1)
)
.set_series_opts(label_opts=opts.LabelOpts(is_show=True))
.set_global_opts(
title_opts=opts.TitleOpts(title="2017/05/05至2017/05/13的广告点击情况",pos_left="18%"),
yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} "), max_=350000),
legend_opts=opts.LegendOpts(pos_top="10%", pos_right="10%")
)
)
line = Line().add_xaxis(name).add_yaxis("点击率", v3, yaxis_index=1)
bar.overlap(line)
bar.render_notebook()
8.3 各点击区间的广告点击情况(堆积柱形-折线图、饼图)
data_adgroup = data.groupby('adgroup_id')['clk'].agg({
'展示量':'count',
'点击量':sum,
'点击率':np.mean
})
data_adgroup['点击量区间'] = data_adgroup.点击量.values # 添加新列用来分区间
data_adgroup.sort_values(by='点击量',ascending=False)
# 对所有广告点击量进行分段
bins = [0,10,20,50,100,100000]
labels = ['<10','10-20','20-50','50-100','>=100']
data_adgroup['点击量区间'] = pd.cut(data_adgroup.点击量区间,bins=bins,labels=labels,right=False)
data_adgroup.sort_values(by='点击量',ascending=False)
data_adgroup_clk= data_adgroup.groupby('点击量区间').agg({
'点击量':['count',sum],
'展示量':sum
})
data_adgroup_clk.columns = ['广告量','点击量','展示量']
data_adgroup_clk['点击率'] = data_adgroup_clk.点击量.values/data_adgroup_clk.展示量.values*100
data_adgroup_clk
"""
利用堆积柱形图-折线图可视化分析各点击数区间的广告点击率情况
"""
from pyecharts import options as opts
from pyecharts.charts import Bar, Line, Scatter
from pyecharts.faker import Faker
name = data_adgroup_clk.index.tolist()
v0 = data_adgroup_clk.展示量.tolist()
v1 = (data_adgroup_clk.展示量-data_adgroup_clk.点击量).tolist()
v2 = data_adgroup_clk.点击量.tolist()
v3 = data_adgroup_clk.点击率.tolist()
v3 = [round(i,2) for i in v3]
v4 = data_adgroup_clk.广告量.tolist()
b = dict(zip(name,v4))
words=list(zip(name,v4))
bar = (
Bar(init_opts=opts.InitOpts(width="640px", height="460px"))
.add_xaxis(name)
.add_yaxis("无点击量", v1, stack="stack1", category_gap="60%")
.add_yaxis("点击量", v2, stack="stack1", category_gap="60%")
.extend_axis(
yaxis=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} %"), interval=1)
)
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
.set_global_opts(
title_opts=opts.TitleOpts(title="各点击数区间的广告点击率情况", pos_left="30%"),
yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value}"), max_=2500000),
legend_opts=opts.LegendOpts(pos_top="6%", pos_left="50%")
)
)
line = Line().add_xaxis(name).add_yaxis("点击率", v3, yaxis_index=1)
bar.overlap(line)
bar.render_notebook()
"""
利用玫瑰图描绘点击用户所在的城市层次分布
"""
from pyecharts import options as opts
from pyecharts.charts import Pie
pie = (
Pie(init_opts=opts.InitOpts(width="666px", height="365px"))
.add("", words, center=["48%", "62%"],radius=["40%", "75%"], rosetype="radius")
.set_global_opts(title_opts=opts.TitleOpts(title="各点击数区间的广告在整体广告中的占比分布",pos_right="22%"),
legend_opts=opts.LegendOpts(pos_top="6%", pos_right="16%"))
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}({d}%)"))
)
pie.render_notebook()
8.4 一天24小时中用户的活跃时间(堆积柱形-折线图)
data_hour = data.groupby('hour')['clk'].agg({
'展示量':'count',
'点击量':sum,
'点击率':np.mean
})
data_hour
"""
利用堆积柱形图-折线图可视化分析一天(24小时)中用户的点击情况
"""
from pyecharts import options as opts
from pyecharts.charts import Bar, Line
name = data_hour.index.tolist()
v0 = data_hour.展示量.tolist()
v1 = (data_hour.展示量-data_hour.点击量).tolist()
v2 = data_hour.点击量.tolist()
v3 = data_hour.点击率.tolist()
v3 = ((data_hour.点击率.values)*100).tolist()
v3 = [round(i,2) for i in v3]
b = dict(zip(name,v2))
words=list(zip(name,v2))
bar = (
Bar(init_opts=opts.InitOpts(width="1020px", height="480px"))
.add_xaxis(name)
.add_yaxis("无点击量", v1, stack="stack1", category_gap="80%")
.add_yaxis("点击量", v2, stack="stack1", category_gap="80%")
.extend_axis(
yaxis=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} %"), interval=1, min_ = 4.5, max_ = 5.2)
)
.set_series_opts(label_opts=opts.LabelOpts(is_show=False, position="right"))
.set_global_opts(
title_opts=opts.TitleOpts(title="一天(24小时)中用户的点击情况", pos_left="35%"),
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15)),
yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} "), max_ = 180000),
legend_opts=opts.LegendOpts(pos_top="6%", pos_left="55%")
)
)
line = Line().add_xaxis(name).add_yaxis("点击率", v3, yaxis_index=1,markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(type_="average")]),)
bar.overlap(line)
bar.render_notebook()
8.5 广告在不同资源位上的点击情况(堆积柱形图-折线图)
"""
利用堆积柱形图-折线图可视化分析广告在不同资源位上的点击情况
"""
data_pid = data.groupby('pid')['clk'].agg({
'展示量':'count',
'点击量':sum,
'点击率':np.mean
})
data_pid
from pyecharts import options as opts
from pyecharts.charts import Bar, Line
name = data_pid.index.tolist()
v0 = data_pid.展示量.tolist()
v1 = (data_pid.展示量-data_pid.点击量).tolist()
v2 = data_pid.点击量.tolist()
v3 = data_pid.点击率.tolist()
v3 = ((data_pid.点击率.values)*100).tolist()
v3 = [round(i,2) for i in v3]
b = dict(zip(name,v2))
words=list(zip(name,v2))
bar = (
Bar(init_opts=opts.InitOpts(width="620px", height="460px"))
.add_xaxis(name)
.add_yaxis("无点击量", v1, stack="stack1", category_gap="80%")
.add_yaxis("点击量", v2, stack="stack1", category_gap="80%")
.extend_axis(
yaxis=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} %"), interval=1)
)
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
.set_global_opts(
title_opts=opts.TitleOpts(title="广告在不同资源位上的点击情况", pos_left="30%"),
yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value}"), max_=1500000),
legend_opts=opts.LegendOpts(pos_top="6%", pos_left="50%")
)
)
line = Line().add_xaxis(name).add_yaxis("点击率", v3, yaxis_index=1)
bar.overlap(line)
bar.render_notebook()
8.6 广告的受众群体特征
from pyecharts import options as opts
from pyecharts.charts import Radar
data1 = [{"value": [0, 3, 2, 3, 2, 3, 0, 2], "name": "主要受众群体"}]
# data2 = [{"value": [2, 6, 1, 1, 0, 2], "name": "开销分配"}]
c_schema = [
{"name": "微群", "max": 97, "min": 0},
{"name": "cms_group", "max": 12, "min": 0},
{"name": "性别(1男2女)", "max": 2, "min": 1},
{"name": "年龄层次", "max": 6, "min": 0},
{"name": "消费档次(1:低档,2:中档,3:高档)", "max": 3, "min": 1},
{"name": "购物深度(1:浅层用户,2:中度用户,3:深度用户)", "max": 3, "min": 1},
{"name": "是否大学生(1:是,0:否)", "max": 1, "min": 0},
{"name": "城市层级", "max": 4, "min": 1},
]
radar = (
Radar()
.set_colors(["#4587E7"])
.add_schema(
schema=c_schema,
shape="circle",
center=["50%", "50%"],
radius="80%",
textstyle_opts=opts.TextStyleOpts(color="#000"),
angleaxis_opts=opts.AngleAxisOpts(
min_=0,
max_=360,
is_clockwise=False,
interval=5,
axistick_opts=opts.AxisTickOpts(is_show=False),
axislabel_opts=opts.LabelOpts(is_show=False),
axisline_opts=opts.AxisLineOpts(is_show=False),
splitline_opts=opts.SplitLineOpts(is_show=False),
),
radiusaxis_opts=opts.RadiusAxisOpts(
min_=0,
max_=8,
interval=2,
splitarea_opts=opts.SplitAreaOpts(
is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)
),
),
polar_opts=opts.PolarOpts(),
splitarea_opt=opts.SplitAreaOpts(is_show=False),
splitline_opt=opts.SplitLineOpts(is_show=False),
)
.add(
series_name="主要受众群体",
data=data1,
areastyle_opts=opts.AreaStyleOpts(opacity=0.1),
linestyle_opts=opts.LineStyleOpts(width=1, color="#CD0000"),
)
)
radar.render_notebook()
根据不同广告商品分析获取不同的受众群体特征:
# 查看所有投放的广告并按点击量排序
data_adgroup = data.groupby('adgroup_id')['clk'].agg({
'展示量':'count',
'点击量':sum,
'点击率':np.mean
})
data_adgroup.sort_values(by='点击量',ascending=False).head(10)
# 查看所有投放的广告并按点击量排序
data_adgroup = data.groupby('adgroup_id')['clk'].agg({
'展示量':'count',
'点击量':sum,
'点击率':np.mean
})
data_adgroup.sort_values(by='点击量',ascending=False) # adgroup_id为118317的广告点击量第一(展示量3474,点击量348,点击率10.02%)
# 筛选adgroup_id为118317的广告
data_118317 = data[data['adgroup_id']==118317]
data_118317
# 筛选【adgroup_id为118317】且【被点击】的广告
data_118317_clk = data_118317[data_118317['clk']==1]
data_118317_clk
# 根据【adgroup_id】、【userid】这两列去重
data_118317_clk.drop_duplicates(subset=['userid','adgroup_id'],keep='first',inplace=True)
data_118317_cms = data_118317.groupby('cms_segid')['clk'].agg({
'点击量':sum,
})
data_118317_cms_group = data_118317.groupby('cms_group_id')['clk'].agg({
'点击量':sum,
})
data_118317_gender = data_118317.groupby('final_gender_code')['clk'].agg({
'点击量':sum,
})
data_118317_age = data_118317.groupby('age_level')['clk'].agg({
'点击量':sum,
})
data_118317_shopping = data_118317.groupby('shopping_level')['clk'].agg({
'点击量':sum,
})
data_118317_occupation = data_118317.groupby('occupation')['clk'].agg({
'点击量':sum,
})
data_118317_city = data_118317.groupby('new_user_class_level ')['clk'].agg({
'点击量':sum,
})
data_118317_pvalue = data_118317.groupby('pvalue_level')['clk'].agg({
'点击量':sum,
})
from pyecharts import options as opts
from pyecharts.charts import Pie
name_cms = data_118317_cms.index.tolist()
for i in range(0,len(name_cms)):
name_cms[i]=f'微群{name_cms[i]}'
v_cms = data_118317_cms.点击量.tolist()
b_cms = dict(zip(name_cms,v_cms))
words_cms = list(zip(name_cms,v_cms))
name_cms_group = data_118317_cms_group.index.tolist()
# for i in range(0,13):
# name_cms_group[i]=f'cms_group{i}'
v_cms_group = data_118317_cms_group.点击量.tolist()
b_cms_group = dict(zip(name_cms_group,v_cms_group))
words_cms_group = list(zip(name_cms_group,v_cms_group))
name_gender = data_118317_gender.index.tolist()
name_gender[0]='男'
name_gender[1]='女'
v_gender = data_118317_gender.点击量.tolist()
b_gender = dict(zip(name_gender,v_gender))
words_gender = list(zip(name_gender,v_gender))
name_age = data_118317_age.index.tolist()
for i in range(0,7):
name_age[i]=f'年龄层次{i}'
v_age = data_118317_age.点击量.tolist()
b_age = dict(zip(name_age,v_age))
words_age = list(zip(name_age,v_age))
name_shopping = data_118317_shopping.index.tolist()
name_shopping[0]='浅度'
name_shopping[1]='中度'
name_shopping[2]='深度'
v_shopping = data_118317_shopping.点击量.tolist()
b_shopping = dict(zip(name_shopping,v_shopping))
words_shopping = list(zip(name_shopping,v_shopping))
name_occupation = data_118317_occupation.index.tolist()
name_occupation[0]='非大学生'
name_occupation[1]='大学生'
v_occupation = data_118317_occupation.点击量.tolist()
b_occupation = dict(zip(name_occupation, v_occupation))
words_occupation = list(zip(name_occupation, v_occupation))
name_city = data_118317_city.index.tolist()
for i in range(0,4):
name_city[i]=f'城市层次{i+1}'
v_city = data_118317_city.点击量.tolist()
b_city = dict(zip(name_city, v_city))
words_city = list(zip(name_city, v_city))
name_pvalue = data_118317_pvalue.index.tolist()
name_pvalue[0]='低档'
name_pvalue[1]='中档'
name_pvalue[2]='高档'
v_pvalue = data_118317_pvalue.点击量.tolist()
b_pvalue = dict(zip(name_pvalue, v_pvalue))
words_pvalue = list(zip(name_pvalue, v_pvalue))
def new_label_opts():
return opts.LabelOpts( formatter="{b}: {c}({d}%)", position="inside")
c = (
Pie(init_opts=opts.InitOpts(width="980px", height="460px"))
.add(
"微群分布",
words_cms,
center=["10%", "30%"],
radius=[40, 80],
label_opts=new_label_opts(),
)
.add(
"cms_group分布",
words_cms_group,
center=["35%", "30%"],
radius=[40, 80],
label_opts=new_label_opts(),
)
.add(
"性别分布",
words_gender,
center=["60%", "30%"],
radius=[40, 80],
label_opts=opts.LabelOpts(formatter="{b}: {c}({d}%)", position="inside"),
)
.add(
"年龄层次分布",
words_age,
center=["85%", "30%"],
radius=[40, 80],
label_opts=new_label_opts(),
)
.add(
"购物深度分布",
words_shopping,
center=["10%", "70%"],
radius=[40, 80],
label_opts=new_label_opts(),
)
.add(
"是否大学生分布",
words_occupation,
center=["35%", "70%"],
radius=[40, 80],
label_opts=new_label_opts(),
)
.add(
"城市层次分布",
words_city,
center=["60%", "70%"],
radius=[40, 80],
label_opts=new_label_opts(),
)
.add(
"消费档次分布",
words_pvalue,
center=["85%", "70%"],
radius=[40, 80],
label_opts=new_label_opts(),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="广告id为【118317】的点击用户特征分布", pos_left="30%"),
legend_opts=opts.LegendOpts(
type_="scroll", pos_top="6%", pos_left="0%",
),
)
.set_series_opts(
tooltip_opts=opts.TooltipOpts(
trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"
),
)
)
c.render_notebook()
8.7 基于物品的协同过滤推荐
# 筛选有点击的数据
data_clk_1 = data[data['clk'] == 1]
# 根据【adgroup_id】、【userid】这两列去重
data_clk_1.drop_duplicates(subset=['userid','adgroup_id'],keep='first',inplace=True)
# 选取【'userid', 'adgroup_id', 'clk'】三列数据
columns = ['userid', 'adgroup_id', 'clk']
data_clk_col = data_clk_1[columns]
# 将数据写入.txt
data_clk_col.to_csv('D:/淘宝展示广告点击率预估数据集/data_clk_col.txt',sep=',',index=False)
f = open('D:/淘宝展示广告点击率预估数据集/data_clk_col.txt', "r", encoding="utf-8")
dataSet = {}
for line in f:
userid, adgroup_id, clk = line.strip().split(",")
dataSet.setdefault(userid, {})
dataSet [userid][adgroup_id] = clk
print(dataSet)
dataSet.pop('userid')
print('-'*60)
print(dataSet)
from math import sqrt
import operator
N={};#喜欢广告i的总人数
C={};#喜欢广告i也喜欢广告j的人数
for userid,item in dataSet.items():
for i,score in item.items():
N.setdefault(i,0)
N[i]+=1
C.setdefault(i,{})
for j,scores in item.items():
if j not in i:
C[i].setdefault(j,0)
C[i][j]+=1
print("---构造的共现矩阵---")
print ('N:',N);
print ('C:',C);
# 计算广告与广告之间的相似矩阵
W={};
for i,item in C.items():
W.setdefault(i,{});
for j,item2 in item.items():
W[i].setdefault(j,0);
W[i][j]=C[i][j]/sqrt(N[i]*N[j]);
print("---构造广告的相似矩阵---")
print(W)
# 查询【118317】的推荐产品机器相似度并将其转换为dataFrame格式
recommend_118317 = pd.DataFrame([W['118317']]).T
# 修改列名
recommend_118317.rename(columns={0:'相似度'},inplace=True)
# 按相似度倒序排序
recommend_118317.sort_values(by='相似度',ascending=False).head(10)
# 获取118317中符合主要受众群体的用户
data_118317_clk_people = data_118317_clk[(data_118317_clk['cms_segid']==0) & (data_118317_clk['cms_group_id']==8)
& (data_118317_clk['final_gender_code']==1) & (data_118317_clk['occupation']==0)
& (data_118317_clk['age_level']==2) & (data_118317_clk['shopping_level']==3)
& (data_118317_clk['pvalue_level']==2) & (data_118317_clk['new_user_class_level ']==2)]
data_118317_clk_people
根据用户历史行为进行推荐在此处以userid为’387456’为例:
rank={};
for i,score in dataSet['387456'].items():#获得用户userid‘387456’点击广告历史记录
for j,w in sorted(W[i].items(),key=operator.itemgetter(1),reverse=True)[0:3]:#获得与广告i相似的k个物品
if j not in dataSet['387456'].keys():#该相似的广告不在用户userid的记录里
rank.setdefault(j,0);
rank[j]+=float(score) * w;
recommend_to_387456 = sorted(rank.items(),key=operator.itemgetter(1),reverse=True)[0:10]
recommend_to_387456 = pd.DataFrame(recommend_to_387456)
# 修改列名
recommend_to_387456.rename(columns={0:'adgroup_id'},inplace=True)
recommend_to_387456.rename(columns={1:'用户点击的概率'},inplace=True)
print("---推荐给用户【387456】----")
recommend_to_387456