京东JData算法大赛——高潜用户购买意向预测

赛题分析

京东提供了用户在2016-02-01-2016-04-15时间区域内用户对商品的行为评价等数据,预测2016-04-16-2016-04-20时间区间内用户的下单情况。
赛题和阿里的第一个竞赛天猫推荐竞赛差不多,记得那时候也有个好心的同学提供了代码的框架,自己胡改一通还进了复赛,总之学习到很多东西。同学们可以找找当时的资料看看,不过现在大家的水平比那时候可高多了。
作为不严谨的效果一般的start code,其目的是为了帮助像我当时一样苦于无思路的朋友们入门这个竞赛,同时希望大家能够取得好成绩并且有所收获。
本文介绍如何用python实现:

  • 如何构造训练集/测试集
  • 时间滑动窗口特征的构造
  • 评测代码
  • 应用xgboost模型

类别特征处理

pandas处理特征非常好用,如下代码处理用户属性表,利用pd.get_dummies方法将类别特征进行编码。
def convert_age(age_str):
    if age_str == u'-1':
        return 0
    elif age_str == u'15岁以下':
        return 1
    elif age_str == u'16-25岁':
        return 2
    elif age_str == u'26-35岁':
        return 3
    elif age_str == u'36-45岁':
        return 4
    elif age_str == u'46-55岁':
        return 5
    elif age_str == u'56岁以上':
        return 6
    else:
        return -1

def get_basic_user_feat():
    dump_path = './cache/basic_user.pkl'
    if os.path.exists(dump_path):
        user = pickle.load(open(dump_path))
    else:
        user = pd.read_csv(user_path, encoding='gbk')
        user['age'] = user['age'].map(convert_age)
        age_df = pd.get_dummies(user["age"], prefix="age")
        sex_df = pd.get_dummies(user["sex"], prefix="sex")
        user_lv_df = pd.get_dummies(user["user_lv_cd"], prefix="user_lv_cd")
        user = pd.concat([user['user_id'], age_df, sex_df, user_lv_df], axis=1)
        pickle.dump(user, open(dump_path, 'w'))
    return user

滑动窗口特征

赛题的要求是预测未来几天用户是否购买某个类目下的商品,经过简单的分析大概有30%的购买是有过行为记录的,所以我们的重点是预测在用户在有过行为记录下的购买情况。

我们现在有用户浏览/点击/购物车等信息的日志,比较简单特征提取方法有:

  • 滑动窗口
  • 时间衰减

在这个不严谨的start code中,我利用滑动窗口处理行为数据。可能有些人要问,什么叫滑动窗口,简单的可以这样理解。用户在购买商品之前,可能会对商品已经有过不同的行为,近期的行为影响较大,远期的行为影响较少。建立在这个逻辑下,我们可以统计不同时间区间内用户的行为累积。

示例代码: 统计时间区间内行为累积

def get_action_feat(start_date, end_date):
    dump_path = './cache/action_accumulate_%s_%s.pkl' % (start_date, end_date)
    if os.path.exists(dump_path):
        actions = pickle.load(open(dump_path))
    else:
        actions = get_actions(start_date, end_date)
        actions = actions[['user_id', 'sku_id', 'type']]
        df = pd.get_dummies(actions['type'], prefix='%s-%s-action' % (start_date, end_date))
        actions = pd.concat([actions, df], axis=1)  # type: pd.DataFrame
        actions = actions.groupby(['user_id', 'sku_id'], as_index=False).sum()
        del actions['type']
        pickle.dump(actions, open(dump_path, 'w'))
    return actions

统计距离预测开始日期前1、2、3、5、7、10、15、21、30的行为累积,别问我为什么这样设计,随手写的。
actions = None
for i in (1, 2, 3, 5, 7, 10, 15, 21, 30):
    start_days = datetime.strptime(train_end_date, '%Y-%m-%d') - timedelta(days=i)
    start_days = start_days.strftime('%Y-%m-%d')
    if actions is None:
        actions = get_action_feat(start_days, train_end_date)
    else:
        actions = pd.merge(actions, get_action_feat(start_days, train_end_date), how='left',on=['user_id', 'sku_id'])

累积特征

start_code中还用到了用户和商品的购买率等累积特征,代码就不贴了。克隆项目即可。

训练集/测试集划分

训练集: [t, t+30] [t+31, t+35]

测试集: [t+5, t+35] [t+36, t+40]

评测代码

由于线上一天只能提交两次,如果需要不断的优化效果,就需要本地的评测代码。
def report(pred, label):

    actions = label
    result = pred

    # 所有用户商品对
    all_user_item_pair = actions['user_id'].map(str) + '-' + actions['sku_id'].map(str)
    all_user_item_pair = np.array(all_user_item_pair)
    # 所有购买用户
    all_user_set = actions['user_id'].unique()

    # 所有品类中预测购买的用户
    all_user_test_set = result['user_id'].unique()
    all_user_test_item_pair = result['user_id'].map(str) + '-' + result['sku_id'].map(str)
    all_user_test_item_pair = np.array(all_user_test_item_pair)

    # 计算所有用户购买评价指标
    pos, neg = 0,0
    for user_id in all_user_test_set:
        if user_id in all_user_set:
            pos += 1
        else:
            neg += 1
    all_user_acc = 1.0 * pos / ( pos + neg)
    all_user_recall = 1.0 * pos / len(all_user_set)
    print '所有用户中预测购买用户的准确率为 ' + str(all_user_acc)
    print '所有用户中预测购买用户的召回率' + str(all_user_recall)

    pos, neg = 0, 0
    for user_item_pair in all_user_test_item_pair:
        if user_item_pair in all_user_item_pair:
            pos += 1
        else:
            neg += 1
    all_item_acc = 1.0 * pos / ( pos + neg)
    all_item_recall = 1.0 * pos / len(all_user_item_pair)
    print '所有用户中预测购买商品的准确率为 ' + str(all_item_acc)
    print '所有用户中预测购买商品的召回率' + str(all_item_recall)
    F11 = 6.0 * all_user_recall * all_user_acc / (5.0 * all_user_recall + all_user_acc)
    F12 = 5.0 * all_item_acc * all_item_recall / (2.0 * all_item_recall + 3 * all_item_acc)
    score = 0.4 * F11 + 0.6 * F12
    print 'F11=' + str(F11)
    print 'F12=' + str(F12)
    print 'score=' + str(score)

xgboost模型

为什么选择xgboost模型,因为树模型对特征处理的要求不高而且效果也相当不错,不管是类别特征,连续特征效果都很好。在Kaggle竞赛中,该模型通常能取得较好成绩。
user_index, training_data, label = make_train_set(train_start_date, train_end_date, test_start_date, test_end_date)
X_train, X_test, y_train, y_test = train_test_split(training_data.values, label.values, test_size=0.2, random_state=0)    dtrain=xgb.DMatrix(X_train, label=y_train)
dtest=xgb.DMatrix(X_test, label=y_test)
param = {'max_depth': 6, 'eta': 0.05, 'silent': 1, 'objective': 'binary:logistic'}
num_round = 309
param['nthread'] = 4
plst = param.items()
plst += [('eval_metric', 'logloss')]
evallist = [(dtest, 'eval'), (dtrain, 'train')]
bst=xgb.train(plst, dtrain, num_round, evallist)
sub_user_index, sub_trainning_data = make_test_set(sub_start_date, sub_end_date,)
sub_trainning_data = xgb.DMatrix(sub_trainning_data.values)
y = bst.predict(sub_trainning_data)

全部代码链接:https://github.com/foursking1/jd

数据官网下载:http://www.datafountain.cn/projects/jdata/











你可能感兴趣的:(数据挖掘项目实例)