提供用户在2016年1月1日至2016年6月30日之间真实线上线下消费行为,预测用户在2016年7月领取优惠券后15天以内的使用情况。
一、总体分析
dfoff = pd.read_csv('data/ccf_offline_stage1_train.csv')
dfon = pd.read_csv('data/ccf_online_stage1_train.csv')
dftest = pd.read_csv('data/ccf_offline_stage1_test_revised.csv')
print('有优惠卷,购买商品:%d' % dfoff[(dfoff['Date_received'].notnull()) & (dfoff['Date'].notnull())].shape[0])
print('有优惠卷,未购商品:%d' % dfoff[(dfoff['Date_received'].notnull()) & (dfoff['Date'].isnull())].shape[0])
print('无优惠卷,购买商品:%d' % dfoff[(dfoff['Date_received'].isnull()) & (dfoff['Date'].notnull())].shape[0])
print('无优惠卷,未购商品:%d' % dfoff[(dfoff['Date_received'].isnull()) & (dfoff['Date'].isnull())].shape[0])
结果如下:
有优惠卷,购买商品:75382 有优惠卷,未购商品:977900 无优惠卷,购买商品:701602 无优惠卷,未购商品:0
可见,很多人(701602)购买商品却没有使用优惠券,也有很多人(977900)有优惠券但却没有使用
二、数据集划分:
预测未来N天的流量(人流,销售等),预测未来N天内的用户-对象对(用户-商品等)的问题等,可以利用滑窗法解决。题目描述为预测未来N天的息,其中N的取值为大于等于1。基本诸如此种问题,题目会给出前X天的详细信息。这样,我们就知道,其完整的数据为 X+N 的形式,其中 N 是需要我们预测的部分,其真实值未知。
需要在 X 中构造出与 X+N 格式一致的样本,这种情况下,切分X为两个部分,[X-M,M],其中M=N的长度。这样,就拥有了带有标签的数据,其中M是我们线下预测的部分其本质是符合线上的N的部分,而X-M类似于在[X,N]中的X部分。普遍在代码实现时,在[X-M,M]区间中,我们会首先提取M中的真实值Y和唯一标识ID,之后再X-M中不停的统计,最后通过唯一标识ID拼接到这个M中。
本比赛中,考虑采用滑窗法,将ccf_offline_stage1_train.csv数据如下划分:
训练集1: 将2016年1月1日到4月13日的数据提取特征,利用4月14日的到5月14日的提取label作为训练集1
训练集2: 将2月1日到5月14日的作为数据集提取特征,利用5月15日6月15日的提取label作为训练集2
测试集: 将3月15日到6月30日作为数据集提取特征,再测试7月1日到7月31日的数据
dataset3 = dftest
feature3 = dfoff[((dfoff.date>=20160315)&(dfoff.date<=20160630))|((dfoff.date != dfoff.date )&(dfoff.date_received>=20160315)&(dfoff.date_received<=20160630))]
dataset2 = dfoff[(dfoff.date_received>=20160515)&(dfoff.date_received<=20160615)]
feature2 = dfoff[(dfoff.date>=20160201)&(dfoff.date<=20160514)|((dfoff.date != dfoff.date)&(dfoff.date_received>=20160201)&(dfoff.date_received<=20160514))]
dataset1 = dfoff[(dfoff.date_received>=20160414)&(dfoff.date_received<=20160514)]
feature1 = dfoff[(dfoff.date>=20160101)&(dfoff.date<=20160413)|((dfoff.date != dfoff.date )&(dfoff.date_received>=20160101)&(dfoff.date_received<=20160413))]
三、特征工程
1、其他特征
这部分特征在训练集1、训练集2和测试集1中提取,即分别在dataset1、dataset2和dataset3中提取,共计8个特征,如下:
this_month_user_receive_all_coupon_count 用户领取的所有优惠券数目
this_month_user_receive_same_coupon_count 用户领取的特定优惠券数目
this_month_user_receive_same_coupon_lastone 特定优惠券是否是用户这个月最新领取的时间
this_month_user_receive_same_coupon_firstone 特定优惠券是否是用户这个月最早领取的时间
this_day_user_receive_all_coupon_count 用户当天领取的优惠券数目
this_day_user_receive_same_coupon_count 用户当天领取的特定优惠券数目
day_gap_before 用户上一次领取的时间间隔
day_gap_after 用户下一次领取的时间间隔
2、商家相关特征
特征集合中提取,共计9个特征
total_sales 商家做了多少笔生意
sales_use_coupon 商家被核销过的不同优惠券数量
total_coupon 商家发放的优惠券总数
merchant_min_distance 商家被核销优惠券中的最小用户-商家距离
merchant_max_distance 商家被核销优惠券中的最大用户-商家距离
merchant_mean_distance 商家被核销优惠券中的平均用户-商家距离
merchant_median_distance 商家被核销优惠券中的平均用户-商家距离中位数
merchant_coupon_transfer_rate 商家优惠券被领取后核销率
coupon_rate 使用优惠券占商家总的销售笔数
3、用户线下相关的特征
特征集合中提取,共计13个特征
count_merchant 用户购买的商家个数
user_min_distance 用户使用优惠券购买商品距离商店的最小距离
user_max_distance 用户使用优惠券购买商品距离商店的最大距离
user_mean_distance 用户使用优惠券购买商品距离商店的平均距离
user_median_distance 用户使用优惠券购买商品距离商店的中位数距离
buy_use_coupon 用户使用优惠券购买的次数
buy_total 用户购买的次数
coupon_received 用户接收到优惠券的数量
user_date_datereceived_gap 用户使用优惠券消费与领取优惠券的时间间隔
avg_user_date_datereceived_gap 用户使用优惠券的平均时间间隔
min_user_date_datereceived_gap 用户使用优惠券的最小时间间隔
max_user_date_datereceived_gap 用户使用优惠券的最大时间间隔
buy_use_coupon_rate 用户所有购买行为中使用优惠券占的比例
user_coupon_transfer_rate 对于用户来说,接收到的优惠券使用率
4、用户和商户 User & Merchant 交叉特征
这部分也是特征集合中提取,共计9个特征
user_merchant_buy_tota 每个用户商户对交易统计
user_merchant_received 每个用户商户对发放优惠券的统计
user_merchant_buy_use_coupon 每个用户商户对使用优惠卷的交易行为的统计
user_merchant_any 用户和商户对统计
user_merchant_buy_common 用户和商户对交易不使用优惠券统计
user_merchant_coupon_transfer_rate 使用优惠卷的交易行为占所有用户商户对发放优惠卷的比例
user_merchant_coupon_buy_rate 使用优惠卷的交易行为占所有用户商户对交易行为的比例
user_merchant_rate 每个用户商户对交易行为占所有用户商户对的比例
user_merchant_common_buy_rate 用户和商户对交易不使用优惠券占所有用户商户对交易行为的比例
5、优惠券特征
这部分特征在训练集1、训练集2和测试集1中提取,即分别在dataset1、dataset2和dataset3中提取,共计8个特征
day_of_week 领取优惠券是一周的第几天
day_of_month 领取优惠券是一月的第几天
days_distance 距离6月30日天数
discount_man 满
discount_jian 减
is_man_jian 是否满减
discount_rate 打折率
coupon_count
四、训练集和测试集
is_weekend 是否周末
weekday1、weekday2....weekday7 独热编码
label 标签
最后dataset1、dataset2在加上上面9个特征,去掉'merchant_id','day_of_week','date','date_received','coupon_id','coupon_count'后,共计56个字段
dataset3去掉'merchant_id','day_of_week','coupon_count',共计57个字段
五、模型训练
将训练集1和训练集2合并后,作为训练集
dataset12 = pd.concat([dataset1,dataset2],axis=0)
dataset1_y = dataset1.label
dataset1_x = dataset1.drop(['user_id','label','day_gap_before','day_gap_after'],axis=1)
dataset2_y = dataset2.label
dataset2_x = dataset2.drop(['user_id','label','day_gap_before','day_gap_after'],axis=1)
dataset12_y = dataset12.label
dataset12_x = dataset12.drop(['user_id','label','day_gap_before','day_gap_after'],axis=1)
dataset3_preds = dataset3[['user_id','coupon_id','date_received']]
dataset3_x = dataset3.drop(['user_id','coupon_id','date_received','day_gap_before','day_gap_after'],axis=1)
去掉部分特征后,最终训练的特征共计52个,
predictors = dataset12_x.columns
trainSub, validSub = train_test_split(dataset12, test_size = 0.2, stratify = dataset12['label'], random_state=100)
import lightgbm as lgb
from sklearn.model_selection import KFold, train_test_split
model = lgb.LGBMClassifier(
learning_rate = 0.01,
boosting_type = 'gbdt',
objective = 'binary',
metric = 'logloss',
max_depth = 5,
sub_feature = 0.7,
num_leaves = 3,
colsample_bytree = 0.7,
n_estimators = 5000,
early_stop = 50,
verbose = -1)
model.fit(trainSub[predictors], trainSub['label'])
y_valid_pred = model.predict_proba(validSub[predictors])
validSub1 = validSub.copy()
validSub1['pred_prob'] = y_valid_pred[:, 1]
validSub1.head()
#测试
y_test_pred = model.predict_proba(dataset3[predictors])
submit = dataset3_preds.copy()
submit.to_csv('submit8.csv', index=False, header=False)
submit.head()