项目概述
这个数据集是一些模拟 Starbucks rewards 移动 app 上用户行为的数据。每隔几天,星巴克会向 app 的用户发送一些推送。这个推送可能仅仅是一条饮品的广告或者是折扣券或 BOGO(买一送一)。一些顾客可能一连几周都收不到任何推送。顾客收到的推送可能是不同的,这就是这个数据集的挑战所在。
数据集中还包含 app 上支付的交易信息,交易信息包括购买时间和购买支付的金额。交易信息还包括该顾客收到的推送种类和数量以及看了该推送的时间。顾客做出了购买行为也会产生一条记录。同样需要记住有可能顾客购买了商品,但没有收到或者没有看推送。
问题描述
有三种可以发出的推送:买一送一(BOGO)、折扣推送和消息型推送。在 BOGO 推送中,用户需要花费一定数额的钱来获得等值的奖励。在折扣推送中,用户获得消费额度的某个比例的奖励。在消息型推送中,用户不会收到任何奖励,但用户也不会有任何最低消费要求。推送可以通过多种渠道传递。
每种推送都有有效期。例如,买一送一(BOGO)优惠券推送的有效期可能只有 5 天。你会发现数据集中即使是一些消息型的推送都有有效期,哪怕这些推送仅仅是饮品的广告,例如,如果一条消息型推送的有效期是 7 天,你可以认为是该顾客在这 7 天都可能受到这条推送的影响。
数据集如下,一共有三个数据文件:
- portfolio.json – 包括推送的 id 和每个推送的元数据(持续时间、种类等等)
-profile.json – 每个顾客的人口统计数据
-transcript.json – 交易、收到的推送、查看的推送和完成的推送的记录
以下是文件中每个变量的类型和解释 :
portfolio.json 30 天试验期间的推送(10 个推送 X 6 个字段)
- id (string) – string/hash,推送的id
- offer_type (string) – 推送的种类,例如 BOGO、打折(discount)、信息(informational)
- difficulty (int) – 数值变量,满足推送的要求所需的最少花费,即最低消费额度
- reward (int) – 数值变量,满足推送的要求后给与的优惠
- duration (int) – 数值变量,推送持续的时间,单位是天
- channels 字符串列表 - 网站 web;邮件 email;短信 mobile;社交平台 social
profile.json 得到奖励的顾客(17000 个用户 x 5 个字段)
- age (int) – 顾客的年龄 ,数值变量,缺失值编码为118
- became_member_on (int) – 该顾客第一次注册app的时间,日期格式 - YYYYMMDD
- gender (str) – 顾客的性别(注意除了表示男性的 M 和表示女性的 F 之外,还有表示其他的 O或者null)
- id (str) – string/hash,顾客id
- income (float) – 顾客的收入,数值变量
transcript.json 事件记录 (306648 个事件 x 4 个字段)
-
event (str) – 记录的描述(比如交易记录、推送已收到、推送已阅)string,收到的推送 offer received;推送已阅offer viewed;交易记录 transaction;完成的推送交易 offer completed
-
person (str) – string/hash,顾客id
-
time (int) – 数值变量,单位是小时,测试开始时计时。该数据从时间点 t=0 开始
-
value - (dict of strings) – 推送的id 或者交易的数额。字典,类型根据事件的类型而变
- offer id:(string/hash) 推送 id
- amount:(数值变量) 交易额
- reward:(数值变量) 满足推送的要求后得到的优惠
本项目的基本任务是通过分析各种数据(人口的统计信息、移动 App 上的交易信息,以及促销广告信息)对星巴克的顾客进行细分,来识别各类人群各自最喜欢的推送是什么?以及如何最好地使用这些推送来激发顾客消费?
所以,本项目的研究目的是:根据提供的 portfolio.json、profile.json以及transcript.json数据集,搭建一个模型去预测某顾客是否对推送进行反应(这里的反应是指:顾客接收到推送通知到交易完成的这段时间是否在推送时间之内,是的话表示顾客做出了正反馈,否代表顾客做出了负反馈)。
解决思路
1、锁定目标客户
由于项目数据集中没有提供标签,所以我们要自定义标签。目前,在这项目中,我是用自定义的 valid_transaction 作为标签,其中,值为1表示 有效交易;值为0表示 无效交易。
2、数据集加载以及预处理
其中,预处理包括 空值情况、分类标签转换为独热编码(例如:portfolio中的channels、offer_type列等)、统一列名称(例如:将profile数据集中的 id列 重命名为 person)等。
3、定义函数,提取出交易记录中所有person的交易情况(从接收到消息推送到交易完成,期间经历的时间小于或等于该推送的duration时间即为合法,其他情况为非合法)
主要步骤如下:
- 合并预处理后的profile和transcript数据集并按照person列分组及排序;
- 遍历新数据集;
- 记录 每个顾客在接收到推送通知到交易完成的这段时间是否在推送时间之内 的所有记录(每个顾客接收到多条同样的推送通知只取一条记录)
- 最后,返回所有连接好的交易条目。
4、数据可视化;
5、模型的评估与验证。
评价指标
本文主要是预测顾客是否对推送进行正反馈,这是个二分类任务。分类任务常见的评价指标有准确率(Accuracy)、精确率(Precision)、召回率(Recall)、F1 score、ROC曲线(Receiver Operating Characteristic Curve)等。
准确率(Accuracy):正确分类的样本占总样本的比例,对总体准确率的评估。
精确率(Precision):在被识别为正类别的样本中,确实为正类别的比例是多少?
召回率(Recall):在所有正类别样本中,被正确识别为正类别的比例是多少?
混淆矩阵(Confusion Matrix):对于二分类问题,可将样例根据其真实类别与分类器预测类别的组合进行划分:
- 真正例(Ture Positive,TP),将正类预测为正类的数量,即预测正确;
- 真负例(True Negative,TN),将负类预测为负类的数量,即预测正确;
- 假正例(False Positive,FP),将负类预测为正类的数量,即预测错误--误报;
- 假负类(False Negative,FN),将正类预测为负类的数量,即预测错误--漏报。
ROC曲线:一种显示分类模型在所有分类阈值下的效果的图表。
F1 score:F1值为算数平均数除以几何平均数,且越大越好。
这个数据集中存在数据类别不平衡的问题,例如:在接收到推送通知的顾客中,只有很小部分顾客对推送做出反应(打开推送或产生消费),考虑到如果使用准确率作为评估目标,要先对数据进行平衡处理;另外,推送通知也算是一种广告推广,对于不希望接收到推送通知的顾客,我们不应该给他们发起推送,应该尽量只给潜在顾客或者目标顾客发送相对应的推送通知。所以,在上述评估指标中,我选取F1 score作为最终的评估目标,
探索性数据分析
导入数据以及相关的库:![](https://img.haomeiwen.com/i1938492/f9bab62f8731faeb.png)
![](https://img.haomeiwen.com/i1938492/396b9739e7974c28.png)
![](https://img.haomeiwen.com/i1938492/98fbb91ba868a217.png)
由上述可知,profile数据集中,gender、income各有2175个缺失值
分析 profile数据集 空值原因:![](https://img.haomeiwen.com/i1938492/233df1e6ef89652d.png)
![](https://img.haomeiwen.com/i1938492/5ea82cd236f2c05e.png)
数据预处理
对 portfolio数据集的操作(重命名id列为offer_id、offer_type以及channels进行独热编码):![](https://img.haomeiwen.com/i1938492/dbe186fe2c258ee4.png)
![](https://img.haomeiwen.com/i1938492/2e1c0ef909ca4d4c.png)
在对profile数据集进行空值计数时:gender为null时,对应的顾客年龄均为118岁,这里的数据也许是个脏数据,可能是记录有误导致的,而且gender为null占profile整个数据集比例为12.79%,占的比例不是很大,这里考虑将 所有空值 都删除。
# 删除profile中所有的空值
profile.dropna(inplace=True)
将profile数据集中的 id列 重命名为 person
profile.rename(columns={'id': 'person'}, inplace=True)
根据person值连接 profile和transcript数据集
profile_transcript_df = profile.join(transcript.set_index('person'), on='person')
profile_transcript_df.reset_index(drop=True, inplace=True)
获取所有person的交易情况(从接收到消息推送到交易完成,期间经历的时间小于或等于该推送的duration时间即为合法,其他情况为非合法),并组合为新的DataFrame。
![](https://img.haomeiwen.com/i1938492/55f3a6e9c32712ae.png)
![](https://img.haomeiwen.com/i1938492/6da424b2e4fb55ea.png)
数据可视化
没有收到推送消息与收到推送消息的情况下去购买星巴克饮品的占比图:![](https://img.haomeiwen.com/i1938492/1aa4da3098a702bc.png)
由饼状图可知,极少数人会在没有消息推送的情况下去购买星巴克饮品。
transcript数据集中时间记录饼状图:![](https://img.haomeiwen.com/i1938492/d019614f9d7610a5.png)
从上面饼状图可知,交易记录占了45.33%,比重比较大,这表明客户还是比较喜欢购买星巴克饮品。
不同年龄段购买星巴克饮品的分布图:![](https://img.haomeiwen.com/i1938492/04cc221550d5686b.png)
由上图可知,购买星巴克饮品以中老年居多,其中中年占比最大。
对交易数据进行时间序列数据的分析 (transcript表的time列):![](https://img.haomeiwen.com/i1938492/e07f56e35ae8fac9.png)
由上图可知,在18天的时候,transaction的交易数目最多。其次是第25天。每次新一轮的offer received总是在offer viewed和offer completed的最低点过后的一天触发。
热力图(移除邮箱列):![](https://img.haomeiwen.com/i1938492/f571becd8c3fcf19.png)
由热力图可知,与valid_transaction相关性较强的有以下特征:duration、difficulty、offer_type_discount、reward以及channels_web,与特征offer_type_informational相关性最低。而且目前相关性最大的值只是0.35,这表明所有特征与valid_transaction的相关性都不是很好。
模型的评估与验证
拆分数据集与验证集
y = person_offer_status_new_df['valid_transaction']
# cols = ['income', 'difficulty', 'duration', 'reward', 'offer_type_discount', 'channels_web', 'gender_F']
# X = person_offer_status_new_df[cols]
X = person_offer_status_new_df.drop(['person', 'became_member_on', 'offer_id', 'valid_transaction'], axis=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
使用逻辑回归模型进行预测:
![](https://img.haomeiwen.com/i1938492/ca8482c60609712d.png)
![](https://img.haomeiwen.com/i1938492/14904659a3d7cf42.png)
![](https://img.haomeiwen.com/i1938492/72104a50f4705f2c.png)
![](https://img.haomeiwen.com/i1938492/172abf59a13a612e.png)
刚开始使用 LogisticRegression模型去预测,F1 Score为: 0.70,预测执行时间将近300ms,其次是RandomForestClassifier模型,F1 Score为: 0.71,预测执行时间420ms左右,最后是AdaBoostClassifier模型,F1 Score为: 0.78,预测执行时间1.3s左右。由于AdaBoostClassifier 模型的F1 score得分最高,虽然预测时间比其他两种算法较长,但还可以接受;另外,AdaBoostClassifier模型用错分数据点来识别问题,通过调整错分数据点的权重(增大分错样本权重,降低分对样本权重)来改进模型,有利于找出对推送通知做出反应的精准用户而又不会乱发送推送通知。所以,综上所述,最终使用AdaBoostClassifier模型来进行本项目的预测。
模型改进
获取自变量的重要性情况并按照重要性进行排序,重新筛选用来训练的特征
# 自变量的重要性排序
importance = pd.Series(ada_model.feature_importances_, index = X_train.columns)
importance.sort_values().plot(kind='barh')
plt.show()
![](https://img.haomeiwen.com/i1938492/b388c8304832af5a.png)
![](https://img.haomeiwen.com/i1938492/387e8fe5a775131c.png)
![](https://img.haomeiwen.com/i1938492/76fea1397341a268.png)
讨论结果
由上面模型预测的结果来看,目前通过GridSearchCV调参,并用最优模型(learning_rate=0.1, n_estimators=300,base_estimator使用max_depth=3的DecisionTreeClassifier分类器)去预测,得到的F1 Score只为0.77左右,分值并不是很高,这表明该模型还需要再改进。我想导致得分不高的一部分原因是:要预测的 valid_transaction 特征跟其他特征相关性不是很强,最高也只是0.35。这其中可能是由于数据集不平衡导致的,另外,如果要提高F1 Score,可尝试换别的分类算法去预测。
反思
- 在本项目中,我最终利用AdaBoostClassifier模型,将'age', 'income', 'difficulty', 'duration'作为重要性特征进行预测,预测顾客对不同推送通知做出的反馈效果,从而定向地向这一类顾客推送消息。
- 从最开始的数据预处理(主要是空值处理、独热编码以及统一列名、数据集合并,自定义标签),再到数据可视化(接收到推送通知与不接收到推送通知对购买星巴克饮品的影响程度、不同年龄段购买星巴克饮品的情况等),然后到模型的评估与验证(LogisticRegression、RandomForestClassifier以及AdaBoostClassifier模型的预测及运行时间,最后采取AdaBoostClassifier模型去进行预测),最后,选取重要性特征、选取最优模型去改进模型的预测情况(虽然最后面也没啥改善...);
- 在本项目中,我个人觉得比较难的是自定义标签:valid_transaction,也即是计算 所有person的交易情况(从接收到消息推送到交易完成,期间经历的时间小于或等于该推送的duration时间即为合法,其他情况为非合法)、即 get_person_offer_status_df方法里面的逻辑,这块代码逻辑花了我很长时间去实现;另外,在模型选取方面,我也是选取了个人常用的分类算法,其他的分类算法还没去验证。
改进
- 目前使用了最优模型去预测,得分并不是很高,可尝试换别的分类算法去预测;
- 年龄特征可以按照某个区间来分割(我只在数据可视化那里有处理),然后再连接到 person_offer_status_new_df 数据集中进行预测;
- 在执行方法 get_person_offer_status_df 的过程中,程序运行太久,至少11分钟,可尝试用 vaex(基于python的数据处理第三方库) 去优化。
疑问
- 我在通过 get_person_offer_status_df(df) 方法获取 person_offer_status_new_df数据集的时候,获取方式是否是正确的?我个人觉得是没问题的;
- 最后建立的模型预测出来的F1 Score分值都不高,这个还有哪些可能导致的因素呢?
- 在绘制 混淆矩阵 的时候,我在mac电脑上使用 Visual Studio Code软件,出来的图像没有偏差;在win10上 使用Visual Studio Code软件,出来的图像坐标往上偏了,而且热力图的坐标也会偏移。
网友评论