天猫用户重复购买预测赛题——赛题理解 + 数据探索
理论知识
-
双十一进行促销,商家希望知道那些用户可能会成为重复购买其店铺商品的忠诚用户,从而精准营销
-
可以用复购率来衡量店铺的用户忠诚程度
- 复购率 = 重复购买用户数/用户样本数量
- 复购率= 重复购买行为次数/用户样本数量
- 100用户 20复购行为 10复购1次 10复购2次 方法1:20/100 方法2:(10x1+10x2)/100
-
影响复购率的因素
- 用户维度
- 性别、身高、体重、月收入、喜好
- 女性客户 品牌偏好
- 商品维度
- 手机类
- 母婴类
- 用户维度
-
缺失值处理
-
count() 统计不为空数据的个数
-
shape() 统计数据样本的个数
-
dropna() 根据各标签的值 是否缺失数据对轴标签过滤,可通过阈值调节
-
fillna() 用定值或者插值 填充缺失值
-
对于数据缺失比较严重一般将其删除
- 数据缺失过大 所蕴含信息较少
- 缺失率过大的数据 可能导致用户在建模中产生偏差 难以追溯
-
-
缺失值较少的情况下,处理方法
处理方法 | 说明 |
---|---|
统计量填充 | 连续值:中位数 排除异常值造成的影响 离散型:众数 |
特殊值填充 | 填不在正常取值范围的树 -999、0 等表示 |
不处理 | XGB和LGB对缺失值不敏感 |
- 缺失值填充方法
插补方法 | 说明 | 优点 | 缺点 | 使用环境 |
---|---|---|---|---|
类均值插补 | 数值型:均值 非数值型:众数 | 简单易行,被插补的值比较稳定 | 不能反映缺失值的变异 | 缺失率低首选 |
类随机插补 | 聚类填充 使用所有可能的值进行填充 | 能体现数据的变异性 | 依赖于观测值 | 低缺失率 |
回归插补 | 基于完整的数据集 建立回归模型 | 方差好估计 | 稳定性依赖于辅助变量 抽样误差不易控制 | 变量间的相关性强 |
Em插补 | 通过观测数据的边际分布对未知参数进行极大似然估计 | 考虑了缺失值的不确定性 | 计算复杂 | 高缺失率 |
多重插补MCMC | 估计出待插补的值,然后加上不同的噪声,形成多组可选插补值 | 考虑了缺失值的不确定性 | 计算复杂 | 高缺失率首选 |
- 不均衡样本
- 很多真实场景中存在长尾效应,也就是二八原理,一个事物20%的特性决定了事物80%的重要性
- 如:在市场营销中即指"企业80%的业务来自20%的顾客"
- 很多场景是假设数据基于多元的正态分布,但是在分类任务中,不同类别的训练样例数目常出现差异较大情况
- 样本不均衡会造成模型对样本数较多的类别过拟合、对较少的类别欠拟合
- 举例说明
- 实际为负 预测为负 9700 预测为正150
- 实际为正 预测为负 50 预测为正150
- 准确率 (9700+150)/(9700+150+50+150) = 0.98
- 如果全部预测为负
- 实际为负 预测为负 9850 预测为正
- 实际为正 预测为负 150 预测为正
- 准确率 0.985 反而增加了 对不平衡样本过拟合了 所以 AUC作为评分
- 解决不平衡样本的方法
- 随机欠采样
- 正50例 负950例 负样本中随机选10% 新训练集95+50
- 平衡数据的同时减少了数据量,加速了训练
- 数据减少影响模型的特征学习能力和泛化能力
- 随机过采样
- 正50例 负950例 正样本复制10次 新训练集500+950
- 相对于欠采样 没有导致数据信息的损失
- 对较少类别的复制 增加了过拟合的可能性
- 基于聚类的过采样方法
- 依据聚类中心进行过采样/欠采样 使原始类中每个集群样本数目相同
- SMOTE算法
- 基于数据清洗的SMOTE
- 去掉一些重叠样本 Tomek Links
- 对于一对样本( x i , x j x_i,x_j xi,xj) x i x_i xi来自minority classes x j x_j xj来自majority classes 。如果离 x i x_i xi最近的少量样本是 x j x_j xj,对 x j x_j xj来说是 x i x_i xi,称这一对是Tomek Link,移去所有Tomek Link 样本重叠减小 利于分类
- 随机欠采样
- 常见数据分布
- 伯努利分布
- 只有两种可能 1成功 0失败
- P ( X = x ) = { 1 − p , x = 0 p , x = 1 P(X = x)= \begin{cases}1-p,x=0\\p ,x=1\end{cases} P(X=x)={1−p,x=0p,x=1
- 二项分布
- n个独立的是/非中成功次数的离散概率分布
- P ( X = x ) = n ! ( n − x ) ! x ! p x q n − x P(X=x) = {n!\over (n-x)!x!}p^xq^{n-x} P(X=x)=(n−x)!x!n!pxqn−x
- 柏松分布
- 任何一个成功的事件都不应该影响另一个成功的事件
- 短时间内成功率等于在更长时间内成功的概率
- P ( X = x ) = e − u u x x ! P(X=x)=e^{-u} {u^x \over x!} P(X=x)=e−ux!ux
- 正态分布
- f ( x ) = 1 2 π σ e − ( x − u ) 2 2 σ 2 f(x) = {1\over \sqrt{2\pi\sigma}}e^{-{(x-u)^2 \over 2\sigma^2}} f(x)=2πσ1e−2σ2(x−u)2
- 指数分布
- f ( x ) = { λ e − λ x , x > 0 0 , x < = 0 f(x)= \begin{cases}\lambda e^{-\lambda x},x>0\\0 ,x<=0\end{cases} f(x)={λe−λx,x>00,x<=0
- 伯努利分布
1. 赛题信息
-
用户行为表 user_info
DataFields Definition user_id 用户ID item_id 商品ID cat_id 商品类别ID seller_id 商家ID brand_id 品牌ID time_tamp 时间戳 action_type 取值范围{0,1,2,3}
0:点击 1:加购物车 2:购买 3:收藏 -
用户特征表 user_log
DataFields Definition user_id 用户ID age_range 用户年龄范围{0,1,2,3}
<18 :1 [18,24] :2 [25,29] :3 [30,34] :4 [35,39] :5 [40,49 ] :6 >= 50 : 7和8
0和NULL表示未知gender 用户性别{0,1,2}
0表示女性,1表示男性,2和NULL表示未知 -
训练数据 train_data
DataFields Definition user_id 用户ID merchant_id 商家ID label 取值范围{0,1,2}
0表示非重复购买,1表示重复购买,测试集数据这个字段为Null
2. 评估指标 AUC
- AUC作为评估指标 二分类评测指标
- A U C = ∑ i ∈ p o s i t i v e C l a s s r a n k i − M ( 1 + M ) 2 M ∗ N AUC = {\sum_{i \in positive Class}rank_{i}-{M(1+M)\over 2} \over {M*N}} AUC=M∗N∑i∈positiveClassranki−2M(1+M)
- AUC 只反应模型对正负样本排序能力的强弱,对score的大小和精度没有要求
- AUC越高 说明模型排序的能力越强 模型把所有正样本都排在负样本之前时,AUC为1
- from sklearn.metrics import roc_auc_score
- 注意两种预测结果函数
- predict() 返回的是标签值
- predict_proba() 预测属于某标签的概率
3. 查看数据样例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
user_info = pd.read_csv('../datasets/data_format1/user_info_format1.csv')
user_log = pd.read_csv('../datasets/data_format1/user_log_format1.csv')
test_data = pd.read_csv('../datasets/data_format1/test_format1.csv')
train_data = pd.read_csv('../datasets/data_format1/train_format1.csv')
4. 缺失值查看
# 年龄缺失值查看
user_age_miss_pro = (user_info.shape[0]-user_info['age_range'].count())/user_info.shape[0]
print("年龄缺失率:{:.3}%".format(user_age_miss_pro*100))
# 年龄未知个数
user_age_un_count = user_info[user_info['age_range'].isna() |
(user_info['age_range']==0)]['age_range'].count()
print("年龄未知及缺失值个数:{},总样本数:{}".format(user_age_un_count,user_info.shape[0]))
# 每个年龄层个数
user_info.groupby(['age_range'])['user_id'].count()
'''
年龄缺失率:0.523%
年龄未知及缺失值个数:92914,总样本数:424170
age_range
0.0 92914
1.0 24
2.0 52871
3.0 111654
4.0 79991
5.0 40777
6.0 35464
7.0 6992
8.0 1266
Name: user_id, dtype: int64
'''
# 性别缺失值查看
user_age_miss_pro = (user_info.shape[0]-user_info['gender'].count())/user_info.shape[0]
print("性别缺失率:{:.3}%".format(user_age_miss_pro*100))
# 性别未知个数
user_age_un_count = user_info[user_info['gender'].isna() |
(user_info['gender']==2)]['gender'].count()
print("性别未知及缺失值个数:{},总样本数:{}".format(user_age_un_count,user_info.shape[0]))
# 每个性别个数
user_info.groupby(['gender'])['user_id'].count()
'''
性别缺失率:1.52%
性别未知及缺失值个数:10426,总样本数:424170
gender
0.0 285638
1.0 121670
2.0 10426
Name: user_id, dtype: int64
'''
# user_log缺失
user_log.isna().sum()
'''
user_id 0
item_id 0
cat_id 0
seller_id 0
brand_id 91015
time_stamp 0
action_type 0
dtype: int64
'''
5. 查看数据分布
# 数据分布
label = train_data.groupby('label')['user_id'].count()
print('正负样本的数量:\n',label)
plt.figure(figsize=(12,6))
plt.subplot(1,2,1)
# autopct 保留几位小数 shadow 阴影 explode 偏移量
# value_counts()
train_data.label.value_counts().plot(kind='pie',autopct='%1.2f%%',shadow=True,explode=[0,0.3])
plt.subplot(1,2,2)
# countplot()
sns.countplot('label',data=train_data)
6. 探究影响复购的各种因素
# 对店铺的分析 购买次数和是否购买图
print('选取top5店铺')
top_5_merchant = train_data.merchant_id.value_counts().head(5).index.tolist()
print(top_5_merchant)
train_data_merchant = train_data.copy()
train_data_merchant['Top'] = train_data_merchant['merchant_id'].apply(lambda x:1 if x in top_5_merchant else 0)
train_data_merchant = train_data_merchant[train_data_merchant['Top'] == 1]
plt.figure(figsize=(8,6))
plt.title('Merchant VS Label')
ax = sns.countplot('merchant_id',hue='label',data=train_data_merchant)
# 性别和复购的关系
train_data_user_info = train_data.merge(user_info,on=['user_id'],how='left')
plt.figure(figsize=(8,8))
plt.title('Gender VS Label')
ax = sns.countplot('gender',hue='label',data=train_data_user_info)
# 年龄和复购的关系
plt.figure(figsize=(8,8))
plt.title('Age VS Label')
ax = sns.countplot('age_range',hue='label',data=train_data_user_info)