一、分析背景
电商平台的发展取决于用户。电商平台如何让用户点击里面的内容,就是留住用户的第一步。电商平台的首页往往会分成好几个模块,比如促销活动广告,而这些模块都是代表了部分商品。如果平台的活动广告不是用户喜欢的,那么这个活动就没有意义。准确判断用户是不是喜欢广告中的商品,可以大大改善广告点击情况。
二、分析目的
现有阿里云天池(https://tianchi.aliyun.com/dataset/dataDetail?dataId=56)提供的淘宝用户的广告点击数据。数据中包含了四张表,分别为用户行为日志behavior_log(简称为bl)、原始样本骨架raw_sample(简称为rs)、广告基本信息表ad_feature(简称为af)、用户基本信息表user_profile(简称为up),具体字段说明如表1:
我们利用现有数据,使用用户画像分析和随机森林,来查看用户的广告点击偏好和预测raw_sample表中用户是否点击广告。
三、分析方法
1. 用户画像分析
针对用户的特征,分析什么种类的用户喜欢什么类型的广告。
2. 随机森林预测
使用数据挖掘方法,将数据分为训练集和测试集,通过训练分类预测模型来预测用户是否点击广告。
四、数据准备——数据清洗
1. user_profile表
查看表结构:
up = pd.read_csv(r'E:\datafile\ad_clik\user_profile.csv',index_col='userid')
up.info()
up.describe()
发现无重复值、异常值和格式错误值,但cms_segid、cms_group_id、pvalue_level和new_user_class_level有缺失值。
对于缺失值处理:① 删除 ②增补
此次,我们删除了特征缺失很多的501条数据;对于仅城市层级缺失的数据,用随机森林方法进行增补;其他缺失情况的数据,按照全非空数据的比例进行增补。
2. ad_feature表
查看表结构,发现brand字段有缺失。通过adgroup_id字段连接raw_sample表,增补对应的brand。但是发现同一个adgroup_id有两个brand的情况,直接删除。
3. raw_sample表
无缺失值、重复值、异常值和格式错误值。
五、数据分析
1. up表中用户特征的分布情况
1.1 性别、是否大学生
以性别为例:
gender = up['final_gender_code'].value_counts()
# print(gender)
# 女性用户有684026人,男性用户有377241人
plt.figure()
plt.grid(alpha=0.4)
gender.plot(kind='bar')
plt.title('up表的性别分布情况',fontproperties=my_font)
plt.xlabel('性别(1为男,2为女)',fontproperties=my_font)
plt.ylabel('人数',fontproperties=my_font)
plt.show()
结论:女性淘宝用户占比很大,淘宝用户主力是非大学生。
1.2 年龄层
结论:年龄层分布基本符合高斯分布。年龄层为3的用户最多,最少的为6。我们可以推测出年龄层为3的是在30岁左右的青年。
1.3 消费档次
中档消费用户最多,其次为低档,高档的最少。
1.4 购物深度
淘宝深度用户占绝大多数,说明淘宝的用户忠诚度方面做得很好,与现实相符。
1.5 城市层级
城市层级为2的用户最多,最少的是层级为1的。可以推测,层级为2的城市是二线城市。而一线城市为何是最少的?一个解释就是一线城市数本身就比其他线城市少。
2. af表的广告属性分布情况
2.1 价格
首先我们将价格按照等距离分桶,查看分桶后的分布情况。
p = af['price']
bins = np.arange(0,600,10)
bins = pd.cut(p,bins)
bins = p.groupby(bins).count()
plt.figure(figsize=(30,8))
bins.plot(kind='bar')
plt.show()
我们可以发现,0-10、90-100、190-200、290-300、390-400、490-500是比周围价格区间的广告单元的数量都高。因为广告商品的价格都很喜欢198,99,98等类型的价格。至于价格在0-10的广告数量很多,是因为淘宝中此类商品本身就特别多。
2.2 商品类目
我们查看商品类目出现的个数:
cate = af['cate_id'].value_counts()
bins = np.arange(0,200,10)
bins = pd.cut(cate,bins)
bin_counts = cate.groupby(bins).count()
plt.figure(figsize=(8,8))
bin_counts.plot(kind='pie')
plt.show()
#结果表明:在这个时间段,打广告的商品类目大都比较零散,只有10个以内的广告单元的cate_id占了将近一半。
2.3 广告计划
在这个时间段内,大多数广告计划各不相同。
2.4 广告主
各个广告对应的广告主也各不相同。
2.5 广告所属品牌
广告所属品牌也很多,这也说明了淘宝中的商家并不存在垄断情况,市场还是良好的。
3. 用户的广告点击偏好
3.1 不同用户特征的商品类目偏好
在这里,我们根据点击量情况,查看不同类用户最经常点击的广告商品类目。
df_clk = df[df['clk']==1]
u = ['final_gender_code','age_level','pvalue_level','shopping_level','occupation','new_user_class_level ']
c = [[1,2],[1,2,3,4,5,6],[1,2,3],[1,2,3],[0,1],[1,2,3,4]]
m = 0
for i in u:
cate_clk = df_clk.groupby([i,'cate_id']).count()['clk'].reset_index()
s = c[m]
m += 1
for n in s:
print(cate_clk[cate_clk[i]==n].sort_values(by='clk',ascending=False).head(1))
print('ok')
我们发现,无论是哪种类型的用户,点击量最高的广告商品类目都是6261。这需要区分清楚6261类广告是真的受用户偏爱,还是它只是曝光量很高但点击率不高。
我们先查看每个商品类目的点击量和点击率情况:
cate_y = df['cate_id'][df['clk']==1].value_counts().reset_index()
cate_y.columns = ['cate_id','clk']
# cate_n = df['cate_id'][df['clk']==0].value_counts().reset_index()
# cate_n.columns = ['cate_id','nclk']
cate_sum = df['cate_id'].value_counts().reset_index()
cate_sum.columns = ['cate_id','counts']
cate = pd.merge(cate_y,cate_sum,how='outer',on='cate_id')
cate = cate.fillna(0)
cate['clk_ratio'] = cate['clk']/cate['counts']
cate['clk_ratio'] = cate['clk_ratio'].map(lambda x:('%.4f')%x)
cate['clk_ratio'] = cate['clk_ratio'].astype(float)
cate.describe([0.1,0.3,0.5,0.7,0.8,0.9,0.95])
从点击率的分布情况,可以看出平均点击率为0.0476,大多数商品类目的点击率都不是很高。
cate[cate['cate_id']==6261]
查看6261的点击率为0.0605,在分位点70%以上,这类商品不仅点击量高而且点击率也高,说明它们的广告还是很吸引用户的。
再进一步,在最偏好的商品类目中,查看最喜欢的品牌:
u = ['final_gender_code','age_level','pvalue_level','shopping_level','occupation','new_user_class_level ']
c = [[1,2],[1,2,3,4,5,6],[1,2,3],[1,2,3],[0,1],[1,2,3,4]]
m = 0
for i in u:
cate_clk = df_clk[df_clk['cate_id']==6261].groupby([i,'brand']).count()['clk'].reset_index()
s = c[m]
m += 1
for n in s:
print(cate_clk[cate_clk[i]==n].sort_values(by='clk',ascending=False).head(1))
print('ok')
结果如下:
总体来看,品牌为234846、商品类目为6261的广告最常被用户点击。只有年龄层为1和2,以及大学生用户最喜欢的品牌是82527。
同样重复上面步骤,查看点击率情况:
按品牌划分的广告点击率平均为0.0412;品牌为234846的广告点击率为0.0608,在分位点75%左右;品牌为82527的点击率为0.0685,超过分为点80%。说明在商品类目为6261中,绝大多数用户最常点击品牌为234846的广告;而对于低年龄层的用户和大学生用户来说,最喜欢品牌为82527的广告,可以推测这个品牌很受年轻人喜爱。
3.2 不同用户特征的价格偏好
u = ['final_gender_code','age_level','pvalue_level','shopping_level','occupation','new_user_class_level ']
c = [[1,2],[1,2,3,4,5,6],[1,2,3],[1,2,3],[0,1],[1,2,3,4]]
m = 0
for i in u:
cate_clk = df_clk.groupby([i,'price']).count()['clk'].reset_index()
s = c[m]
m += 1
for n in s:
print(cate_clk[cate_clk[i]==n].sort_values(by='clk',ascending=False).head(1))
print('ok')
结果如下:
点击的广告价格分布情况:
结果表明:淘宝用户经常点击价格为198的广告。而低年龄层用户消费能力低一些,最喜欢点击98、99左右价格的广告。结合价格分布情况,侧面验证了大多数淘宝用户的消费层次是中档的。
3.3 不同用户特征的广告活动计划偏好
与上面代码类目,得到以下结果:
其中,活动计划为118601的广告最吸引用户,尤其是女性用户和深度用户。
4. 用户的广告点击情况预测
由于数据量太大,我们只选取三天的数据,进行预测。
4.1 数据转换
由于广告属性的cate_id、campaign_id、customer和brand无法直接进行建模,需要进行数据转换。此处,按照广告属性的点击量和点击率情况,将它们进行等频分桶,分成十个等级,转换成8个新变量。
以cate_id为例:
# 先计算cate_id的点击量和点击率
cate_y = df['cate_id'][df['clk']==1].value_counts().reset_index()
cate_y.columns = ['cate_id','clk']
# cate_n = df['cate_id'][df['clk']==0].value_counts().reset_index()
# cate_n.columns = ['cate_id','nclk']
cate_sum = df['cate_id'].value_counts().reset_index()
cate_sum.columns = ['cate_id','counts']
cate = pd.merge(cate_y,cate_sum,how='outer',on='cate_id')
cate = cate.fillna(0)
cate['clk_ratio'] = cate['clk']/cate['counts']
cate['clk_ratio'] = cate['clk_ratio'].map(lambda x:('%.4f')%x)
cate['clk_ratio'] = cate['clk_ratio'].astype(float)
# 点击量分桶
cate['cate_clk_bins'] = pd.qcut(cate['clk'],13,duplicates='drop',labels=[1,2,3,4,5,6,7,8,9,10])
cate['cate_clk_bins'] = cate['cate_clk_bins'].astype(int)
# cate['clk_bins'].unique().size
## 点击率分桶
cate['cate_clk_ratio_bins'] = pd.qcut(cate['clk_ratio'],13,duplicates='drop',labels=[1,2,3,4,5,6,7,8,9,10])
cate['cate_clk_ratio_bins'] = cate['cate_clk_ratio_bins'].astype(int)
# cate['clk_ratio_bins'].unique().size
cate.drop(['clk','counts','clk_ratio'],axis=1,inplace=True)
4.2 相关分析和特征筛选
对于定类数据之间的相关分析,我们使用卡方检验;对于定类和定距数据,我们使用eta系数。
from sklearn.feature_selection import chi2,SelectKBest
train_x = df_012.drop('clk',axis=1).values
train_y = df_012['clk'].tolist()
selector = SelectKBest(chi2,k='all')
selector.fit(train_x, train_y)
scores = selector.scores_
print(scores)
pvalues = selector.pvalues_
print(pvalues) #p值都小于0.05
selector = SelectKBest(chi2,k=10)
v = selector.fit(train_x, train_y).get_support(indices=True)
print(v)
scores = selector.scores_
print(scores)
根据特征得分,只保留10个定类特征,删除了'pid','clk','age_level','pvalue_level','shopping_level','occupation','new_user_class_level ','cate_clk_bins'这些特征。
同时使用SPSS计算了eta系数,结果显示它们相关。
4.2 训练数据并预测
选择使用随机森林模型,进行用户广告点击情况预测。
首先将三天的数据切分成训练集和测试集:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score,train_test_split
todrop = ['pid','clk','age_level','pvalue_level','shopping_level','occupation','new_user_class_level ','cate_clk_bins']
x = df_012.drop(todrop,axis=1).values
y = df_012['clk'].tolist()
train_X,test_X, train_y, test_y = train_test_split(x,y,test_size=1/5)
然后对训练集进行交叉验证训练模型:
clf1 = RandomForestClassifier(n_estimators=10,max_depth=None,min_samples_split=2,random_state=0)
scores = cross_val_score(clf1,train_X,train_y,scoring='accuracy',cv=5)
clf1.fit(train_X,train_y)
print(scores.mean())
print(scores.std())
以精确率为得分,训练结果平均得分为0.9401836626955434,标准差为0.00010639743381447468。
模型训练结束之后,使用预测集预测:
y_pred = clf1.predict(test_X)
最后,做一个预测结果验证,计算准确率:
test = pd.DataFrame([y_pred,test_y],index=['y_pred','test_y'])
test = test.T
print('预测准确率:',test[(y_pred==test_y)]['y_pred'].size/test['y_pred'].size) # 0.9404274614218243