├── .gitignore ├── README.md ├── data_analysics.ipynb ├── feature_engineering ├── __init__.py ├── basic_feature.py ├── product_repay_feature.py ├── user_repay_feature1.py ├── user_repay_feature2.py ├── user_repay_feature3.py ├── user_repay_samples.py └── user_tag_embedding.py ├── main.py ├── pictures ├── 用户.png ├── 用户提前还款分布统计.png ├── 用户还款日志的每日还款量统计.png ├── 训练集用户每日还款量统计.png └── 预测误差分析.png ├── train_model ├── __init__.py └── train.py └── utils ├── __init__.py ├── __pycache__ ├── __init__.cpython-37.pyc └── functions.cpython-37.pyc └── functions.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | dataset/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 第四届魔镜杯资金流动性管理 建模分享 2 | 3 | ## 0. 其他解决方案链接 4 | [冠军队伍分享](https://zhuanlan.zhihu.com/p/75199206) 5 | [亚军队伍分享](https://zhuanlan.zhihu.com/p/74749772) 6 | 7 | ## 1. 项目背景 8 | 资金流动性管理迄今仍是金融领域的经典问题。在互联网金融信贷业务中,单个资产标的金额小且复杂多样,对于拥有大量出借资金的金融机构或散户而言,资金管理压力巨大,精准地预测出借资金的流动情况变得尤为重要。本次比赛以互联网金融信贷业务为背景,以《现金流预测》为题,希望选手能够利用我们提供的数据,精准地预测资产组合在未来一段时间内每日的回款金额。 本赛题涵盖了信贷违约预测、现金流预测等金融领域常见问题,同时又是复杂的时序问题和多目标预测问题。希望参赛者利用聪明才智把互联网金融的数据优势转化为行业解决方案。 9 | ![赛题说明](https://aifile.ppdai.com/752890cd0f9e4aa0b36d529bd4433580..png) 10 | 11 | ## 2. 数据介绍 12 | 13 | 本赛题对回款预测问题进行了简化,选手需要分别预测**每个资产标的第一期从成交日期至第一期应还款日期每日的还款金额,并最终在整体上以资产组合每日还款的误差作为评价指标。** 14 | 15 | 我们提供了2018年1月1日至2018年12月31日的标的第一期的还款数据作为训练集,需要选手预测2019年2月1日至2019年3月31日成交标的第一期的还款情况。同时还提供了相关的标的属性信息,借款用户基础信息、画像标签和行为日志等数据供选手使用。 16 | 17 | **注:为了保护用户隐私和比赛的公正性,数据经过脱敏处理,同时进行了必要过滤和有偏采样。** 18 | 19 | **数据集描述** 20 | 21 | **1\. 样本集(train.csv和test.csv)** 22 | 23 | 本赛题提供的样本集包含训练集(train.csv)和测试集(test.csv),它们的数据表结构基本一致,但测试集中不含实际还款信息。整个样本集共有约113万个标的和90万位借款用户,部分借款用户可能有多个标的记录,但在测试集时间范围内每位用户只有一条记录。 24 | 25 | ![](https://aifile.ppdai.com/d38f4e16e9fc41d9b20ea14998b71227..png) 26 | 27 | 注:上述的数据都经过脱敏处理,脱敏后的数据分布和数值大小关系与脱敏前基本一致。 28 | 29 | **2\. 标的属性表(listing_info.csv)** 30 | 31 | 标的属性表包含了本赛题涉及的所有标的,包括:(1) 样本集中所有标的;(2) 样本集中所有借款用户在过去一段时间内成交和还款的标的。标的属性信息在成交时确定,后续不再变更。 32 | 33 | ![](https://aifile.ppdai.com/89a5bbbb5bc14934957d6aa7ea793b5d..png) 34 | 35 | 注:上述的数据都经过脱敏处理,脱敏后的数据分布和数值大小关系与脱敏前基本一致。 36 | 37 | **3\. 借款用户基础信息表(user_info.csv)** 38 | 39 | 借款用户基础信息表包含了本赛题涉及的所有用户,用户信息可能发生变更,表中同一用户可能存在多条数据。 40 | 41 | ![](https://aifile.ppdai.com/b04e4fc9d3db4c82ac575011356ab02a..png) 42 | 43 | 注:上述的数据都经过脱敏处理,地址类字段为编码数据,城市字段前三位为所属省份ID。 44 | 45 | **4\. 用户画像标签列表(user_taglist.csv)** 46 | 47 | 用户画像标签列表提供了用户的标签信息,用户标签可能发生变更,表中同一用户可能存在多条数据;若在表中无法查到用户标签信息,则表示该用户标签信息未知。 48 | 49 | ![](https://aifile.ppdai.com/7abc249eaac14e6ca59b57e51f7fa13f..png) 50 | 51 | 注:上述的数据都经过脱敏处理。 52 | 53 | **5.** **借款用户操作行为日志表(user_behavior_logs.csv)** 54 | 55 | 借款用户操作行为日志表提供了每位用户在过去一段时间内的操作行为日志数据,行为发生时间精确到秒级,相同用户的相同行为在同一秒内可能出现多条数据。 56 | 57 | ![](https://aifile.ppdai.com/c0dfb50c7e8643589c1a12d72b37d359..png) 58 | 59 | 注:上述的数据都经过脱敏处理。 60 | 61 | **6\. 用户还款日志表(user_repay_logs.csv)** 62 | 63 | 借款用户还款日志表提供了每位用户在过去一段时期内的还款日志数据。 64 | 65 | ![](https://aifile.ppdai.com/48cd690d39ec4e2497a86b4be4f144b5..png) 66 | 67 | 注1:上述的数据都经过脱敏处理,脱敏后的数据分布和数值大小关系与脱敏前基本一致; 68 | 69 | 注2:脱敏后,若还款时本期已逾期,repay_date(实际还款日期)字段默认值为“2200-01-01”,脱敏前实际还款日期一定早于用户最近一个新成交标的成交日期。 70 | 71 | ## 3. 简单数据分析 72 | ### 3.1 训练用户的每日还款量 73 | ![训练集用户每日还款量统计](pictures/%E8%AE%AD%E7%BB%83%E9%9B%86%E7%94%A8%E6%88%B7%E6%AF%8F%E6%97%A5%E8%BF%98%E6%AC%BE%E9%87%8F%E7%BB%9F%E8%AE%A1.png) 74 | 75 | 不同时间的还款量存在差异,月初是还款高峰。具体可以做点分析。 76 | 77 | ### 3.2 用户的还款日志中的还款量统计 78 | ![用户还款日志的每日还款量统计](pictures/%E7%94%A8%E6%88%B7%E8%BF%98%E6%AC%BE%E6%97%A5%E5%BF%97%E7%9A%84%E6%AF%8F%E6%97%A5%E8%BF%98%E6%AC%BE%E9%87%8F%E7%BB%9F%E8%AE%A1.png) 79 | 80 | 感觉比训练集中的数据来说,分布更加平滑,更加符合实际情况。 81 | 82 | ### 3.3 用户提前还款分布情况 83 | ![用户提前还款分布统计1](pictures/%E7%94%A8%E6%88%B7%E6%8F%90%E5%89%8D%E8%BF%98%E6%AC%BE%E5%88%86%E5%B8%83%E7%BB%9F%E8%AE%A1.png) 84 | ![用户提前还款分布统计2](pictures/%E7%94%A8%E6%88%B7.png) 85 | 86 | 该部分历史行为是强特。 87 | 88 | ## 4. 方案整理 89 | ### 规则学习 90 | 在做数据科学项目过程中,可以从规则学习起手。拿什么是规则,西瓜书中有规则学习的定义:规则学习是“符号主义学习”(symbolism learning)的主要代表,是最早开始研究的机器学习技术之一。机器学习中的“规则”(rule)通常是指语义明确、能描述数据分布所隐含的客观规律或领域概念、可写成“若…,则…”形式的逻辑规则。“规则学习”(rule learning)是从训练数据中学习出一组能用于对未见示例进行判别的规则。 91 | 为什么从规则起手比较好,我认为有两个原因: 92 | 1. 找规则过程中往往是你熟悉数据,理解业务的过程,这过程是任何数据工程项目中必不可好并且极为关键的一部分。 93 | 2. 规则通常意味着强特,意味着显著表现。一般规则都可以无缝切入到模型。 94 | 面对这个场景,我先提出的两个简单的规则: 95 | 1. 假设用户都在到期日(due_date)准时还款。 96 | 2. 为了最后的得分表现,我们选择根据还款距离due_date的日期分布情况,加权分配还款金额。 97 | 其他还有部分规则,可以进行探讨,比如逾期率经过成熟的业务控制在未来会降低,比如用户的还款行为各异,比如找到发工资集中的日子等。 98 | 99 | ### 回归算法 100 | 看一下该任务的评价指标:该任务需要预测测试集中所有标的在成交日至第1期应还款日内日的回款金额,预测金额数据应为非负数且保留四位小数。本次比赛主要期望选手对未知资产组合每日总回款金额数据预测得越准越好,因此,不会给出标的归属资产组合序号,并选用RMSE作为评估指标,计算公式如下: 101 | ![评价指标](https://aifile.ppdai.com/db57926066ed44e783dbe7e9a2565144..png) 102 | 103 | 很明显这是一个回归问题,所以我们可以作为回归问题来求解。样本设置为每一个标的在未来每个存在还款的日子里的还款金额。 104 | 在此基础上我训练了模型,效果一般。后来分析得出三个关键问题,并且没有很好的解答。 105 | 1. 每一个标的会产生30个左右的样本,导致训练集扩大了30倍,这对训练的性能带来的挑战。 106 | 2. 每一个标的生成的这30个样本强相关,并且这种相关性如何用特征区分开来 是一个挑战。 107 | 3. 该问题跟问题2本质是一样的,就是该回归问题有约束,每个标的的回归值得和是一个定值,并且训练集中得目标值分布畸形,存在绝大多数的0. 108 | 因为是多任务学习的本质导致了这是一个畸形的回归问题,所以用传统的回归方法求解该问题,在实践上无法取得良好的表现,该问题这里也做一个记录,未来说不定会有新的IDEA产生。 109 | 110 | ### 多分类算法 111 | 最终选择了多分类算法作为求解,通过due_amt*该类概率获得最终的回归值,也就是预测结果。这样的话,没有给标的就是一个样本。类别label的定义,则是还款日到到期日(due_date)的距离.其中逾期设置为第32类。这里需要注意I的是可以设置所有的当天还款为31类。克服每个月的天数不一样带来的影响。 112 | 113 | 该方案也有不足: 114 | 1. 未来每个还款日的特征无法加进去(或许有加进去的办法,但是我没有想到)。当然,该问题可以通过后续的规则进行解决。 115 | 2. 最终计算的到的结果是概率,并且评价指标也是multi_logloss。这和最终结果的RMSE评价指标当中会有所差异。具体的差异刻画没有找到。 116 | 117 | 因为该方案线上表现最好,所以后续的工作在该方案上展开。 118 | 119 | 120 | ## 4. 特征工程 121 | 确定了目标方案为多分类,定义好了训练样本和测试样本后,开始进行特征工程。 122 | 123 | ### 基础特征 124 | * 用户基础特征:性别,年龄,注册时间,地址信息 125 | * 标的基础特征:期数,利率,借款本金,标的起始时间特征 126 | ### 用户还款特征 127 | * 用户在历史n天之内的到期标的次数和金额(n=15,31,90,180) 128 | * 用户在历史n天之内的实际还款的标的数量和金额(n=15,31,90,180) 129 | * 用户在历史n天之内的还款时间统计特征(历史label的统计,包括mean,median,std,max,min) 130 | * 用户在历史n天之内非首期标的提前还款的标的数量和金额(n=15,31,90,180) 131 | * 用户在历史n天之内逾期的标的次数和金额(n=15,31,90,180) 132 | * 用户最近一次还款时间到现在的距离,用户最早一次还款时间到现在的距离 133 | * 用户最近一次借款时间到现在的距离,用户最早一次借款时间到现在的距离 134 | * 用户的历史还款频率 (通过还款时间间隔的统计特征来刻画) 135 | * 用户的历史借款频率 (通过标的借款时间间隔的统计特征来刻画) 136 | * 用户尚未还清的标的中最近需要还的时间到现在的距离。 137 | * 用户未来n天需要还款的标的数量和金额(n=30,60,90) 138 | * 用户历史上还款行为在星期曜日上的分布比例 139 | * 用户历史上还款行为在月初,月中,月末的分布比例 140 | 141 | 这部分是该项目最主要的特征,特征方面记录两点: 142 | 1. 这里学到了通过时间窗口特征采用先join再filter最后groupby的顺序进行操作,能够快速获得,但是比较耗内存 143 | 2. 各种比例特征最好进行平滑操作。 144 | 145 | ### 用户历史行为特征 146 | * 用户行为(1,2,3 脱敏)在n个月(n=1,3,6)内的行为次数 147 | * 用户历史行为在24个小时内的分布比例 148 | * 用户历史行为在四个时间段内的分布比例(0-6,6-12,12-18,16-24) 149 | * 用户历史上各行为(1,2,3)最近一次到当前的时间间隔 150 | * 用户历史上各行为(1,2,3)的行为频率(通过行为顺序的时间间隔的统计特征表示) 151 | 152 | ### 用户标签特征 153 | 这里的用户标签是脱敏的,离散的并且是不等长的。通过Tfidf+TruncatedSVD的方式,抽象为文本主题模型,向量化到10维作为特征。 154 | ```python 155 | from sklearn.feature_extraction.text import TfidfVectorizer 156 | from sklearn.decomposition import TruncatedSVD 157 | vectorizer = TfidfVectorizer() 158 | X = vectorizer.fit_transform(user_tag.taglist.values) 159 | svd_enc = TruncatedSVD(n_components=10, n_iter=20, random_state=2019) 160 | mode_svd = svd_enc.fit_transform(X) 161 | mode_svd = pd.DataFrame(mode_svd) 162 | mode_svd.columns = ['taglist_svd0','taglist_svd1','taglist_svd2','taglist_svd3','taglist_svd4','taglist_svd5','taglist_svd6','taglist_svd7','taglist_svd8','taglist_svd9'] 163 | user_tag = pd.concat([user_tag,mode_svd],axis=1) 164 | ``` 165 | 最近了解了Graph Embedding的一些技术,DeepWalk等,但是没有整理好轮子(对的,是整理不是造),之后计划加入到自己的工具栈中)个人感觉,GraphEmbedding在这里向量化标签应该会取得更好的效果,但是本项目中缺乏业务解释,从实验中发现该特征重要性并不高。 166 | 167 | ### 贷款产品历史表现特征 168 | 项目中并没有明确指定贷款产品,但是对标的的属性给出了 利率,期限和本金三个特征。 假设这三个特征能够代表一个具体的产品。根据产品粒度做了历史的滑窗统计特征。 169 | * 该产品在历史n天之内的到期标的次数(n=15,31,90,180) 170 | * 该产品在历史n天之内的实际还款的标的数量(n=15,31,90,180) 171 | * 该产品在历史n天之内覆盖的用户数和天数(n=15,31,90,180) 172 | * 该产品在历史n天之内逾期的标的数量(n=15,31,90,180) 173 | * 该产品在历史n天之内的还款时间统计特征(历史label的统计,包括mean,median,std,max,min) 174 | 175 | 用户粒度做的历史行为特征重要显著高于这里假设的产品粒度的历史行为,但是聊胜于无。 176 | 177 | ### 衍生特征 178 | * 该产品在历史n天之内的平滑后的逾期率(n=15,31,90,180) 179 | * 该用户在历史n天之内的平滑后的逾期率(n=15,31,90,180) 180 | * 该用户在历史n天之内的平滑后的提前还款概率 181 | * 通过年利率计算出的月利率特征 182 | * 其他一些特征之间的四则运算(往往业务出发,可以探索强特) 183 | 184 | ## 5. 模型训练 185 | ### 基础版本 186 | 这部分直接上训练代码,其实没啥内容 187 | ```python 188 | train_sample = train_data[(train_data.auditing_date<'2018-10-01')] 189 | testt_sample = train_data[train_data.auditing_date>='2018-11-01'] 190 | train_datas=lgb.Dataset(data=train_sample[features].values,label=train_sample.backward_days,feature_name=features,weight=train_sample.weights) 191 | testt_datas=lgb.Dataset(data=testt_sample[features].values,label=testt_sample.backward_days,feature_name=features,weight=testt_sample.weights) 192 | params = { 193 | 'nthread': 2, # 进程数 194 | 'max_depth': 4, # 最大深度 195 | 'learning_rate': 0.1, # 学习率 196 | 'bagging_fraction': 1, # 采样数 197 | 'num_leaves': 11, # 终点节点最小样本占比的和 198 | 'feature_fraction': 0.3, # 样本列采样 199 | 'objective': 'multiclass', 200 | 'lambda_l1': 10, # L1 正则化 201 | 'lambda_l2': 1, # L2 正则化 202 | 'bagging_seed': 100, # 随机种子,light中默认为100 203 | 'verbose': 0, 204 | 'num_class': 33, 205 | 'min_data_in_leaf':500 206 | } 207 | model1 = lgb.train(params,train_datas,num_boost_round=10000,valid_sets=[testt_datas,train_datas],early_stopping_rounds=20) 208 | ``` 209 | 210 | ### LightGBM的权重设置 211 | 因为样本的权重不一样,样本比例也不相同。本次学习到可以通过设置样本权重来影响损失函数的操作。 212 | 一般机器学习算法面对带权重的训练样本时,处理方式往往是权重乘该样本损失形式: 213 | final_loss = ∑w_i * L(x_i, y_i) 214 | 其中{(x_i, y_i)} 是样本集,w_i 是 (x_i, y_i) 的weight。也就是说,转化成了对Loss 的加权。 215 | 对于决策树,微软的lightGbm的实现也是体现了上述方式。lightGBM支持训练样本带权重,通过分析其源代码,发现是把 weight 乘上了 loss 函数的梯度及hessian(二阶导数)当做新的梯度与hessian。这时,pseudo-reponse就是改写后的了。 216 | 217 | ### 迁移学习实践 218 | 带有时间的数据分析项目中,因为随着时间变化特征和标签的分布都是随着时间变化而变化的。 219 | 可以参考[ijcai-2018的迁移学习方案](https://github.com/plantsgo/ijcai-2018)。那个比赛中他用第1到7天的数据,预测第8天上午和下午,两者一起预测。类比到这比赛中,前面n个月预测后面n个月和未来的2个月,然后基于此重新训练模型。至于预测的目标可以是逾期率,最后n天归还概率,提前还款的天数等。也可以理解为模型融合的方案。 220 | 这个项目中,该方案线下取得更好成绩,但是线上爆炸,故之后没有采纳。在此做记录。 221 | 222 | ## 6. 线下测评 223 | 线下单独的RMSE在106左右,但是线上得分为6000+,在线上标的组合没有明确的前提下,线下通过随机采样方式构建结果。再次基础上进行评测。 224 | 严格按照线上方式构建线下评测。 使用2018/01/01-2018/09/30的数据作为训练集,2018/11/01-2018/12/31的数据作为线下验证集。发现一共就几十个资产组合,大量的标的组合在一起,现在均分成30类时,成绩接近线上。 225 | 226 | ### 线下评测脚本 227 | ```python 228 | from sklearn.model_selection import KFold 229 | kf = KFold(n_splits=30, shuffle=True, random_state=1) 230 | idxs = [] 231 | for idx in kf.split(result_data): 232 | idxs.append(idx[1]) 233 | i = 0 234 | for idx in idxs: 235 | result_data.loc[idx,'listing_group'] = i 236 | i+=1 237 | 238 | test_result = pd.merge(test_result[['listing_id','repay_date','repay_amt_predict','repay_amt']], result_data, on='listing_id',how='inner') 239 | def rule(x): 240 | rates = { 241 | 0:0.984977, 242 | 1:1.000038, 243 | 2:1.0185, 244 | 3:1.012136, 245 | 4:1.019214, 246 | 5:0.979497, 247 | 6:0.964942 248 | } 249 | return x['repay_amt_predict']*rates[ pd.to_datetime(x['repay_date']).weekday()] 250 | test_result['repay_amt_predict_adjust'] = test_result.apply(lambda x:rule(x),axis=1) 251 | result1 = test_result.groupby(['listing_group','repay_date'])['repay_amt_predict','repay_amt'].sum().reset_index() 252 | result2 = test_result.groupby(['listing_group','repay_date'])['repay_amt_predict_adjust','repay_amt'].sum().reset_index() 253 | from sklearn.metrics import mean_squared_error 254 | print(mean_squared_error(test_result.repay_amt_predict,test_result.repay_amt)) 255 | print(math.sqrt(mean_squared_error(result1.repay_amt_predict,result1.repay_amt))) 256 | print(math.sqrt(mean_squared_error(result2.repay_amt_predict_adjust,result2.repay_amt))) 257 | ``` 258 | ### 误差结果分析 259 | 在2018年07月到12月作为验证集情况下,分析了模型预测结果和真实分布之间的差异。 260 | ![预测误差分析](pictures/%E9%A2%84%E6%B5%8B%E8%AF%AF%E5%B7%AE%E5%88%86%E6%9E%90.png) 261 | 262 | 可以发现因为模型特征没有刻画还款日信息,因为多分类的局限性,所以有些日子预测总体偏大(月末,双休日等),有些日子总体偏小(月初).可以通过**后处理**进行调整。 263 | 264 | ## 7. 建模感想 265 | 1. 这是一个比较新颖的任务,也相对单一目的的机器学习场景更加复杂,本项目涵盖了信贷违约预测、现金流预测等金融领域常见问题,同时又是复杂的时序问题和多目标预测问题。最终通过多分类解决。还有更多的解题方案可以探讨,比如dnn+lstm 266 | 2. 在建模过程中,因为数据量的问题,遇到了Pandas处理数据的优化问题。给出[引用](https://www.jianshu.com/p/e5122ac6ffbe) 267 | 3. 如果模型优化目标和最终的评测指标有差异,一般需要经过后处理。 268 | 4. 探索数据还是排在第一。 269 | 270 | 我们模型的优点: 271 | 1. 模拟构建出了线下测试集,规则引入简单有效。 272 | 2. 仅仅使用了训练集数据。特征简单高效(也有详细特征版本) 273 | 274 | 275 | * 比赛项目链接:https://ai.ppdai.com/mirror/goToMirrorDetail?mirrorId=17 276 | * github代码地址:https://github.com/vinklibrary/ppd_mojing4 277 | -------------------------------------------------------------------------------- /data_analysics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 6, 6 | "metadata": { 7 | "collapsed": true, 8 | "pycharm": { 9 | "is_executing": false 10 | } 11 | }, 12 | "outputs": [], 13 | "source": "import pandas as pd\nimport numpy as np\nimport seaborn as sns" 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 5, 18 | "outputs": [], 19 | "source": "# Read Data \n# 因为jupyter服务配置,所以文件目录和项目文件目录存在差异,如果运行时请更改\ntrain \u003d pd.read_csv(\"./data/train.csv\",na_values\u003d\"\\\\N\")\ntest \u003d pd.read_csv(\"./data/test.csv\", na_values\u003d\"\\\\N\")\nlisting_info \u003d pd.read_csv(\"./data/listing_info.csv\")\nuser_info \u003d pd.read_csv(\"./data/user_info.csv\")\nuser_repay_logs \u003d pd.read_csv(\"./data/user_repay_logs.csv\")\nuser_taglist \u003d pd.read_csv(\"./data/user_taglist.csv\")", 20 | "metadata": { 21 | "pycharm": { 22 | "metadata": false, 23 | "name": "#%%\n", 24 | "is_executing": false 25 | } 26 | } 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "source": "# 数据基本情况查看", 31 | "metadata": { 32 | "pycharm": { 33 | "metadata": false, 34 | "name": "#%% md\n" 35 | } 36 | } 37 | }, 38 | { 39 | "cell_type": "code", 40 | "source": "train.info()", 41 | "metadata": { 42 | "pycharm": { 43 | "metadata": false, 44 | "name": "#%%\n", 45 | "is_executing": false 46 | } 47 | }, 48 | "execution_count": 7, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "text": [ 53 | "\u003cclass \u0027pandas.core.frame.DataFrame\u0027\u003e\nRangeIndex: 1000000 entries, 0 to 999999\nData columns (total 7 columns):\nuser_id 1000000 non-null int64\nlisting_id 1000000 non-null int64\nauditing_date 1000000 non-null object\ndue_date 1000000 non-null object\ndue_amt 1000000 non-null float64\nrepay_date 882808 non-null object\nrepay_amt 882808 non-null float64\ndtypes: float64(2), int64(2), object(3)\nmemory usage: 53.4+ MB\n" 54 | ], 55 | "output_type": "stream" 56 | } 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "source": "1. 100万条记录,之后可以考虑每个月的分布,看是否有特殊时间点,比如还款集中的时间段\n2. 882808条还款记录,逾期率11.8%.可以考虑逾期率的变化", 62 | "metadata": { 63 | "pycharm": { 64 | "metadata": false, 65 | "name": "#%% md\n" 66 | } 67 | } 68 | }, 69 | { 70 | "cell_type": "code", 71 | "source": "test.info()", 72 | "metadata": { 73 | "pycharm": { 74 | "metadata": false, 75 | "name": "#%% \n", 76 | "is_executing": false 77 | } 78 | }, 79 | "execution_count": 8, 80 | "outputs": [ 81 | { 82 | "name": "stdout", 83 | "text": [ 84 | "\u003cclass \u0027pandas.core.frame.DataFrame\u0027\u003e\nRangeIndex: 130000 entries, 0 to 129999\nData columns (total 5 columns):\nuser_id 130000 non-null int64\nlisting_id 130000 non-null int64\nauditing_date 130000 non-null object\ndue_date 130000 non-null object\ndue_amt 130000 non-null float64\ndtypes: float64(1), int64(2), object(2)\nmemory usage: 5.0+ MB\n" 85 | ], 86 | "output_type": "stream" 87 | } 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "source": "1. 共13万条记录,2,3月份借款数量较少\n2. ?是怎样一个抽样逻辑", 93 | "metadata": { 94 | "pycharm": { 95 | "metadata": false, 96 | "name": "#%% md\n" 97 | } 98 | } 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 9, 103 | "outputs": [ 104 | { 105 | "name": "stdout", 106 | "text": [ 107 | "\u003cclass \u0027pandas.core.frame.DataFrame\u0027\u003e\nRangeIndex: 5484891 entries, 0 to 5484890\nData columns (total 6 columns):\nuser_id int64\nlisting_id int64\nauditing_date object\nterm int64\nrate float64\nprincipal int64\ndtypes: float64(1), int64(4), object(1)\nmemory usage: 251.1+ MB\n" 108 | ], 109 | "output_type": "stream" 110 | } 111 | ], 112 | "source": "listing_info.info()", 113 | "metadata": { 114 | "pycharm": { 115 | "metadata": false, 116 | "name": "#%%\n", 117 | "is_executing": false 118 | } 119 | } 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 10, 124 | "outputs": [ 125 | { 126 | "data": { 127 | "text/plain": " user_id listing_id auditing_date term rate principal\n0 316610 1556649 2017-11-26 9 7.6 4800\n1 62002 1556633 2017-11-26 6 7.6 4000\n2 192135 1556629 2017-11-26 12 8.0 8660\n3 487382 1556628 2017-11-26 9 7.6 4780\n4 235186 1556627 2017-11-26 9 7.6 1480", 128 | "text/html": "\u003cdiv\u003e\n\u003cstyle scoped\u003e\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n\u003c/style\u003e\n\u003ctable border\u003d\"1\" class\u003d\"dataframe\"\u003e\n \u003cthead\u003e\n \u003ctr style\u003d\"text-align: right;\"\u003e\n \u003cth\u003e\u003c/th\u003e\n \u003cth\u003euser_id\u003c/th\u003e\n \u003cth\u003elisting_id\u003c/th\u003e\n \u003cth\u003eauditing_date\u003c/th\u003e\n \u003cth\u003eterm\u003c/th\u003e\n \u003cth\u003erate\u003c/th\u003e\n \u003cth\u003eprincipal\u003c/th\u003e\n \u003c/tr\u003e\n \u003c/thead\u003e\n \u003ctbody\u003e\n \u003ctr\u003e\n \u003cth\u003e0\u003c/th\u003e\n \u003ctd\u003e316610\u003c/td\u003e\n \u003ctd\u003e1556649\u003c/td\u003e\n \u003ctd\u003e2017-11-26\u003c/td\u003e\n \u003ctd\u003e9\u003c/td\u003e\n \u003ctd\u003e7.6\u003c/td\u003e\n \u003ctd\u003e4800\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003cth\u003e1\u003c/th\u003e\n \u003ctd\u003e62002\u003c/td\u003e\n \u003ctd\u003e1556633\u003c/td\u003e\n \u003ctd\u003e2017-11-26\u003c/td\u003e\n \u003ctd\u003e6\u003c/td\u003e\n \u003ctd\u003e7.6\u003c/td\u003e\n \u003ctd\u003e4000\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003cth\u003e2\u003c/th\u003e\n \u003ctd\u003e192135\u003c/td\u003e\n \u003ctd\u003e1556629\u003c/td\u003e\n \u003ctd\u003e2017-11-26\u003c/td\u003e\n \u003ctd\u003e12\u003c/td\u003e\n \u003ctd\u003e8.0\u003c/td\u003e\n \u003ctd\u003e8660\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003cth\u003e3\u003c/th\u003e\n \u003ctd\u003e487382\u003c/td\u003e\n \u003ctd\u003e1556628\u003c/td\u003e\n \u003ctd\u003e2017-11-26\u003c/td\u003e\n \u003ctd\u003e9\u003c/td\u003e\n \u003ctd\u003e7.6\u003c/td\u003e\n \u003ctd\u003e4780\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003cth\u003e4\u003c/th\u003e\n \u003ctd\u003e235186\u003c/td\u003e\n \u003ctd\u003e1556627\u003c/td\u003e\n \u003ctd\u003e2017-11-26\u003c/td\u003e\n \u003ctd\u003e9\u003c/td\u003e\n \u003ctd\u003e7.6\u003c/td\u003e\n \u003ctd\u003e1480\u003c/td\u003e\n \u003c/tr\u003e\n \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e" 129 | }, 130 | "metadata": {}, 131 | "output_type": "execute_result", 132 | "execution_count": 10 133 | } 134 | ], 135 | "source": "listing_info.head()", 136 | "metadata": { 137 | "pycharm": { 138 | "metadata": false, 139 | "name": "#%%\n", 140 | "is_executing": false 141 | } 142 | } 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "outputs": [], 148 | "source": "", 149 | "metadata": { 150 | "pycharm": { 151 | "metadata": false, 152 | "name": "#%%\n", 153 | "is_executing": true 154 | } 155 | } 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "outputs": [], 161 | "source": "\n", 162 | "metadata": { 163 | "pycharm": { 164 | "metadata": false, 165 | "name": "#%%\n" 166 | } 167 | } 168 | } 169 | ], 170 | "metadata": { 171 | "language_info": { 172 | "codemirror_mode": { 173 | "name": "ipython", 174 | "version": 2 175 | }, 176 | "file_extension": ".py", 177 | "mimetype": "text/x-python", 178 | "name": "python", 179 | "nbconvert_exporter": "python", 180 | "pygments_lexer": "ipython2", 181 | "version": "2.7.6" 182 | }, 183 | "kernelspec": { 184 | "name": "python3", 185 | "language": "python", 186 | "display_name": "Python 3" 187 | } 188 | }, 189 | "nbformat": 4, 190 | "nbformat_minor": 0 191 | } -------------------------------------------------------------------------------- /feature_engineering/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: __init__.py.py 10 | @time: 2019/7/8 13:48 11 | 12 | 这一行开始写关于本文件的说明与解释 13 | """ -------------------------------------------------------------------------------- /feature_engineering/basic_feature.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: basic_feature.py 10 | @time: 2019/7/8 14:15 11 | 12 | 这一行开始写关于本文件的说明与解释 13 | """ 14 | import pandas as pd 15 | import numpy as np 16 | 17 | train_data = pd.read_csv("./dataset/raw_data/train.csv",na_values="\\N") 18 | testt_data = pd.read_csv("./data/raw_date/test.csv") 19 | user_info = pd.read_csv("./dataset/raw_data/user_info.csv") 20 | listing_info = pd.read_csv("./dataset/raw_data/listing_info.csv") 21 | 22 | # 加入用户基础特征 23 | train_data = pd.merge(train_data,user_info,on='user_id',how='left') 24 | train_data = train_data[train_data.auditing_date > train_data.insertdate] 25 | filters = train_data.groupby(['user_id','listing_id','due_date'])['insertdate'].max().reset_index() 26 | train_data = pd.merge(filters,train_data,on=['user_id','listing_id','due_date','insertdate'],how='left') 27 | 28 | testt_data = pd.merge(testt_data, user_info, on='user_id', how='left') 29 | testt_data = testt_data[testt_data.auditing_date > testt_data.insertdate] 30 | filters = testt_data.groupby(['user_id','listing_id','due_date'])['insertdate'].max().reset_index() 31 | testt_data = pd.merge(filters,testt_data,on=['user_id','listing_id','due_date','insertdate'],how='left') 32 | 33 | # 加入标的基础特征 34 | train_data = pd.merge(train_data,listing_info,on=['user_id','listing_id','auditing_date'],how='left') 35 | testt_data = pd.merge(testt_data,listing_info,on=['user_id','listing_id','auditing_date'],how='left') 36 | 37 | -------------------------------------------------------------------------------- /feature_engineering/product_repay_feature.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: product_repay_feature.py 10 | @time: 2019/7/9 12:40 11 | 12 | 这一行开始写关于本文件的说明与解释 13 | """ 14 | import pandas as pd 15 | import numpy as np 16 | from utils.functions import get_converation_rate 17 | from functools import reduce 18 | 19 | train_data = pd.read_csv("./train.csv",na_values="\\N") 20 | testt_data = pd.read_csv("./test.csv") 21 | train_data['flag'] = 'train' 22 | testt_data['flag'] = 'testt' 23 | data = pd.concat([train_data[['user_id','listing_id','auditing_date','due_date','flag']],testt_data[['user_id','listing_id','auditing_date','due_date','flag']]]) 24 | 25 | listing_info = pd.read_csv("../dataset/raw_date/listing_info.csv") 26 | user_pay_logs_fe1 = pd.read_csv("./data/samples/user_pay_logs_fe1.csv") 27 | user_pay_logs_fe1 = user_pay_logs_fe1.rename(columns={'listing_id_x':'listing_id'}) 28 | user_pay_logs_fe1 = pd.merge(user_pay_logs_fe1,listing_info,on=['user_id','listing_id','auditing_date'],how='left') 29 | 30 | product_daoqi_in15days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15].groupby(['term','rate','principal','auditing_date']).size()\ 31 | .reset_index().rename(columns={0:'product_daoqi_in15days'}) 32 | product_daoqi_in31days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31].groupby(['term','rate','principal','auditing_date']).size()\ 33 | .reset_index().rename(columns={0:'product_daoqi_in31days'}) 34 | product_daoqi_in90days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90].groupby(['term','rate','principal','auditing_date']).size()\ 35 | .reset_index().rename(columns={0:'product_daoqi_in90days'}) 36 | product_daoqi_in180days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180].groupby(['term','rate','principal','auditing_date']).size()\ 37 | .reset_index().rename(columns={0:'product_daoqi_in180days'}) 38 | 39 | product_yuqi_in15days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15)].groupby(['term','rate','principal','auditing_date']).size()\ 40 | .reset_index().rename(columns={0:'product_yuqi_in15days'}) 41 | product_yuqi_in31days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31)].groupby(['term','rate','principal','auditing_date']).size()\ 42 | .reset_index().rename(columns={0:'product_yuqi_in31days'}) 43 | product_yuqi_in90days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90)].groupby(['term','rate','principal','auditing_date']).size()\ 44 | .reset_index().rename(columns={0:'product_yuqi_in90days'}) 45 | product_yuqi_in180days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180)].groupby(['term','rate','principal','auditing_date']).size()\ 46 | .reset_index().rename(columns={0:'product_yuqi_in180days'}) 47 | 48 | product_u_count_in15days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15].groupby(['term','rate','principal','auditing_date'])['user_id'].nunique()\ 49 | .reset_index().rename(columns={'user_id':'product_u_count_in15days'}) 50 | product_u_count_in31days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31].groupby(['term','rate','principal','auditing_date'])['user_id'].nunique()\ 51 | .reset_index().rename(columns={'user_id':'product_u_count_in31days'}) 52 | product_u_count_in90days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90].groupby(['term','rate','principal','auditing_date'])['user_id'].nunique()\ 53 | .reset_index().rename(columns={'user_id':'product_u_count_in90days'}) 54 | product_u_count_in180days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180].groupby(['term','rate','principal','auditing_date'])['user_id'].nunique()\ 55 | .reset_index().rename(columns={'user_id':'product_u_count_in180days'}) 56 | 57 | product_days_in15days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15].groupby(['term','rate','principal','auditing_date'])['due_date'].nunique()\ 58 | .reset_index().rename(columns={'due_date':'product_days_in15days'}) 59 | product_days_in31days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31].groupby(['term','rate','principal','auditing_date'])['due_date'].nunique()\ 60 | .reset_index().rename(columns={'due_date':'product_days_in31days'}) 61 | product_days_in90days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90].groupby(['term','rate','principal','auditing_date'])['due_date'].nunique()\ 62 | .reset_index().rename(columns={'due_date':'product_days_in90days'}) 63 | product_days_in180days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180].groupby(['term','rate','principal','auditing_date'])['due_date'].nunique()\ 64 | .reset_index().rename(columns={'due_date':'product_days_in180days'}) 65 | 66 | product_repay_features1 = reduce(lambda x, y: pd.merge(x, y, on=['term','rate','principal','auditing_date'], how='outer'), 67 | [product_daoqi_in15days,product_daoqi_in31days,product_daoqi_in90days,product_daoqi_in180days, 68 | product_yuqi_in15days,product_yuqi_in31days,product_yuqi_in90days,product_yuqi_in180days, 69 | product_u_count_in15days,product_u_count_in31days,product_u_count_in90days,product_u_count_in180days, 70 | product_days_in15days,product_days_in31days,product_days_in90days,product_days_in180days]) 71 | product_repay_features1 = product_repay_features1.fillna(0) 72 | 73 | # 逾期率的计算 (轻微过拟合 获得训练集的转化率可防止) 74 | product_repay_features1['product_yuqi_rate_in15days'] = get_converation_rate(product_repay_features1,'product_yuqi_in15days','product_daoqi_in15days')[0] 75 | product_repay_features1['product_yuqi_rate_in31days'] = get_converation_rate(product_repay_features1,'product_yuqi_in31days','product_daoqi_in31days')[0] 76 | product_repay_features1['product_yuqi_rate_in90days'] = get_converation_rate(product_repay_features1,'product_yuqi_in90days','product_daoqi_in90days')[0] 77 | product_repay_features1['product_yuqi_rate_in180days'] = get_converation_rate(product_repay_features1,'product_yuqi_in180days','product_daoqi_in180days')[0] 78 | product_repay_features1.to_csv("../dataset/gen_features/product_repay_features1.csv", index=None) 79 | 80 | user_pay_logs_fe2 = pd.read_csv("./data/samples/user_pay_logs_fe2.csv") 81 | user_pay_logs_fe2 = user_pay_logs_fe2.rename(columns={'listing_id_x':'listing_id'}) 82 | user_pay_logs_fe2 = pd.merge(user_pay_logs_fe2,listing_info,on=['user_id','listing_id','auditing_date'],how='left') 83 | 84 | # 该产品 历史上首期的平均还款时间 85 | user_pay_logs_fe2_order1 = user_pay_logs_fe2[user_pay_logs_fe2.order_id==1] 86 | 87 | product_repay1_backward_mean_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 88 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_mean_in15days'}) 89 | produce_repay1_backward_mean_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 90 | .reset_index().rename(columns={'backward_days':'produce_repay1_backward_mean_in31days'}) 91 | product_repay1_backward_mean_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 92 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_mean_in90days'}) 93 | product_repay1_backward_mean_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 94 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_mean_in180days'}) 95 | 96 | product_repay1_backward_median_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 97 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_median_in15days'}) 98 | produce_repay1_backward_median_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 99 | .reset_index().rename(columns={'backward_days':'produce_repay1_backward_median_in31days'}) 100 | product_repay1_backward_median_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 101 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_median_in90days'}) 102 | product_repay1_backward_median_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 103 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_median_in180days'}) 104 | 105 | product_repay1_backward_max_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 106 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_max_in15days'}) 107 | produce_repay1_backward_max_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 108 | .reset_index().rename(columns={'backward_days':'produce_repay1_backward_max_in31days'}) 109 | product_repay1_backward_max_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 110 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_max_in90days'}) 111 | product_repay1_backward_max_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 112 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_max_in180days'}) 113 | 114 | product_repay1_backward_min_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 115 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_min_in15days'}) 116 | produce_repay1_backward_min_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 117 | .reset_index().rename(columns={'backward_days':'produce_repay1_backward_min_in31days'}) 118 | product_repay1_backward_min_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 119 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_min_in90days'}) 120 | product_repay1_backward_min_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 121 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_min_in180days'}) 122 | 123 | product_repay1_backward_std_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 124 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_std_in15days'}) 125 | produce_repay1_backward_std_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 126 | .reset_index().rename(columns={'backward_days':'produce_repay1_backward_std_in31days'}) 127 | product_repay1_backward_std_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 128 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_std_in90days'}) 129 | product_repay1_backward_std_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 130 | .reset_index().rename(columns={'backward_days':'product_repay1_backward_std_in180days'}) 131 | 132 | # 该产品 历史上首期的平均还款时间 133 | # user_pay_logs_fe2_order1 = user_pay_logs_fe2[user_pay_logs_fe2.order_id==1] 134 | user_pay_logs_fe2_order2 = user_pay_logs_fe2[user_pay_logs_fe2.backward_days<=31] 135 | 136 | product_repay2_backward_mean_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 137 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_mean_in15days'}) 138 | produce_repay2_backward_mean_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 139 | .reset_index().rename(columns={'backward_days':'produce_repay2_backward_mean_in31days'}) 140 | product_repay2_backward_mean_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 141 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_mean_in90days'}) 142 | product_repay2_backward_mean_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].mean()\ 143 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_mean_in180days'}) 144 | 145 | product_repay2_backward_median_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 146 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_median_in15days'}) 147 | produce_repay2_backward_median_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 148 | .reset_index().rename(columns={'backward_days':'produce_repay2_backward_median_in31days'}) 149 | product_repay2_backward_median_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 150 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_median_in90days'}) 151 | product_repay2_backward_median_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].median()\ 152 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_median_in180days'}) 153 | 154 | product_repay2_backward_max_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 155 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_max_in15days'}) 156 | produce_repay2_backward_max_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 157 | .reset_index().rename(columns={'backward_days':'produce_repay2_backward_max_in31days'}) 158 | product_repay2_backward_max_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 159 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_max_in90days'}) 160 | product_repay2_backward_max_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].max()\ 161 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_max_in180days'}) 162 | 163 | product_repay2_backward_min_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 164 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_min_in15days'}) 165 | produce_repay2_backward_min_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 166 | .reset_index().rename(columns={'backward_days':'produce_repay2_backward_min_in31days'}) 167 | product_repay2_backward_min_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 168 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_min_in90days'}) 169 | product_repay2_backward_min_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].min()\ 170 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_min_in180days'}) 171 | 172 | product_repay2_backward_std_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 173 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_std_in15days'}) 174 | produce_repay2_backward_std_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 175 | .reset_index().rename(columns={'backward_days':'produce_repay2_backward_std_in31days'}) 176 | product_repay2_backward_std_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 177 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_std_in90days'}) 178 | product_repay2_backward_std_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['term','rate','principal','auditing_date'])['backward_days'].std()\ 179 | .reset_index().rename(columns={'backward_days':'product_repay2_backward_std_in180days'}) 180 | 181 | # 该商品提前还款的单数 182 | user_pay_logs_fe2_before = user_pay_logs_fe2[user_pay_logs_fe2.backward_days>31] 183 | 184 | product_repay_before_in15days = user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date>user_pay_logs_fe2_before.daybefore15].groupby(['term','rate','principal','auditing_date']).size()\ 185 | .reset_index().rename(columns={0:'product_repay_before_in15days'}) 186 | product_repay_before_in31days = user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date>user_pay_logs_fe2_before.daybefore31].groupby(['term','rate','principal','auditing_date']).size()\ 187 | .reset_index().rename(columns={0:'product_repay_before_in31days'}) 188 | product_repay_before_in90days = user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date>user_pay_logs_fe2_before.daybefore90].groupby(['term','rate','principal','auditing_date']).size()\ 189 | .reset_index().rename(columns={0:'product_repay_before_in90days'}) 190 | product_repay_before_in180days = user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date>user_pay_logs_fe2_before.daybefore180].groupby(['term','rate','principal','auditing_date']).size()\ 191 | .reset_index().rename(columns={0:'product_repay_before_in180days'}) 192 | 193 | product_repay_by_dow = user_pay_logs_fe2.groupby(['term','rate','principal','auditing_date','repay_dow']).size().reset_index().rename(columns={0:'product_repay_by_dow'}) 194 | product_repay_by_dow = product_repay_by_dow.pivot_table(index=['term','rate','principal','auditing_date'],columns='repay_dow',values='product_repay_by_dow').reset_index() 195 | product_repay_by_dow.columns = ['term','rate','principal','auditing_date','p_repay_dow1','p_repay_dow2','p_repay_dow3','p_repay_dow4','p_repay_dow5','p_repay_dow6','p_repay_dow7'] 196 | product_repay_counts = user_pay_logs_fe2.groupby(['term','rate','principal','auditing_date']).size().reset_index().rename(columns={0:'product_repay_counts'}) 197 | product_repay_distribution = pd.merge(product_repay_by_dow,product_repay_counts,on=['term','rate','principal','auditing_date'],how='left') 198 | product_repay_distribution = product_repay_distribution.fillna(0) 199 | product_repay_distribution['p_repay_dow1_rate'] = get_converation_rate(product_repay_distribution,'p_repay_dow1','product_repay_counts')[0] 200 | product_repay_distribution['p_repay_dow2_rate'] = get_converation_rate(product_repay_distribution,'p_repay_dow2','product_repay_counts')[0] 201 | product_repay_distribution['p_repay_dow3_rate'] = get_converation_rate(product_repay_distribution,'p_repay_dow3','product_repay_counts')[0] 202 | product_repay_distribution['p_repay_dow4_rate'] = get_converation_rate(product_repay_distribution,'p_repay_dow4','product_repay_counts')[0] 203 | product_repay_distribution['p_repay_dow5_rate'] = get_converation_rate(product_repay_distribution,'p_repay_dow5','product_repay_counts')[0] 204 | product_repay_distribution['p_repay_dow6_rate'] = get_converation_rate(product_repay_distribution,'p_repay_dow6','product_repay_counts')[0] 205 | product_repay_distribution['p_repay_dow7_rate'] = get_converation_rate(product_repay_distribution,'p_repay_dow7','product_repay_counts')[0] 206 | 207 | product_repay_features2 = reduce(lambda x, y: pd.merge(x, y, on=['term','rate','principal','auditing_date'], how='outer'), 208 | [product_repay1_backward_mean_in15days,produce_repay1_backward_mean_in31days,product_repay1_backward_mean_in90days,product_repay1_backward_mean_in180days, 209 | product_repay1_backward_median_in15days,produce_repay1_backward_median_in31days,product_repay1_backward_median_in90days,product_repay1_backward_median_in180days, 210 | product_repay1_backward_max_in15days,produce_repay1_backward_max_in31days,product_repay1_backward_max_in90days,product_repay1_backward_max_in180days, 211 | product_repay1_backward_min_in15days,produce_repay1_backward_min_in31days,product_repay1_backward_min_in90days,product_repay1_backward_min_in180days, 212 | product_repay1_backward_std_in15days,produce_repay1_backward_std_in31days,product_repay1_backward_std_in90days,product_repay1_backward_std_in180days, 213 | product_repay2_backward_mean_in15days,produce_repay2_backward_mean_in31days,product_repay2_backward_mean_in90days,product_repay2_backward_mean_in180days, 214 | product_repay2_backward_median_in15days,produce_repay2_backward_median_in31days,product_repay2_backward_median_in90days,product_repay2_backward_median_in180days, 215 | product_repay2_backward_max_in15days,produce_repay2_backward_max_in31days,product_repay2_backward_max_in90days,product_repay2_backward_max_in180days, 216 | product_repay2_backward_min_in15days,produce_repay2_backward_min_in31days,product_repay2_backward_min_in90days,product_repay2_backward_min_in180days, 217 | product_repay2_backward_std_in15days,produce_repay2_backward_std_in31days,product_repay2_backward_std_in90days,product_repay2_backward_std_in180days, 218 | product_repay_before_in15days,product_repay_before_in31days,product_repay_before_in90days,product_repay_before_in180days, 219 | product_repay_distribution]) 220 | product_repay_features2 = product_repay_features2.fillna(0) 221 | product_repay_features2.to_csv("../dataset/gen_features/product_repay_features2.csv",index=None) -------------------------------------------------------------------------------- /feature_engineering/user_repay_feature1.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: user_repay_feature1.py 10 | @time: 2019/7/8 16:15 11 | 12 | 这一行开始写关于本文件的说明与解释 13 | """ 14 | import pandas as pd 15 | import numpy as np 16 | from utils.functions import get_date_before, Caltime 17 | from functools import reduce 18 | 19 | user_pay_logs_fe1 = pd.read_csv("../samples/user_pay_logs_fe1.csv") 20 | # 统计最近15,31,90,180天内的情况 21 | user_pay_logs_fe1['daybefore15'] = user_pay_logs_fe1.auditing_date.map(lambda x:get_date_before(x,15)) 22 | user_pay_logs_fe1['daybefore31'] = user_pay_logs_fe1.auditing_date.map(lambda x:get_date_before(x,31)) 23 | user_pay_logs_fe1['daybefore90'] = user_pay_logs_fe1.auditing_date.map(lambda x:get_date_before(x,90)) 24 | user_pay_logs_fe1['daybefore180'] = user_pay_logs_fe1.auditing_date.map(lambda x:get_date_before(x,180)) 25 | 26 | user_daoqi_in15days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15].groupby(['user_id','auditing_date']).size()\ 27 | .reset_index().rename(columns={0:'user_daoqi_in15days'}) 28 | user_daoqi_in31days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31].groupby(['user_id','auditing_date']).size()\ 29 | .reset_index().rename(columns={0:'user_daoqi_in31days'}) 30 | user_daoqi_in90days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90].groupby(['user_id','auditing_date']).size()\ 31 | .reset_index().rename(columns={0:'user_daoqi_in90days'}) 32 | user_daoqi_in180days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180].groupby(['user_id','auditing_date']).size()\ 33 | .reset_index().rename(columns={0:'user_daoqi_in180days'}) 34 | 35 | user_daoqi_amt_in15days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 36 | .reset_index().rename(columns={'due_amt':'user_daoqi_amt_in15days'}) 37 | user_daoqi_amt_in31days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 38 | .reset_index().rename(columns={'due_amt':'user_daoqi_amt_in31days'}) 39 | user_daoqi_amt_in90days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 40 | .reset_index().rename(columns={'due_amt':'user_daoqi_amt_in90days'}) 41 | user_daoqi_amt_in180days = user_pay_logs_fe1[user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 42 | .reset_index().rename(columns={'due_amt':'user_daoqi_amt_in180days'}) 43 | 44 | user_yuqi_in15days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15)].groupby(['user_id','auditing_date']).size()\ 45 | .reset_index().rename(columns={0:'user_yuqi_in15days'}) 46 | user_yuqi_in31days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31)].groupby(['user_id','auditing_date']).size()\ 47 | .reset_index().rename(columns={0:'user_yuqi_in31days'}) 48 | user_yuqi_in90days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90)].groupby(['user_id','auditing_date']).size()\ 49 | .reset_index().rename(columns={0:'user_yuqi_in90days'}) 50 | user_yuqi_in180days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180)].groupby(['user_id','auditing_date']).size()\ 51 | .reset_index().rename(columns={0:'user_yuqi_in180days'}) 52 | 53 | user_yuqi_amt_in15days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore15)].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 54 | .reset_index().rename(columns={0:'user_yuqi_amt_in15days'}) 55 | user_yuqi_amt_in31days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore31)].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 56 | .reset_index().rename(columns={0:'user_yuqi_amt_in31days'}) 57 | user_yuqi_amt_in90days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore90)].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 58 | .reset_index().rename(columns={0:'user_yuqi_amt_in90days'}) 59 | user_yuqi_amt_in180days = user_pay_logs_fe1[(user_pay_logs_fe1.repay_date=='2200-01-01')&(user_pay_logs_fe1.due_date>user_pay_logs_fe1.daybefore180)].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 60 | .reset_index().rename(columns={0:'user_yuqi_amt_in180days'}) 61 | 62 | latest_due_date = user_pay_logs_fe1.groupby(['user_id','auditing_date'])['due_date'].max().reset_index().rename(columns={'due_date':'latest_due_date'}) 63 | farthest_due_date = user_pay_logs_fe1.groupby(['user_id','auditing_date'])['due_date'].min().reset_index().rename(columns={'due_date':'farthest_due_date'}) 64 | 65 | latest_due_date['latest_due_date_diff'] = latest_due_date.apply(lambda x:Caltime(x['latest_due_date'],x['auditing_date']),axis=1) 66 | farthest_due_date['farthest_due_date_diff'] = farthest_due_date.apply(lambda x:Caltime(x['farthest_due_date'],x['auditing_date']),axis=1) 67 | 68 | user_repay_features2 = reduce(lambda x, y: pd.merge(x, y, on=['user_id','auditing_date'], how='outer'), 69 | [user_daoqi_in15days,user_daoqi_in31days,user_daoqi_in90days,user_daoqi_in180days, 70 | user_daoqi_amt_in15days,user_daoqi_amt_in31days,user_daoqi_amt_in90days,user_daoqi_amt_in180days, 71 | user_yuqi_in15days,user_yuqi_in31days,user_yuqi_in90days,user_yuqi_in180days, 72 | user_yuqi_amt_in15days,user_yuqi_amt_in31days,user_yuqi_amt_in90days,user_yuqi_amt_in180days, 73 | latest_due_date,farthest_due_date]) 74 | 75 | user_repay_features2.to_csv("../dataset/gen_features/user_repay_features2.csv",index=None) -------------------------------------------------------------------------------- /feature_engineering/user_repay_feature2.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: user_repay_feature2.py.py 10 | @time: 2019/7/8 15:12 11 | 12 | 这一行开始写关于本文件的说明与解释 13 | """ 14 | import pandas as pd 15 | import numpy as np 16 | from utils.functions import Caltime, get_date_before 17 | from functools import reduce 18 | 19 | user_pay_logs_fe2 = pd.read_csv("../dataset/gen_data/user_pay_logs_fe2.csv") 20 | # 统计最近15,31,90,180天内的情况 21 | user_pay_logs_fe2['daybefore15'] = user_pay_logs_fe2.auditing_date.map(lambda x:get_date_before(x,15)) 22 | user_pay_logs_fe2['daybefore31'] = user_pay_logs_fe2.auditing_date.map(lambda x:get_date_before(x,31)) 23 | user_pay_logs_fe2['daybefore90'] = user_pay_logs_fe2.auditing_date.map(lambda x:get_date_before(x,90)) 24 | user_pay_logs_fe2['daybefore180'] = user_pay_logs_fe2.auditing_date.map(lambda x:get_date_before(x,180)) 25 | print("#"*10 + "user_pay_logs_fe2 sample preprocess finished"+"#"*10) 26 | 27 | # 用户还款特征,最近yi段时间内的还款标的数 28 | user_repay_in15days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore15].groupby(['user_id','auditing_date']).size()\ 29 | .reset_index().rename(columns={0:'user_repay_in15days'}) 30 | user_repay_in31days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore31].groupby(['user_id','auditing_date']).size()\ 31 | .reset_index().rename(columns={0:'user_repay_in31days'}) 32 | user_repay_in90days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore90].groupby(['user_id','auditing_date']).size()\ 33 | .reset_index().rename(columns={0:'user_repay_in90days'}) 34 | user_repay_in180days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore180].groupby(['user_id','auditing_date']).size()\ 35 | .reset_index().rename(columns={0:'user_repay_in180days'}) 36 | 37 | user_repay_amt_in15days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore15].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 38 | .reset_index().rename(columns={'due_amt':'user_repay_amt_in15days'}) 39 | user_repay_amt_in31days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore31].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 40 | .reset_index().rename(columns={'due_amt':'user_repay_amt_in31days'}) 41 | user_repay_amt_in90days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore90].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 42 | .reset_index().rename(columns={'due_amt':'user_repay_amt_in90days'}) 43 | user_repay_amt_in180days = user_pay_logs_fe2[user_pay_logs_fe2.repay_date>user_pay_logs_fe2.daybefore180].groupby(['user_id','auditing_date'])['due_amt'].sum()\ 44 | .reset_index().rename(columns={'due_amt':'user_repay_amt_in180days'}) 45 | print("#"*10 + "user_pay_fea2 class1 finished"+"#"*10) 46 | 47 | # 用户历史上首期的平均还款时间 48 | user_pay_logs_fe2_order1 = user_pay_logs_fe2[user_pay_logs_fe2.order_id==1] 49 | 50 | user_repay1_backward_mean_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 51 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_mean_in15days'}) 52 | user_repay1_backward_mean_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 53 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_mean_in31days'}) 54 | user_repay1_backward_mean_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 55 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_mean_in90days'}) 56 | user_repay1_backward_mean_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 57 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_mean_in180days'}) 58 | 59 | user_repay1_backward_median_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].median()\ 60 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_median_in15days'}) 61 | user_repay1_backward_median_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].median()\ 62 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_median_in31days'}) 63 | user_repay1_backward_median_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].median()\ 64 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_median_in90days'}) 65 | user_repay1_backward_median_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].median()\ 66 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_median_in180days'}) 67 | 68 | user_repay1_backward_max_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].max()\ 69 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_max_in15days'}) 70 | user_repay1_backward_max_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].max()\ 71 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_max_in31days'}) 72 | user_repay1_backward_max_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].max()\ 73 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_max_in90days'}) 74 | user_repay1_backward_max_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].max()\ 75 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_max_in180days'}) 76 | 77 | user_repay1_backward_min_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].min()\ 78 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_min_in15days'}) 79 | user_repay1_backward_min_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].min()\ 80 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_min_in31days'}) 81 | user_repay1_backward_min_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].min()\ 82 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_min_in90days'}) 83 | user_repay1_backward_min_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].min()\ 84 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_min_in180days'}) 85 | 86 | user_repay1_backward_std_in15days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].std()\ 87 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_std_in15days'}) 88 | user_repay1_backward_std_in31days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].std()\ 89 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_std_in31days'}) 90 | user_repay1_backward_std_in90days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].std()\ 91 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_std_in90days'}) 92 | user_repay1_backward_std_in180days = user_pay_logs_fe2_order1[user_pay_logs_fe2_order1.repay_date>user_pay_logs_fe2_order1.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].std()\ 93 | .reset_index().rename(columns={'backward_days':'user_repay1_backward_std_in180days'}) 94 | print("#"*10 + "user_pay_fea2 class2 finished"+"#"*10) 95 | 96 | # 所有的在还款期间的还款行为特征 97 | user_pay_logs_fe2_order2 = user_pay_logs_fe2[user_pay_logs_fe2.backward_days<=31] 98 | 99 | user_repay2_backward_mean_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 100 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_mean_in15days'}) 101 | user_repay2_backward_mean_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 102 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_mean_in31days'}) 103 | user_repay2_backward_mean_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 104 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_mean_in90days'}) 105 | user_repay2_backward_mean_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].mean()\ 106 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_mean_in180days'}) 107 | 108 | user_repay2_backward_median_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].median()\ 109 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_median_in15days'}) 110 | user_repay2_backward_median_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].median()\ 111 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_median_in31days'}) 112 | user_repay2_backward_median_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].median()\ 113 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_median_in90days'}) 114 | user_repay2_backward_median_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].median()\ 115 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_median_in180days'}) 116 | 117 | user_repay2_backward_max_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].max()\ 118 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_max_in15days'}) 119 | user_repay2_backward_max_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].max()\ 120 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_max_in31days'}) 121 | user_repay2_backward_max_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].max()\ 122 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_max_in90days'}) 123 | user_repay2_backward_max_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].max()\ 124 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_max_in180days'}) 125 | 126 | user_repay2_backward_min_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].min()\ 127 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_min_in15days'}) 128 | user_repay2_backward_min_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].min()\ 129 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_min_in31days'}) 130 | user_repay2_backward_min_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].min()\ 131 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_min_in90days'}) 132 | user_repay2_backward_min_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].min()\ 133 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_min_in180days'}) 134 | 135 | user_repay2_backward_std_in15days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore15].groupby(['user_id','auditing_date'])['backward_days'].std()\ 136 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_std_in15days'}) 137 | user_repay2_backward_std_in31days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore31].groupby(['user_id','auditing_date'])['backward_days'].std()\ 138 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_std_in31days'}) 139 | user_repay2_backward_std_in90days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore90].groupby(['user_id','auditing_date'])['backward_days'].std()\ 140 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_std_in90days'}) 141 | user_repay2_backward_std_in180days = user_pay_logs_fe2_order2[user_pay_logs_fe2_order2.repay_date>user_pay_logs_fe2_order2.daybefore180].groupby(['user_id','auditing_date'])['backward_days'].std()\ 142 | .reset_index().rename(columns={'backward_days':'user_repay2_backward_std_in180days'}) 143 | print("#"*10 + "user_pay_fea2 class3 finished"+"#"*10) 144 | 145 | # 用户提前还款的单数,和金额 146 | user_pay_logs_fe2_before = user_pay_logs_fe2[user_pay_logs_fe2.backward_days > 31] 147 | 148 | user_repay_before_in15days = user_pay_logs_fe2_before[ 149 | user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore15].groupby( 150 | ['user_id', 'auditing_date']).size() \ 151 | .reset_index().rename(columns={0: 'user_repay_before_in15days'}) 152 | user_repay_before_in31days = user_pay_logs_fe2_before[ 153 | user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore31].groupby( 154 | ['user_id', 'auditing_date']).size() \ 155 | .reset_index().rename(columns={0: 'user_repay_before_in31days'}) 156 | user_repay_before_in90days = user_pay_logs_fe2_before[ 157 | user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore90].groupby( 158 | ['user_id', 'auditing_date']).size() \ 159 | .reset_index().rename(columns={0: 'user_repay_before_in90days'}) 160 | user_repay_before_in180days = user_pay_logs_fe2_before[ 161 | user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore180].groupby( 162 | ['user_id', 'auditing_date']).size() \ 163 | .reset_index().rename(columns={0: 'user_repay_before_in180days'}) 164 | 165 | user_repay_amt_before_in15days = \ 166 | user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore15].groupby( 167 | ['user_id', 'auditing_date'])['due_amt'].sum() \ 168 | .reset_index().rename(columns={'due_amt': 'user_repay_amt_before_in15days'}) 169 | user_repay_amt_before_in31days = \ 170 | user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore31].groupby( 171 | ['user_id', 'auditing_date'])['due_amt'].sum() \ 172 | .reset_index().rename(columns={'due_amt': 'user_repay_amt_before_in31days'}) 173 | user_repay_amt_before_in90days = \ 174 | user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore90].groupby( 175 | ['user_id', 'auditing_date'])['due_amt'].sum() \ 176 | .reset_index().rename(columns={'due_amt': 'user_repay_amt_before_in90days'}) 177 | user_repay_amt_before_in180days = \ 178 | user_pay_logs_fe2_before[user_pay_logs_fe2_before.repay_date > user_pay_logs_fe2_before.daybefore180].groupby( 179 | ['user_id', 'auditing_date'])['due_amt'].sum() \ 180 | .reset_index().rename(columns={'due_amt': 'user_repay_amt_before_in180days'}) 181 | 182 | latest_repay_date = user_pay_logs_fe2.groupby(['user_id', 'auditing_date'])['repay_date'].max().reset_index().rename( 183 | columns={'repay_date': 'latest_repay_date'}) 184 | farthest_repay_date = user_pay_logs_fe2.groupby(['user_id', 'auditing_date'])['repay_date'].min().reset_index().rename( 185 | columns={'repay_date': 'farthest_repay_date'}) 186 | latest_repay_date['latest_repay_date_diff'] = latest_repay_date.apply( 187 | lambda x: Caltime(x['latest_repay_date'], x['auditing_date']), axis=1) 188 | farthest_repay_date['farthest_repay_date_diff'] = farthest_repay_date.apply( 189 | lambda x: Caltime(x['farthest_repay_date'], x['auditing_date']), axis=1) 190 | user_repay_by_dow = user_pay_logs_fe2.groupby(['user_id', 'auditing_date', 'repay_dow']).size().reset_index().rename( 191 | columns={0: 'user_repay_by_dow'}) 192 | user_repay_by_dow = user_repay_by_dow.pivot_table(index=['user_id', 'auditing_date'], columns='repay_dow', 193 | values='user_repay_by_dow').reset_index() 194 | user_repay_by_dow.columns = ['user_id', 'auditing_date', 'repay_dow1', 'repay_dow2', 'repay_dow3', 'repay_dow4', 195 | 'repay_dow5', 'repay_dow6', 'repay_dow7'] 196 | user_repay_counts = user_pay_logs_fe2.groupby(['user_id', 'auditing_date']).size().reset_index().rename( 197 | columns={0: 'user_repay_counts'}) 198 | 199 | user_repay_distribution = pd.merge(user_repay_by_dow, user_repay_counts, on=['user_id', 'auditing_date'], how='left') 200 | user_repay_distribution = user_repay_distribution.fillna(0) 201 | for column in ['repay_dow1', 'repay_dow2', 'repay_dow3', 'repay_dow4', 'repay_dow5', 'repay_dow6', 'repay_dow7']: 202 | mean_c = user_repay_distribution['user_repay_counts'].mean() 203 | mean_rate = user_repay_distribution[column].sum() / user_repay_distribution['user_repay_counts'].sum() 204 | user_repay_distribution[column + "rate"] = (user_repay_distribution[column] + mean_c * mean_rate) / ( 205 | user_repay_distribution['user_repay_counts'] + mean_c) 206 | 207 | 208 | def get_mon_code(inte): 209 | if inte <= 10: 210 | return 0 211 | elif inte >= 20: 212 | return 2 213 | else: 214 | return 1 215 | 216 | 217 | user_pay_logs_fe2['mon_code'] = user_pay_logs_fe2.repay_dom.map(lambda x: get_mon_code(x)) 218 | user_repay_by_moncode = user_pay_logs_fe2.groupby(['user_id', 'auditing_date', 'mon_code']).size().reset_index().rename( 219 | columns={0: 'user_repay_by_moncode'}) 220 | user_repay_by_moncode = user_repay_by_moncode.pivot_table(index=['user_id', 'auditing_date'], columns='mon_code', 221 | values='user_repay_by_moncode').reset_index() 222 | user_repay_by_moncode.columns = ['user_id', 'auditing_date', 'mon_code0', 'mon_code1', 'mon_code2'] 223 | user_repay_by_moncode = user_repay_by_moncode.fillna(0) 224 | user_repay_distribution2 = pd.merge(user_repay_by_moncode, user_repay_counts, on=['user_id', 'auditing_date'], 225 | how='left') 226 | for column in ['mon_code0', 'mon_code1', 'mon_code2']: 227 | mean_c = user_repay_distribution2['user_repay_counts'].mean() 228 | mean_rate = user_repay_distribution2[column].sum() / user_repay_distribution2['user_repay_counts'].sum() 229 | user_repay_distribution2[column + "rate"] = (user_repay_distribution2[column] + mean_c * mean_rate) / ( 230 | user_repay_distribution2['user_repay_counts'] + mean_c) 231 | 232 | 233 | # 用户还款时间的diff的差距 234 | def get_diff_mean(x): 235 | return pd.Series(np.sort(np.unique(x))).diff().mean() 236 | def get_diff_min(x): 237 | return pd.Series(np.sort(np.unique(x))).diff().min() 238 | def get_diff_max(x): 239 | return pd.Series(np.sort(np.unique(x))).diff().max() 240 | def get_diff_median(x): 241 | return pd.Series(np.sort(np.unique(x))).diff().median() 242 | def get_diff_std(x): 243 | return pd.Series(np.sort(np.unique(x))).diff().std() 244 | 245 | repay_date_numeric_diff_mean = user_pay_logs_fe2.groupby(['user_id', 'auditing_date'])['repay_date_numeric'].apply( 246 | lambda x: get_diff_mean(x)) \ 247 | .reset_index().rename(columns={'repay_date_numeric': 'repay_date_numeric_diff_mean'}) 248 | 249 | repay_date_numeric_diff_min = user_pay_logs_fe2.groupby(['user_id', 'auditing_date'])['repay_date_numeric'].apply( 250 | lambda x: get_diff_min(x)) \ 251 | .reset_index().rename(columns={'repay_date_numeric': 'repay_date_numeric_diff_min'}) 252 | 253 | repay_date_numeric_diff_max = user_pay_logs_fe2.groupby(['user_id', 'auditing_date'])['repay_date_numeric'].apply( 254 | lambda x: get_diff_max(x)) \ 255 | .reset_index().rename(columns={'repay_date_numeric': 'repay_date_numeric_diff_max'}) 256 | 257 | repay_date_numeric_diff_median = user_pay_logs_fe2.groupby(['user_id', 'auditing_date'])['repay_date_numeric'].apply( 258 | lambda x: get_diff_median(x)) \ 259 | .reset_index().rename(columns={'repay_date_numeric': 'repay_date_numeric_diff_median'}) 260 | 261 | repay_date_numeric_diff_std = user_pay_logs_fe2.groupby(['user_id', 'auditing_date'])['repay_date_numeric'].apply( 262 | lambda x: get_diff_std(x)) \ 263 | .reset_index().rename(columns={'repay_date_numeric': 'repay_date_numeric_diff_std'}) 264 | 265 | print("#"*10 + "user_pay_fea2 class4 finished"+"#"*10) 266 | 267 | user_repay_features2 = reduce(lambda x, y: pd.merge(x, y, on=['user_id', 'auditing_date'], how='outer'), 268 | [user_repay_in15days, user_repay_in31days, user_repay_in90days, user_repay_in180days, 269 | user_repay_amt_in15days, user_repay_amt_in31days, user_repay_amt_in90days, 270 | user_repay_amt_in180days, 271 | user_repay1_backward_mean_in15days, user_repay1_backward_mean_in31days, 272 | user_repay1_backward_mean_in90days, user_repay1_backward_mean_in180days, 273 | user_repay1_backward_median_in15days, user_repay1_backward_median_in31days, 274 | user_repay1_backward_median_in90days, user_repay1_backward_median_in180days, 275 | user_repay1_backward_max_in15days, user_repay1_backward_max_in31days, 276 | user_repay1_backward_max_in90days, user_repay1_backward_max_in180days, 277 | user_repay1_backward_min_in15days, user_repay1_backward_min_in31days, 278 | user_repay1_backward_min_in90days, user_repay1_backward_min_in180days, 279 | user_repay1_backward_std_in15days, user_repay1_backward_std_in31days, 280 | user_repay1_backward_std_in90days, user_repay1_backward_std_in180days, 281 | user_repay2_backward_mean_in15days, user_repay2_backward_mean_in31days, 282 | user_repay2_backward_mean_in90days, user_repay2_backward_mean_in180days, 283 | user_repay2_backward_median_in15days, user_repay2_backward_median_in31days, 284 | user_repay2_backward_median_in90days, user_repay2_backward_median_in180days, 285 | user_repay2_backward_max_in15days, user_repay2_backward_max_in31days, 286 | user_repay2_backward_max_in90days, user_repay2_backward_max_in180days, 287 | user_repay2_backward_min_in15days, user_repay2_backward_min_in31days, 288 | user_repay2_backward_min_in90days, user_repay2_backward_min_in180days, 289 | user_repay2_backward_std_in15days, user_repay2_backward_std_in31days, 290 | user_repay2_backward_std_in90days, user_repay2_backward_std_in180days, 291 | user_repay_before_in15days, user_repay_before_in31days, user_repay_before_in90days, 292 | user_repay_before_in180days, 293 | user_repay_amt_before_in15days, user_repay_amt_before_in31days, 294 | user_repay_amt_before_in90days, user_repay_amt_before_in180days, 295 | latest_repay_date[['user_id', 'auditing_date', 'latest_repay_date_diff']], 296 | farthest_repay_date[['user_id', 'auditing_date', 'farthest_repay_date_diff']], 297 | user_repay_distribution[ 298 | ['user_id', 'auditing_date', 'repay_dow1', 'repay_dow2', 'repay_dow3', 'repay_dow4', 299 | 'repay_dow5', 'repay_dow6', 'repay_dow7', 'user_repay_counts', 'repay_dow1rate', 300 | 'repay_dow2rate', 'repay_dow3rate', 'repay_dow4rate', 'repay_dow5rate', 301 | 'repay_dow6rate', 'repay_dow7rate']], 302 | user_repay_distribution2[ 303 | ['user_id', 'auditing_date', 'mon_code0', 'mon_code1', 'mon_code2', 'mon_code0rate', 304 | 'mon_code1rate', 'mon_code2rate']], 305 | repay_date_numeric_diff_mean, repay_date_numeric_diff_min, repay_date_numeric_diff_max, 306 | repay_date_numeric_diff_median, repay_date_numeric_diff_std]) 307 | 308 | user_repay_features2.to_csv("../dataset/gen_features/user_repay_features2.csv",index=None) 309 | print("#"*10 + "user_repay_features2 gen and save finished"+"#"*10) 310 | -------------------------------------------------------------------------------- /feature_engineering/user_repay_feature3.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: user_repay_feature3.py 10 | @time: 2019/7/8 14:37 11 | 12 | 用户还款特征第三类特征。 13 | """ 14 | import pandas as pd 15 | import tqdm 16 | from utils.functions import get_month_after, Caltime 17 | from functools import reduce 18 | 19 | train_data = pd.read_csv("../dataset/raw_data/train.csv") 20 | testt_data = pd.read_csv("../dataset/raw_data/test.csv") 21 | train_data['flag'] = 'train' 22 | testt_data['flag'] = 'testt' 23 | data = pd.concat([train_data[['user_id','listing_id','auditing_date','due_date','flag']],testt_data[['user_id','listing_id','auditing_date','due_date','flag']]]) 24 | 25 | listing_info = pd.read_csv("../dataset/raw_data/listing_info.csv") 26 | data = pd.merge(data[['user_id','auditing_date']],listing_info,on=['user_id'],how='left') 27 | data = data[data.auditing_date_x>data.auditing_date_y] 28 | print("#"*10 + "raw data preprocess finished"+"#"*10) 29 | 30 | # 历史借贷数 31 | tmp1 = data.groupby(['user_id','auditing_date_x']).size().reset_index() 32 | # 历史本金的情况 33 | tmp2 = data.groupby(['user_id','auditing_date_x'])['principal'].agg(['max','mean','median','min','std']).reset_index() 34 | tmp1.columns = ['user_id','auditing_date','listnum_in_histoy'] 35 | tmp2.columns = ['user_id','auditing_date','principal_his_max','principal_his_mean','principal_his_median','principal_his_min','principal_his_std'] 36 | 37 | listing_info_feature = { 38 | 'user_id': [], 39 | 'listing_id': [], 40 | 'auditing_date': [], 41 | 'order_id': [], 42 | 'order_date': [], 43 | 'due_date': [], 44 | 'order_due_amt': [] 45 | } 46 | for i, value in tqdm.tqdm(enumerate(data.values)): 47 | tmp_user_id = value[0] 48 | tmp_listing_id = value[2] 49 | tmp_auditing_date1 = value[1] 50 | tmp_term = value[4] 51 | tmp_auditing_date2 = value[3] 52 | 53 | for j in range(tmp_term): 54 | if get_month_after(tmp_auditing_date2, j + 1) > tmp_auditing_date1: 55 | listing_info_feature['user_id'].append(tmp_user_id) 56 | listing_info_feature['listing_id'].append(tmp_listing_id) 57 | listing_info_feature['auditing_date'].append(tmp_auditing_date1) 58 | listing_info_feature['order_id'].append(j + 1) 59 | listing_info_feature['order_date'].append(get_month_after(tmp_auditing_date2, j)) 60 | listing_info_feature['due_date'].append(get_month_after(tmp_auditing_date2, j + 1)) 61 | listing_info_feature['order_due_amt'].append(int(value[6] / tmp_term)) 62 | listing_info_feature = pd.DataFrame(listing_info_feature) 63 | print("#"*10 + "basic sample preprocess finished"+"#"*10) 64 | 65 | # 加入未来6个月的时间点 66 | listing_info_feature['month1'] = listing_info_feature.auditing_date.map(lambda x:get_month_after(x,1)) 67 | listing_info_feature['month3'] = listing_info_feature.auditing_date.map(lambda x:get_month_after(x,3)) 68 | listing_info_feature['month6'] = listing_info_feature.auditing_date.map(lambda x:get_month_after(x,6)) 69 | 70 | # 用户在当前标的生效的时间点 还有多少单没有还(listing_id的auditing_date<当前标的的auditing_date) 71 | future_list_counts = listing_info_feature.groupby(['user_id','auditing_date'])['listing_id'].nunique().reset_index().rename(columns={'listing_id':'future_list_counts'}) 72 | future_list_nums = listing_info_feature.groupby(['user_id','auditing_date']).size().reset_index().rename(columns={0:'future_list_nums'}) 73 | 74 | # 未来第一个到期单子到现在的距离。 如果那时候用户经济状态良好,极有可能让本单提早还。 75 | latest_come_date = listing_info_feature.groupby(['user_id','auditing_date'])['due_date'].min().reset_index().rename(columns={'due_date':'latest_come_date'}) 76 | latest_come_date['latest_day_diff'] = latest_come_date.apply(lambda x:Caltime(x['auditing_date'],x['latest_come_date']),axis=1) 77 | 78 | # 未来三个月内的订单情况 非穿越。 79 | future_list_nums_in1month = listing_info_feature[listing_info_feature.due_date='2018-11-01'] 105 | 106 | # 加入放穿越统计特征 107 | cell_province_counts = train_sample.groupby('cell_province').size().reset_index().rename(columns = {0:'cell_province_counts'}) 108 | mean_backward_days_of_cell_province = train_sample[train_sample.is_overdue==0].groupby('cell_province')['backward_days'].mean().reset_index().rename(columns={'backward_days':'mean_backward_days_of_cell_province'}) 109 | mean_due_amt_of_cell_province = train_sample.groupby('cell_province')['due_amt'].mean().reset_index().rename(columns={'due_amt':'mean_due_amt_of_cell_province'}) 110 | mean_term_of_cell_province = train_sample.groupby('cell_province')['term'].mean().reset_index().rename(columns={'term':'mean_term_of_cell_province'}) 111 | mean_is_overdue_cell_province = train_sample.groupby('cell_province')['is_overdue'].mean().reset_index().rename(columns={'is_overdue':'mean_is_overdue_cell_province'}) 112 | mean_regmon_cell_province = train_sample.groupby('cell_province')['reg_mon_diff'].mean().reset_index().rename(columns={'reg_mon_diff':'mean_reg_mon_diff_overdue_cell_province'}) 113 | mean_age_cell_province = train_sample.groupby('cell_province')['age'].mean().reset_index().rename(columns={'age':'mean_age_diff_overdue_cell_province'}) 114 | cell_province_feature = reduce(lambda x, y: pd.merge(x, y, on='cell_province', how='outer'), 115 | [cell_province_counts,mean_backward_days_of_cell_province,mean_due_amt_of_cell_province,mean_term_of_cell_province,\ 116 | mean_is_overdue_cell_province,mean_regmon_cell_province,mean_age_cell_province]) 117 | # 加入放穿越统计特征 118 | id_province_counts = train_sample.groupby('id_province').size().reset_index().rename(columns = {0:'id_province_counts'}) 119 | mean_backward_days_of_id_province = train_sample[train_sample.is_overdue==0].groupby('id_province')['backward_days'].mean().reset_index().rename(columns={'backward_days':'mean_backward_days_of_id_province'}) 120 | mean_due_amt_of_id_province = train_sample.groupby('id_province')['due_amt'].mean().reset_index().rename(columns={'due_amt':'mean_due_amt_of_id_province'}) 121 | mean_term_of_id_province = train_sample.groupby('id_province')['term'].mean().reset_index().rename(columns={'term':'mean_term_of_id_province'}) 122 | mean_is_overdue_id_province = train_sample.groupby('id_province')['is_overdue'].mean().reset_index().rename(columns={'is_overdue':'mean_is_overdue_id_province'}) 123 | mean_regmon_id_province = train_sample.groupby('id_province')['reg_mon_diff'].mean().reset_index().rename(columns={'reg_mon_diff':'mean_reg_mon_diff_overdue_id_province'}) 124 | mean_age_id_province = train_sample.groupby('id_province')['age'].mean().reset_index().rename(columns={'age':'mean_age_diff_overdue_id_province'}) 125 | id_province_feature = reduce(lambda x, y: pd.merge(x, y, on='id_province', how='outer'), 126 | [id_province_counts,mean_backward_days_of_id_province,mean_due_amt_of_id_province,mean_term_of_id_province,\ 127 | mean_is_overdue_id_province,mean_regmon_id_province,mean_age_id_province]) 128 | # 加入放穿越统计特征 129 | id_city_counts = train_sample.groupby('id_city').size().reset_index().rename(columns = {0:'id_city_counts'}) 130 | mean_backward_days_of_id_city = train_sample[train_sample.is_overdue==0].groupby('id_city')['backward_days'].mean().reset_index().rename(columns={'backward_days':'mean_backward_days_of_id_city'}) 131 | mean_due_amt_of_id_city = train_sample.groupby('id_city')['due_amt'].mean().reset_index().rename(columns={'due_amt':'mean_due_amt_of_id_city'}) 132 | mean_term_of_id_city = train_sample.groupby('id_city')['term'].mean().reset_index().rename(columns={'term':'mean_term_of_id_city'}) 133 | mean_is_overdue_id_city = train_sample.groupby('id_city')['is_overdue'].mean().reset_index().rename(columns={'is_overdue':'mean_is_overdue_id_city'}) 134 | mean_regmon_id_city = train_sample.groupby('id_city')['reg_mon_diff'].mean().reset_index().rename(columns={'reg_mon_diff':'mean_reg_mon_diff_overdue_id_city'}) 135 | mean_age_id_city = train_sample.groupby('id_city')['age'].mean().reset_index().rename(columns={'age':'mean_age_diff_overdue_id_city'}) 136 | id_city_feature = reduce(lambda x, y: pd.merge(x, y, on='id_city', how='outer'), 137 | [id_city_counts,mean_backward_days_of_id_city,mean_due_amt_of_id_city,mean_term_of_id_city,\ 138 | mean_is_overdue_id_city,mean_regmon_id_city,mean_age_id_city]) 139 | train_sample = pd.merge(train_sample,id_city_feature,on='id_city',how='left') 140 | testt_sample = pd.merge(testt_sample,id_city_feature,on='id_city',how='left') 141 | 142 | train_sample = pd.merge(train_sample,id_province_feature,on='id_province',how='left') 143 | testt_sample = pd.merge(testt_sample,id_province_feature,on='id_province',how='left') 144 | 145 | train_sample = pd.merge(train_sample,cell_province_feature,on='cell_province',how='left') 146 | testt_sample = pd.merge(testt_sample,cell_province_feature,on='cell_province',how='left') 147 | 148 | train_datas=lgb.Dataset(data=train_sample[features].values,label=train_sample.backward_days,feature_name=features,weight=train_sample.weights) 149 | testt_datas=lgb.Dataset(data=testt_sample[features].values,label=testt_sample.backward_days,feature_name=features,weight=testt_sample.weights) 150 | 151 | params = { 152 | 'nthread': 3, # 进程数 153 | 'max_depth': 4, # 最大深度 154 | 'learning_rate': 0.1, # 学习率 155 | 'bagging_fraction': 1, # 采样数 156 | 'num_leaves': 13, # 终点节点最小样本占比的和 157 | 'feature_fraction': 0.3, # 样本列采样 158 | 'objective': 'multiclass', 159 | 'lambda_l1': 10, # L1 正则化 160 | 'lambda_l2': 1, # L2 正则化 161 | 'bagging_seed': 100, # 随机种子,light中默认为100 162 | 'verbose': 0, 163 | 'num_class': 33, 164 | 'min_data_in_leaf':1000 165 | } 166 | 167 | model_offline = lgb.train(params,train_datas,num_boost_round=1000,valid_sets=[testt_datas,train_datas],early_stopping_rounds=15) 168 | 169 | result1 = model_offline.predict(testt_sample[features]) 170 | 171 | test_result = { 172 | 'listing_id':[], 173 | 'repay_date':[], 174 | 'backward_day':[], 175 | 'repay_amt_predict':[], 176 | 'due_amt':[], 177 | 'repay_amt':[] 178 | } 179 | for i,values in tqdm.tqdm_notebook(enumerate(testt_sample.values)): 180 | real_backward_day = testt_sample.backward_days.values[i] 181 | backward_days_max = Caltime(values[2],values[3]) 182 | 183 | for j in range(backward_days_max+1): 184 | test_result['listing_id'].append(values[1]) 185 | test_result['repay_date'].append(get_date_before(values[3],j)) 186 | test_result['backward_day'].append(j) 187 | test_result['repay_amt_predict'].append(values[4]*result1[i][j]) 188 | test_result['due_amt'].append(values[4]) 189 | if j == real_backward_day: 190 | test_result['repay_amt'].append(values[4]) 191 | else: 192 | test_result['repay_amt'].append(0) 193 | test_result = pd.DataFrame(test_result) 194 | 195 | result_data = testt_sample 196 | result_data = result_data.reset_index(drop=True) 197 | result_data = result_data[['listing_id','auditing_date']] 198 | result_data['listing_group'] = 0 199 | from sklearn.model_selection import KFold 200 | kf = KFold(n_splits=30, shuffle=True, random_state=1) 201 | idxs = [] 202 | for idx in kf.split(result_data): 203 | idxs.append(idx[1]) 204 | i = 0 205 | for idx in idxs: 206 | result_data.loc[idx,'listing_group'] = i 207 | i+=1 208 | 209 | test_result = pd.merge(test_result[['listing_id','repay_date','repay_amt_predict','repay_amt']], result_data, on='listing_id',how='inner') 210 | 211 | from sklearn.metrics import mean_squared_error 212 | print(mean_squared_error(test_result.repay_amt_predict,test_result.repay_amt)) 213 | print(math.sqrt(mean_squared_error(result1.repay_amt_predict,result1.repay_amt))) 214 | print(math.sqrt(mean_squared_error(result2.repay_amt_predict_adjust,result2.repay_amt))) -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: __init__.py.py 10 | @time: 2019/7/8 13:48 11 | 12 | 这一行开始写关于本文件的说明与解释 13 | """ -------------------------------------------------------------------------------- /utils/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinklibrary/ppd_mojing4/13c0d1cd2bd07fbeb7e0d829e705c1707a3dea7a/utils/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /utils/__pycache__/functions.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinklibrary/ppd_mojing4/13c0d1cd2bd07fbeb7e0d829e705c1707a3dea7a/utils/__pycache__/functions.cpython-37.pyc -------------------------------------------------------------------------------- /utils/functions.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: vinklibrary 4 | @contact: shenxvdong1@gmail.com 5 | @site: Todo 6 | 7 | @version: 1.0 8 | @license: None 9 | @file: functions.py 10 | @time: 2019/7/8 13:50 11 | 12 | 这一行开始写关于本文件的说明与解释 13 | """ 14 | import time 15 | import datetime 16 | 17 | # 计算两个日期相差天数,自定义函数名,和两个日期的变量名。 小的日期在前面 18 | def Caltime(date1,date2): 19 | # 跟项目相关,pandas 空值类型为float 20 | if type(date1)!=str or type(date2)!=str: 21 | return -1 22 | if date1 =="\\N" or date2=="\\N": 23 | return -1 24 | date1=time.strptime(date1,"%Y-%m-%d") 25 | date2=time.strptime(date2,"%Y-%m-%d") 26 | date1=datetime.datetime(date1[0],date1[1],date1[2]) 27 | date2=datetime.datetime(date2[0],date2[1],date2[2]) 28 | #返回两个变量相差的值,就是相差天数 29 | return (date2-date1).days 30 | 31 | # 获得n天前(后)的日期 32 | def get_date_before(date1, days): 33 | date1=time.strptime(date1,"%Y-%m-%d") 34 | date1=datetime.datetime(date1[0],date1[1],date1[2]) 35 | return str(date1-datetime.timedelta(days=days))[0:10] 36 | 37 | def get_date_after(date1, days): 38 | date1=time.strptime(date1,"%Y-%m-%d") 39 | date1=datetime.datetime(date1[0],date1[1],date1[2]) 40 | return str(date1+datetime.timedelta(days=days))[0:10] 41 | 42 | # 计算两个月份之间相差几个月 mon1>mon2 43 | def CalMon(mon1,mon2): 44 | mon1_y = int(mon1[0:4]) 45 | mon1_m = int(mon1[5:7]) 46 | mon2_y = int(mon2[0:4]) 47 | mon2_m = int(mon2[5:7]) 48 | return (mon1_y-mon2_y)*12+(mon1_m-mon2_m) 49 | 50 | # 获得当前日期n个月前的时间 51 | def get_month_before(tmp_auditing_date,num): 52 | # 项目相关。闰年未考虑 53 | month_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 54 | if int(tmp_auditing_date[5:7]) <= num: 55 | month = 12-num+int(tmp_auditing_date[5:7]) 56 | day = int(tmp_auditing_date[8:10]) if int(tmp_auditing_date[8:10])<=month_days[month] else month_days[month] 57 | before_date = datetime.date(int(tmp_auditing_date[0:4])-1,12-num+int(tmp_auditing_date[5:7]),day) 58 | else: 59 | month = int(tmp_auditing_date[5:7]) - num 60 | day = int(tmp_auditing_date[8:10]) if int(tmp_auditing_date[8:10])<=month_days[month] else month_days[month] 61 | before_date = datetime.date(int(tmp_auditing_date[0:4]),int(tmp_auditing_date[5:7]) - num,day) 62 | return str(before_date) 63 | 64 | def get_month_after(tmp_auditing_date,num): 65 | month_days = [0,31,28,31,30,31,30,31,31,30,31,30,31] 66 | if int(tmp_auditing_date[5:7]) + num <= 12: 67 | month = num+int(tmp_auditing_date[5:7]) 68 | day = int(tmp_auditing_date[8:10]) if int(tmp_auditing_date[8:10])<=month_days[month] else month_days[month] 69 | before_date = datetime.date(int(tmp_auditing_date[0:4]),month,day) 70 | else: 71 | month = int(tmp_auditing_date[5:7]) + num - 12 72 | day = int(tmp_auditing_date[8:10]) if int(tmp_auditing_date[8:10])<=month_days[month] else month_days[month] 73 | before_date = datetime.date(int(tmp_auditing_date[0:4])+1, month ,day) 74 | return str(before_date) 75 | 76 | # 获得曜日dow信息 77 | def get_day_of_week(date1): 78 | date1 = datetime.date(int(date1[0:4]),int(date1[5:7]),int(date1[8:10])) 79 | return date1.weekday() 80 | 81 | # 转化率平滑计算 col1/col2 82 | def get_converation_rate(df,col1,col2,mean=None,mean_rate=None): 83 | if mean == None: 84 | mean = df[col2].mean() 85 | if mean_rate == None: 86 | mean_rate = df[col1].sum()/df[col2].sum() 87 | return (df[col1]+mean*mean_rate)/(df[col2]+mean),mean,mean_rate --------------------------------------------------------------------------------