├── AutomatedLoanRepayment.ipynb
├── README.md
├── output.svg
├── output_34_0.svg
└── requirements.txt
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # 基于Featuretools自动特征工程构建预测贷款偿还特征
3 |
4 |
5 | 特征工程是一个基于现有数据构建新特征(也称作预测变量或具有解释的变量)的过程. 传统上,使用领域知识手工一次构建一个来完成特征工程.在以前的笔记中,我们知道特征工程对于数据科学至关重要但人工方法很耗时,乏味,容易出错以及必须针对每个项目重做.自动化特征工程是在短时间内从一组关系表中自动构建成百上千新特征从而如同人工方法一样在这一关键过程中为数据科学家提供帮助.在本文,我们使用[Featuretools,一个Python开源库](https://www.featuretools.com/)将自动化特征工程应用于房贷信用贷违约风险数据集以进行自动构建特征.
6 |
7 |
9 | 这个课题是目前在Kaggle上进行的机器学习竞赛项目,意图是给定历史贷款和申请数据的情况下,预测申请人是否会拖欠贷款. 数据分布在7个不同的表中, 这成为自动化特征工程中一个目标:必须将所有数据整合到一个DataFrame数据框用作训练(一个用于测试), 旨在为预测问题捕获尽可能多的可用信息. 正如所见,Featuretools只需要几行代码有效地执行这些繁琐的过程,即基于这些表构建新特征.而且,此代码通用与任何数据科学问题.
10 |
11 |
12 | 在本文中,我们将实现一个自动特征工程方法来解决贷款偿还问题.而Featuretools提供了许多用于自定义的选项以提高准确性,我们将更专注于比较高级的实现.(后文,包括"Engine Life"和"Retail Spending"展示该库的一些额外功能).
13 |
14 |
15 | 本文方法将包括以下内容:
16 |
17 |
18 | * 读取关系数据表集合
19 |
20 | * 创建一个Featuretools的`实体集`且为它添加`实体`
21 |
22 | * 根据需要识别正确的变量类型
23 |
24 | * 识别数据索引
25 |
26 | * 为`实体`之间添加关联关系
27 |
28 | * 选择特征基元用于创建新特征
29 |
30 | * 基本基元集
31 |
32 | * 检查将要被创建的特征
33 |
34 | * 运行深度特征合成生成成千上万个新特征
35 |
36 |
37 |
38 | ## 课题中的数据集
39 |
40 |
41 | 目前在Kaggle上运行的[房屋贷款信用违约风险竞赛](https://www.kaggle.com/c/home-credit-default-risk)是一项监督分类任务,目的是预测申请人(或称之为客户)申请贷款将是否拖欠该贷款. 该数据包括客户的社会经济指标,历史贷款的特定财务信息,以及有关 Home Credit(赞助比赛的机构)和其他信贷机构历史贷款的综合数据. 此项竞赛的度量标准是受试者特征曲线(ROC AUC),并根据违约情况进行了预测。我们可以通过交叉验证训练数据(带有标签)或将测试预测提交给Kaggle以查看我们在公共排行榜上的位置(仅通过10%的测试计算得出)来评估提交的内容数据.
42 |
43 |
44 | [房屋贷款信用风险数据集](https://www.kaggle.com/c/home-credit-default-risk/data)是由7个表构成:
45 |
46 |
47 | * application_train/application_test:trainning/testing为每个客户的主要家庭授信数据, 主要包括客户社会经济指标和特定的贷款特征.每笔贷款都有唯一的行并由特征`SK_ID_CURR`作为唯一标识符.application_train数据带有`TARGET`列, 其中,0:标识贷款已被偿还, 1标识贷款未被偿还.
48 |
49 |
50 | * bureau: 历史信用数据, 有关客户历史从其他金融机构获得的信用数据(而非家庭信贷)的数据。每笔贷款历史信用在表中都有其自己的行,但是申请表中的一个客户可以具有多笔历史借贷数据。历史信用表由特征`SK_ID_BUREAU`作为唯一标识.
51 |
52 |
53 | * bureau_balance: 贷款账户月度余额,有关征信局的贷款账户月度余额数据.每行都有一个贷款账户信息,单个贷款账户可以有很多行. 这是征信局中回溯每笔贷款历史还款行为数据, 通过`SK_ID_BUREAU`标识每笔贷款(没有唯一索引).
54 |
55 |
56 | * previous_application:在申请表中的客户历史上在Home Credit申请贷款笔数,申请数据表中的每个客户可以拥有多笔历史贷款. 每笔历史贷款在这张表中都有且仅有一行,并由特征`SK_ID_PREV`作为唯一标识.
57 |
58 |
59 | * POS_CASH_BALANCE: 月度余额, 有关历史销售点的月度数据或来自历史贷款数据的现金贷款. 每行是销售点或现金贷款的一个月的数据, 并且一笔历史的贷款可以有多行.这是征信局中回溯每笔现金贷款或销售点历史还款行为数据, 通过`SK_ID_PREV`标识每笔贷款(没有唯一索引).
60 |
61 |
62 | * credit_card_balance: 信用卡贷款月度余额数据, 每一行记录一张信用卡一个月的余额, 且单张信用卡可以有很多行. 这些记录是回溯信用卡历史贷款每个月还款数据,并通过`SK_ID_PREV`标识每张卡(没有唯一索引).
63 |
64 |
65 | * installments_payment: 分期付款, 以前在Home Credit上的贷款付款历史记录. 每笔付款和每笔未支付的款项都有一行. 通过`SK_ID_PREV`关联历史贷款(在此表中不是唯一的).
66 |
67 |
68 | ```python
69 | # pandas and numpy for data manipulation
70 | import pandas as pd
71 | import numpy as np
72 |
73 | # featuretools for automated feature engineering
74 | import featuretools as ft
75 | ```
76 |
77 |
78 | ### 读取数据
79 |
80 |
81 | 首先我们需要读取这7张表,并进行异常值处理(这在手工特征工程也一样).
82 |
83 |
84 | ```python
85 | # Read in the datasets and replace the anomalous values
86 | # 读取数据集并替换异常值
87 | app_train = pd.read_csv('input/application_train.csv').replace({365243: np.nan})
88 | app_test = pd.read_csv('input/application_test.csv').replace({365243: np.nan})
89 | bureau = pd.read_csv('input/bureau.csv').replace({365243: np.nan})
90 | bureau_balance = pd.read_csv('input/bureau_balance.csv').replace({365243: np.nan})
91 | cash = pd.read_csv('input/POS_CASH_balance.csv').replace({365243: np.nan})
92 | credit = pd.read_csv('input/credit_card_balance.csv').replace({365243: np.nan})
93 | previous = pd.read_csv('input/previous_application.csv').replace({365243: np.nan})
94 | installments = pd.read_csv('input/installments_payments.csv').replace({365243: np.nan})
95 | ```
96 |
97 |
98 | 我们将traing和testing数据集合并以确保生成相同的特征, 特征矩阵被构建后,再把两个数据集拆分.
99 |
100 |
101 | ```python
102 | app_test['TARGET'] = np.nan
103 |
104 | # Join together training and testing
105 | app = app_train.append(app_test, ignore_index = True, sort = True)
106 | ```
107 |
108 |
109 | 几个索引是错误的数据类型(浮点数),因此我们需要使它们全部相同(整数)以添加关系,且以 0 填充缺失值
110 |
111 |
112 | ```python
113 | for index in ['SK_ID_CURR', 'SK_ID_PREV', 'SK_ID_BUREAU']:
114 | for dataset in [app, bureau, bureau_balance, cash, credit, previous, installments]:
115 | if index in list(dataset.columns):
116 | dataset[index] = dataset[index].fillna(0).astype(np.int64)
117 | ```
118 |
119 |
120 | # Featuretools 基础
121 |
122 |
123 | [Featuretools](https://docs.featuretools.com/#minute-quick-start)是一个使用一种技术叫做`Deep Feature Synthesis` 深度特征合成从一组相关表中创建特征的python开源库.自动特征工程,像许多机器学习主题一样, 是一个基于简单思想的复杂主题.通过这一次研究这些思想,我们建立对Featuretools的理解,以后更加充分利用它.
124 |
125 |
126 | 在此过程中我们会涉及一些概念:
127 |
128 |
129 | * [实体 and 实体集合](https://docs.featuretools.com/loading_data/using_entitysets.html): 表和用于追踪所有表格的数据结构
130 |
131 | * [表之间关系](https://docs.featuretools.com/loading_data/using_entitysets.html#adding-a-relationship):一张表与另一张表如何关联
132 |
133 | * [特征基元](https://docs.featuretools.com/automated_feature_engineering/primitives.html):聚合和转换通过堆叠以构建特征,通俗理解就是业务主键,通常是业务订单号.
134 |
135 | * [深度特征合成](https://docs.featuretools.com/automated_feature_engineering/afe.html): 使用特征基元生成成千上万个新特征的方法
136 |
137 |
138 | # 实体和实体集合
139 |
140 | 实体是简单的一张表或pandas的`dataframe`数据框. 观测值必须在行中,特征必须在列中. 一个实体在Featuretools中必须有一个唯一的缩影且索引不重复. 目前, 只有`app`, `bureau`和`previous`具有唯一索引(分别是`SK_ID_CURR`, `SK_ID_BUREAU`, 和 `SK_ID_PREV`). 对于别的数据框, 我们必须通过制定参数`make_index=True`参数为这些没有唯一索引的数据框创建索引并命名.
141 |
142 |
143 | 实体也可以具有时间索引,一个时间索引可以指定该行信息(任何数据中都没有日期时间, 但是有一些相对时间(以月或天为单位)可以视为时间变量, 尽管在本文中我们不会将其用作时间索引).
144 |
145 |
146 | 一个实体集由表及其之间的关系构成. 这来自数据结构自身的方法和属性.使用实体集可使我们将多个表组合在一起使创建特征, 这比跟踪单个表和关系简单得多.实体集和实体是可以应用于任何数据集的抽象,因为它们不依赖于基础数据特定字段.
147 |
148 |
149 | 首先我们初始化一个空实体集命名为`clients`.
150 |
151 |
152 | ```python
153 | # Entity set with id applications
154 | es = ft.EntitySet(id = 'clients')
155 | ```
156 |
157 |
158 | ### 变量类型
159 |
160 |
161 | Featuretools 会自动推断变量类型. 然而,有些特征我们需要向Featuretools指定变量类型, 例如表现为整数的`bool`变量. 在Featuretools中可以通过一个字典指定变量类型.
162 |
163 |
164 | 我们首先要为`app`数据指定正确的变量类型,即辨别已经记录为数值(1.0或0.0)的`Boolean`变量,我们可以遍历数据集中的每个变量是否只有2值且类型为数值.我们也可以直接指定某个列为特定类型, 例如有序变量`Ordinal`类型.识别正确的变量类型很重要,因为Featuretools是根据不同的数据类型运行不同的操作逻辑(如同我们进行人工构建特征工程一样).
165 |
166 |
167 | ```python
168 | import featuretools.variable_types as vtypes
169 | ```
170 |
171 |
172 | ```python
173 | app_types = {}
174 |
175 | # Handle the Boolean variables:
176 | # 手动指定`Boolean`变量
177 |
178 | for col in app.columns:
179 | if (app[col].nunique() == 2) and (app[col].dtype == float):
180 | app_types[col] = vtypes.Boolean
181 |
182 | # Remove the `TARGET`
183 | del app_types['TARGET']
184 |
185 | print('There are {} Boolean variables in the application data.'.format(len(app_types)))
186 | ```
187 |
188 | There are 32 Boolean variables in the application data.
189 |
190 |
191 |
192 | ```python
193 | # Ordinal variables
194 | # 有序离散变量
195 | app_types['REGION_RATING_CLIENT'] = vtypes.Ordinal
196 | app_types['REGION_RATING_CLIENT_W_CITY'] = vtypes.Ordinal
197 | app_types['HOUR_APPR_PROCESS_START'] = vtypes.Ordinal
198 | ```
199 |
200 |
201 | `previous`数据表是唯一应记录为`Boolean`特征的其他实体.正确地指定变量类型避免Featuretools构建无意义的特征,例如对`Boolean`进行求均值或求最大值.
202 |
203 |
204 | ```python
205 | previous_types = {}
206 |
207 | # Handle the Boolean variables:
208 | for col in previous:
209 | if (previous[col].nunique() == 2) and (previous[col].dtype == float):
210 | previous_types[col] = vtypes.Boolean
211 |
212 | print('There are {} Boolean variables in the previous data.'.format(len(previous_types)))
213 | ```
214 |
215 | There are 2 Boolean variables in the previous data.
216 |
217 |
218 |
219 | 除了标记为`Boolean`变量外, 我们必须确保Featuretools不会构造没有意义的特征, 例如统计聚合(均值,最大值等).`credit`, `cash`, and `installments`表数据都有`SK_ID_CURR` 变量. 然而,实际上我们不需要这个变量,因为是通过表`previous`中的 `SK_ID_PREV`关联到表`app`中.
220 |
221 |
223 | 我们并不基于`SK_ID_CURR`创建特征, 因为他是没有业务意义的id,并不具有预测能力. 我们处理这些变量的选项是告诉Featuretools忽略它们. 或者在将数据表加入实体集之前就将它们移除, 我们将采用后一种方法.
224 |
225 |
226 | ```python
227 | installments = installments.drop(columns = ['SK_ID_CURR'])
228 | credit = credit.drop(columns = ['SK_ID_CURR'])
229 | cash = cash.drop(columns = ['SK_ID_CURR'])
230 | ```
231 |
232 |
233 | ## 添加实体集
234 |
235 |
现在我们定义了每个实体,表数据, 并将其添加到实体集. 如果没有表索引则需要创建索引或设置参数`make_index=True`. 在这个案例中我们需要创建一个索引,并为这个索引命名. 与此同时,我们通过变量类型字典指定个别需特定的变量类型. 以下代码将7张表添加到实体集中.
236 |
237 |
238 | ```python
239 | # Entities with a unique index
240 | # 具有唯一索引的实体
241 | es = es.entity_from_dataframe(entity_id = 'app', dataframe = app, index = 'SK_ID_CURR',
242 | variable_types = app_types)
243 |
244 | es = es.entity_from_dataframe(entity_id = 'bureau', dataframe = bureau, index = 'SK_ID_BUREAU')
245 |
246 | es = es.entity_from_dataframe(entity_id = 'previous', dataframe = previous, index = 'SK_ID_PREV',
247 | variable_types = previous_types)
248 |
249 | # Entities that do not have a unique index
250 | # 没有唯一索引的实体, 通过`make_index=True`参数标记,并通过`index`参数命名
251 | es = es.entity_from_dataframe(entity_id = 'bureau_balance', dataframe = bureau_balance,
252 | make_index = True, index = 'bureaubalance_index')
253 |
254 | es = es.entity_from_dataframe(entity_id = 'cash', dataframe = cash,
255 | make_index = True, index = 'cash_index')
256 |
257 | es = es.entity_from_dataframe(entity_id = 'installments', dataframe = installments,
258 | make_index = True, index = 'installments_index')
259 |
260 | es = es.entity_from_dataframe(entity_id = 'credit', dataframe = credit,
261 | make_index = True, index = 'credit_index')
262 | ```
263 |
264 |
265 | ```python
266 | # Display entityset so far
267 | # 显示到目前为止的添加的实体
268 | es
269 | ```
270 |
271 |
272 |
273 |
274 | Entityset: clients
275 | Entities:
276 | app [Rows: 356255, Columns: 122]
277 | bureau [Rows: 1716428, Columns: 17]
278 | previous [Rows: 1670214, Columns: 37]
279 | bureau_balance [Rows: 27299925, Columns: 4]
280 | cash [Rows: 10001358, Columns: 8]
281 | installments [Rows: 13605401, Columns: 8]
282 | credit [Rows: 3840312, Columns: 23]
283 | Relationships:
284 | No relationships
285 |
286 |
287 |
288 |
289 |
`实体集`允许我们将所有分表组成一个数据结构. 这相对于一次操作一张表容易得多(正如我们人工构建特征工程那样).
290 |
291 |
292 | # 实体间关系
293 |
294 |
实体间关系不仅是Featuretools中的一个基本概念,也是任何关系型数据库表间关系.表间关系最常见得类型之一就是一对多关系.一对多关系最好的思路之一是将其比喻为父子关系.父是一个单一的个体,但是可以有多个子.在表前后关系中, 一个父表对应每个个体都有唯一一个观测行, 而每个子表对应父表每个个体可以有很多个观测行.在一个父表中, 每个个体有一个单独的行且是通过索引确定的唯一行(也可以叫做主键). 每一个父表中的个体在子表中可以有多行.还可以更复杂,因为子表也可以有他们自己的子表.作为父表的孙子表.
295 |
296 |
297 |
作为父与子关系案例, 每个客户在数据框`app`都有一行(通过`SK_ID_CURR`标记),与此同时每个客户在表`bureau`中有多笔历史贷款.因此, 表`bureau`是表`app`的子表. 表`bureau`是表`bureau_balance`父表因为表`bureau`中的每一笔贷款(通过`SK_ID_BUREAU`指定)在表`bureau_balance`中有多个月还款记录.当我们进行人工特征工程时,跟踪这些关系需要投入大量的时间(也是常见错误的来源). 我们可以将这些关系添加到`实体集`中并让Featuretools跟踪执行.
298 |
299 |
300 | ```python
301 | print('Parent: app, Parent Variable of bureau: SK_ID_CURR\n\n', app.iloc[:, 111:115].head())
302 | print('\nChild: bureau, Child Variable of app: SK_ID_CURR\n\n', bureau.iloc[:, :5].head())
303 | ```
304 |
305 | Parent: app, Parent Variable of bureau: SK_ID_CURR
306 |
307 | SK_ID_CURR TARGET TOTALAREA_MODE WALLSMATERIAL_MODE
308 | 0 100002 1.0 0.0149 Stone, brick
309 | 1 100003 0.0 0.0714 Block
310 | 2 100004 0.0 NaN NaN
311 | 3 100006 0.0 NaN NaN
312 | 4 100007 0.0 NaN NaN
313 |
314 | Child: bureau, Child Variable of app: SK_ID_CURR
315 |
316 | SK_ID_CURR SK_ID_BUREAU CREDIT_ACTIVE CREDIT_CURRENCY DAYS_CREDIT
317 | 0 215354 5714462 Closed currency 1 -497.0
318 | 1 215354 5714463 Active currency 1 -208.0
319 | 2 215354 5714464 Active currency 1 -203.0
320 | 3 215354 5714465 Active currency 1 -203.0
321 | 4 215354 5714466 Active currency 1 -629.0
322 |
323 |
324 |
325 | `SK_ID_CURR`215354行在父表中每一个只有一行但是在子表中有多行.
326 |
327 |
两张表通过一个共享变量链接. 表`app`和表`bureau`通过`Sk_ID_CURR`变量链接, 而表`bureau`和表`bureau_balance`通过`SK_ID_BUREAU`链接. 这个链接变量在父表中称为`父`变量, 在子表中称为`子`变量.
328 |
329 |
330 | ```python
331 | print('Parent: bureau, Parent Variable of bureau_balance: SK_ID_BUREAU\n\n', bureau.iloc[:, :5].head())
332 | print('\nChild: bureau_balance, Child Variable of bureau: SK_ID_BUREAU\n\n', bureau_balance.head())
333 | ```
334 |
335 | Parent: bureau, Parent Variable of bureau_balance: SK_ID_BUREAU
336 |
337 | SK_ID_CURR SK_ID_BUREAU CREDIT_ACTIVE CREDIT_CURRENCY DAYS_CREDIT
338 | 0 215354 5714462 Closed currency 1 -497.0
339 | 1 215354 5714463 Active currency 1 -208.0
340 | 2 215354 5714464 Active currency 1 -203.0
341 | 3 215354 5714465 Active currency 1 -203.0
342 | 4 215354 5714466 Active currency 1 -629.0
343 |
344 | Child: bureau_balance, Child Variable of bureau: SK_ID_BUREAU
345 |
346 | bureaubalance_index SK_ID_BUREAU MONTHS_BALANCE STATUS
347 | 0 0 5715448 0 C
348 | 1 1 5715448 -1 C
349 | 2 2 5715448 -2 C
350 | 3 3 5715448 -3 C
351 | 4 4 5715448 -4 C
352 |
353 |
354 |
355 |
常规上,我们利用父与子之间关系通过将一个父的所有子分组聚合数据从而进行统计计算. 例如,我们可以将一个客户的所有贷款汇总并计算平均贷款金额.这是很直接的,但是当我们想要构建这些成百上千的特征就会变得非常乏味.一次构建一个这样的特征非常低效,因为我们需要一遍又一遍重复写这些代码且这些代码并不能应用于不同的项目!
356 |
357 |
358 |
当我们需要聚合祖孙表时事情将变得更糟糕, 因为我们需要两步: 首先聚合父级, 然后再聚合祖父级.待会我们将见到Featuretools可以自动完成这些工作, 并给予这些数据生成成千上万的特征. 当我们人工构建这些特征平均每个花费15分钟(正如我们人工构建特征一样),如果完成Featuretools构建这些特征将耗费数小时.
359 |
360 |
361 |
362 | ### 添加关系
363 |
364 |
直接使用图数据表定义关系, 对于每个关系,首先需要指定父变量和子变量,在这些表之间总共有6个关系(将trainning和testing关系当做一个).以下我们指明实体与实体之间的关系并将其添加到实体集中.
365 |
366 |
367 | ```python
368 | # Relationship between app_train and bureau
369 | # app_train 与 bureau 关联
370 | r_app_bureau = ft.Relationship(es['app']['SK_ID_CURR'], es['bureau']['SK_ID_CURR'])
371 |
372 | # Relationship between bureau and bureau balance
373 | # bureau 与 balance 关联
374 | r_bureau_balance = ft.Relationship(es['bureau']['SK_ID_BUREAU'], es['bureau_balance']['SK_ID_BUREAU'])
375 |
376 | # Relationship between current app and previous apps
377 | # app 和 previous apps 关联
378 | r_app_previous = ft.Relationship(es['app']['SK_ID_CURR'], es['previous']['SK_ID_CURR'])
379 |
380 | # Relationships between previous apps and cash, installments, and credit
381 | # previous apps and cash, installments, and credit实体之间的关系
382 |
383 | r_previous_cash = ft.Relationship(es['previous']['SK_ID_PREV'], es['cash']['SK_ID_PREV'])
384 | r_previous_installments = ft.Relationship(es['previous']['SK_ID_PREV'], es['installments']['SK_ID_PREV'])
385 | r_previous_credit = ft.Relationship(es['previous']['SK_ID_PREV'], es['credit']['SK_ID_PREV'])
386 | ```
387 |
388 |
389 | ```python
390 | # Add in the defined relationships
391 | # 添加定义的实体关系
392 | es = es.add_relationships([r_app_bureau, r_bureau_balance, r_app_previous,
393 | r_previous_cash, r_previous_installments, r_previous_credit])
394 | # Print out the EntitySet
395 | es
396 | ```
397 |
398 |
399 |
400 |
401 | Entityset: clients
402 | Entities:
403 | app [Rows: 356255, Columns: 122]
404 | bureau [Rows: 1716428, Columns: 17]
405 | previous [Rows: 1670214, Columns: 37]
406 | bureau_balance [Rows: 27299925, Columns: 4]
407 | cash [Rows: 10001358, Columns: 8]
408 | installments [Rows: 13605401, Columns: 8]
409 | credit [Rows: 3840312, Columns: 23]
410 | Relationships:
411 | bureau.SK_ID_CURR -> app.SK_ID_CURR
412 | bureau_balance.SK_ID_BUREAU -> bureau.SK_ID_BUREAU
413 | previous.SK_ID_CURR -> app.SK_ID_CURR
414 | cash.SK_ID_PREV -> previous.SK_ID_PREV
415 | installments.SK_ID_PREV -> previous.SK_ID_PREV
416 | credit.SK_ID_PREV -> previous.SK_ID_PREV
417 |
418 |
419 |
420 |
421 |
再次,使用实体集的好处是实体集能为我们追踪实体关系.这使得我们可以在更高的抽象水平上工作, 考虑整个数据集,而不是每个个体数据表, 极大地提升效率.
422 |
423 |
424 |
粗略概要:我们需要谨慎地在多个来自父表的子表创建菱形图链路. 如果我们直接通过`SK_ID_CURR`链接表`app`和表`cash`;通过`SK_ID_PREV`链接`previous` and `cash`; 通过`SK_ID_CURR`链接`app` 和 `previous`, 然后通过`app`和`cash`创建链路.这个链路显得模糊不清, 因此我们可以通过`previous`替代`app`和`cash`的链接,使用`SK_ID_PREV`在`previous`(父)与`cash`(子)之间创建关系. 然后使用`SK_ID_CURR`在`app`(父)和`previous`(子)之间创建一个关系.然后Featuretools可以通过多重基元堆叠在`app`上创建特征衍生自`previous`和`cash`.
425 |
426 |
427 |
如果这不好理解, 那么只要记住一个路径包括一个父到任何后代. 例如, 一个祖父到祖孙的链路是通过父而不是直接通过共享变量.
428 |
429 |
430 |
所有实体都可以通过链路进行关联.理论上这使得我们可以计算任何实体特征, 但是在实践中, 我们只以表`app`为基准计算特征因为他是用于训练/预测. 最终结果是一个dataframe数据框,且每一个在`app`中客户将有成千上万个特征.
431 |
432 |
433 | 我们几乎可以开始构建成千上万个特征了, 但是还有一些基础概念需要了解. 以下将介绍特征基元.
434 |
435 |
436 | ## 可视化特征实体
437 |
438 |
439 | ```python
440 | %matplotlib inline
441 | ```
442 |
443 |
444 | ```python
445 | es.plot()
446 | ```
447 |
448 |
449 |
450 |
451 | 
452 |
453 |
454 |
455 |
456 | # 特征基元
457 |
458 |
459 |
特征基元是应用于表或一组表集来构建特征的操作准则.这些简单的计算方法, 我们在人工构造变量过程中已经使用了很多,可以彼此堆叠以创建复杂的深层特征.特征基元分为两类:
460 |
461 |
462 | * __聚合__: 该函数将每个父的子分组在一起进行统计数据, 例如平均值,最小值,最大值或标准差,以及子之间的交叉. 一个示例是每个客户的最大历史贷款金额, 使用表之间的关系多个表进行聚合.
463 |
464 | * __转换__: 该操作应用于单表中一个或多个列. 例如取一列的绝对值,或查找一张表中两列的差值.
465 |
466 |
467 |
468 |
Featuretools可以使用的特征基元如下表.
469 |
470 |
471 | ```python
472 | # List the primitives in a dataframe
473 | # 特征基元表.
474 | primitives = ft.list_primitives()
475 | pd.options.display.max_colwidth = 100
476 |
477 | primitives[primitives['type'] == 'aggregation'].tail(10)
478 | ```
479 |
480 |
481 |
482 |
483 |
484 |
497 |
498 |
499 |
500 | |
501 | name |
502 | type |
503 | description |
504 |
505 |
506 |
507 |
508 | 12 |
509 | last |
510 | aggregation |
511 | Determines the last value in a list. |
512 |
513 |
514 | 13 |
515 | mode |
516 | aggregation |
517 | Determines the most commonly repeated value. |
518 |
519 |
520 | 14 |
521 | sum |
522 | aggregation |
523 | Calculates the total addition, ignoring `NaN`. |
524 |
525 |
526 | 15 |
527 | std |
528 | aggregation |
529 | Computes the dispersion relative to the mean value, ignoring `NaN`. |
530 |
531 |
532 | 16 |
533 | median |
534 | aggregation |
535 | Determines the middlemost number in a list of values. |
536 |
537 |
538 | 17 |
539 | avg_time_between |
540 | aggregation |
541 | Computes the average number of seconds between consecutive events. |
542 |
543 |
544 | 18 |
545 | trend |
546 | aggregation |
547 | Calculates the trend of a variable over time. |
548 |
549 |
550 | 19 |
551 | percent_true |
552 | aggregation |
553 | Determines the percent of `True` values. |
554 |
555 |
556 | 20 |
557 | time_since_last |
558 | aggregation |
559 | Calculates the time elapsed since the last datetime (default in seconds). |
560 |
561 |
562 | 21 |
563 | num_unique |
564 | aggregation |
565 | Determines the number of distinct values, ignoring `NaN` values. |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 | ```python
575 | primitives[primitives['type'] == 'transform'].tail(10)
576 | ```
577 |
578 |
579 |
580 |
581 |
582 |
595 |
596 |
597 |
598 | |
599 | name |
600 | type |
601 | description |
602 |
603 |
604 |
605 |
606 | 68 |
607 | less_than_equal_to_scalar |
608 | transform |
609 | Determines if values are less than or equal to a given scalar. |
610 |
611 |
612 | 69 |
613 | isin |
614 | transform |
615 | Determines whether a value is present in a provided list. |
616 |
617 |
618 | 70 |
619 | cum_mean |
620 | transform |
621 | Calculates the cumulative mean. |
622 |
623 |
624 | 71 |
625 | add_numeric_scalar |
626 | transform |
627 | Add a scalar to each value in the list. |
628 |
629 |
630 | 72 |
631 | less_than |
632 | transform |
633 | Determines if values in one list are less than another list. |
634 |
635 |
636 | 73 |
637 | num_words |
638 | transform |
639 | Determines the number of words in a string by counting the spaces. |
640 |
641 |
642 | 74 |
643 | absolute |
644 | transform |
645 | Computes the absolute value of a number. |
646 |
647 |
648 | 75 |
649 | cum_sum |
650 | transform |
651 | Calculates the cumulative sum. |
652 |
653 |
654 | 76 |
655 | multiply_boolean |
656 | transform |
657 | Element-wise multiplication of two lists of boolean values. |
658 |
659 |
660 | 77 |
661 | not_equal_scalar |
662 | transform |
663 | Determines if values in a list are not equal to a given scalar. |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 | ```python
673 | primitives.type.unique()
674 | ```
675 |
676 |
677 |
678 |
679 | array(['aggregation', 'transform'], dtype=object)
680 |
681 |
682 |
683 |
684 | # 深度特征合成
685 |
686 |
687 |
[深度特征合成 (DFS)]是featuretools用于创建新特征的方法.DFS基于特征基元堆栈特征"深度"等同于特征数.例如, 我们要计算客户历史借款最大值(称`MAX(previous.loan_amount)`),这是一个深度为1的深度特征.创建一个深度为2的特征. 我们需要堆叠特征客户的每笔贷款平均月还款额的最大值(例如`MAX(previous(MEAN(installments.payment)))`).在手动特征工程中, 需要两次单独分组和聚合花费超过15分钟为每一个特征写代码.
688 |
689 |
690 |
深度特征合成是一个非常有效的方法,在创造特征过程中为我们人类克服了时间局限性和想象力.无需独立自主进行思考(不用耐心去实现特征构建).因此,DFS只需要进行细微调整即可应用于任何数据集. 在特征工程中, 我们通常应用相同函数于不同的数据集, 但是当我们手动完成时, 必须重复编写代码, 因为每个项目是特定的. Featuretools代码可以应用于任何数据集因为他是更高级的抽象.
691 |
692 |
693 | 你可以阅读[使用深度特征合成在自动化特征工程的论文]如果你想更深层次理解的话.
694 |
695 |
696 |
为了在Featuretools中执行DFS, 我们通过使用`dfs`函数的`entityset`,`target_entity`(我们需要制作的特征), `agg_primitives`,`trans_primitives`,`max_depth`等参数,以及其他一些参数,具体取决于我们的用例. 还有多任务参数`njobs`和信息打印参数`verbose`.
697 |
698 |
699 |
另一个重要参数是`eatures_only`. 如果我们设置为`True`, `dfs`将只生成特征名,而不会实际计算出特征值(特征矩阵值). 当我们进行特征创建和保存检查时,只生成特征名而不进行计算是非常有用的.
700 |
701 |
702 | ## 深度特征合成默认原函数
703 |
704 |
705 |
在Featuretools中不使用任何领域知识我们可以使用深度特征合成默认原函数构建成千上万特征.首先使用聚合和转换原函数. 一个深度为2的实体特征. 将只生成特征名进行保存和检查.
706 |
707 |
708 | ```python
709 | # Default primitives from featuretools
710 | default_agg_primitives = ["sum", "std", "max", "skew", "min", "mean", "count", "percent_true", "num_unique", "mode"]
711 | default_trans_primitives = ["day", "year", "month", "weekday", "haversine", "num_words", "num_characters"]
712 |
713 | # DFS with specified primitives
714 | feature_names = ft.dfs(entityset = es, target_entity = 'app',
715 | trans_primitives = default_trans_primitives,
716 | agg_primitives=default_agg_primitives,
717 | where_primitives = [], seed_features = [],
718 | max_depth = 2, n_jobs = -1, verbose = 1,
719 | features_only=True)
720 | ```
721 |
722 | Built 2069 features
723 |
724 |
725 |
726 |
即使是对深度特征合成基本调用也能为我们生成1500多个特征. 诚然,这些特征并非都重要. 但是依旧意味着我们可以节约数百小时.此外,`dfs`也许能找到重要的特征, 在这之前我们从未想到过的特征.
727 |
728 |
729 |
我们可以查看一些特征名称:
730 |
731 |
732 | ```python
733 | feature_names[-15:]
734 | ```
735 |
736 |
737 |
738 |
739 | [,
740 | ,
741 | ,
742 | ,
743 | ,
744 | ,
745 | ,
746 | ,
747 | ,
748 | ,
749 | ,
750 | ,
751 | ,
752 | ,
753 | ]
754 |
755 |
756 |
757 |
758 |
注意Featuretools是如何将多个基元进行批次堆叠.这是深度特征合成和自动特征工程的背后思路之一,不必人工进行分组和聚合. Featuretools可以使用架构(`实体`,`关系`,和`基元`)进行, 我们也可以使用Featuretools扩展我们的领域知识.
759 |
760 |
761 | # 建立在领域知识上的特征
762 |
763 |
764 |
765 |
Featuretools可以自动为我们构建成千上万个特征. 但是并不意味着我们不能使用领域知识去提升预测性能. Featuretools可以添加我们的领域知识通过堆放额外的基础领域特征.我们根据自己的知识以及数千位在Kaggle上解决此问题的数据科学家的知识,在手动特征工程笔记本中识别并创建了许多有用的特征.我们不仅可以获得一个领域知识特征,而且可以有效获得数十甚至数百个知识特征.__在这里,我们将说明使用领域知识的选项是为了比较.但我们将坚持使用Featuretools的简单实现.__
766 |
767 |
更多信息见Featuretools[文档](https://docs.featuretools.com/guides/tuning_dfs.html)或者阅读其他文章.
768 |
769 | ### 种子特征
770 |
771 |
种子特征是我们在数据中创建的领域特征, 然后可以在Featuretools的基础上构建它们. 例如, 我们看到贷款利率是一个重要特征, 因为利率较高的贷款可能更具风险. 在Featuretools中, 我们可以将贷款利率(当前贷款和历史贷款两者)编码为种子特征, Featuretools会在可能的情况下基于此领域知识构建其他解释变量.
772 |
773 |
774 | ### 特别值
775 |
776 |
特别值与种子特征具有相似的思路, 除了它们使我们能够形成条件特征. 例如, 我们可能想为每个客户查找已关闭的历史贷款的平均金额和仍处于还款状态的历史贷款的平均金额. 通过在表`bureau`中变量`CREDIT_ACTIVE`指定有趣的值, 我们可以让Featuretools做到这一点, 手工执行此操作将非常繁琐,并且积极可能发生错误.
777 |
778 |
779 | ### 自定义基元
780 |
781 |
782 |
如果我们不满意Featuretools中可用的基元, 可以自定义函数完成数据聚合和转换. 这是Featuretools最有用之处, 因为他可以为编写很多特征操作应用于多个数据集.
783 |
784 |
785 |
__在本文中将专注于Featuretools的基本应用, 但是请注意一些功能可以用于优化数据和使用领域知识!__
786 |
787 |
788 | # 基元选择
789 |
790 |
791 |
在实践特征构建中, 我们使用一组基元并进行定义基元组, 他们将生成1800多个特征.
792 |
793 |
794 | ```python
795 | # Specify primitives
796 | agg_primitives = ["sum", "max", "min", "mean", "count", "percent_true", "num_unique", "mode"]
797 | trans_primitives = ['percentile', 'and']
798 | ```
799 |
800 |
801 | ```python
802 | # Deep feature synthesis
803 | feature_names = ft.dfs(entityset=es, target_entity='app',
804 | agg_primitives = agg_primitives,
805 | trans_primitives = trans_primitives,
806 | n_jobs = -1, verbose = 1,
807 | features_only = True,
808 | max_depth = 2)
809 | ```
810 |
811 | Built 2188 features
812 |
813 |
814 |
815 | ```python
816 | ft.save_features(feature_names, 'input/features.txt')
817 | ```
818 |
819 |
820 | 如果我们保存特征, 我们可以使用他们进行`特征矩阵计算`. 这非常有用, 当我们想要在不同数据集生成相同特征时(例如我们有特定的训练和测试集).
821 |
822 |
823 | ## 运行深度特征合成
824 |
825 |
826 |
如果我们对将要构建的特征满意, 则可以运行深度特征合成并创建特征矩阵. 以下调用运行完整的深度特征合成. 根据您的计算机,这可能需要很长时间. Featuretools确实允许并行处理, 但是每个核必须能够处理整个实体集.
827 |
828 |
829 | __该代码运行实际上是使用Dask(云服务器)完成的.Dask上代码运行时间不到2个小时, 这是一个很好的示例, 说明了我们如何使用并行处理以最有效的方式使用资源.__
830 |
831 |
832 | ```python
833 | import sys
834 | print('Total size of entityset: {:.5f} gb.'.format(sys.getsizeof(es) / 1e9))
835 | ```
836 |
837 | Total size of entityset: 11.80507 gb.
838 |
839 |
840 |
841 | ```python
842 | import psutil
843 |
844 | print('Total number of cpus detected: {}.'.format(psutil.cpu_count()))
845 | print('Total size of system memory: {:.5f} gb.'.format(psutil.virtual_memory().total / 1e9))
846 | ```
847 |
848 | Total number of cpus detected: 12.
849 | Total size of system memory: 17.17987 gb.
850 |
851 |
852 |
853 | ```python
854 | # feature_matrix, feature_names = ft.dfs(entityset=es, target_entity='app',
855 | # agg_primitives = agg_primitives,
856 | # trans_primitives = trans_primitives,
857 | # n_jobs = -1, verbose = 1, features_only = False,
858 | # max_depth = 1, chunk_size = 10)
859 | ```
860 |
861 |
862 | ```python
863 | feature_matrix.reset_index(inplace = True)
864 | feature_matrix.to_csv('input/feature_matrix.csv', index = False)
865 | ```
866 |
867 |
868 |
在这个[地址](https://www.kaggle.com/willkoehrsen/home-credit-default-risk-feature-tools)选择`feature_matrix_article.csv`下载特征矩阵.那里还有自动特征工程特征矩阵的其他几种版本.
869 |
870 |
871 | # 结论
872 |
873 |
874 |
在本文, 我们看到了如何针对数据科学问题实施自动化特征工程. __自动化特征工程为我们创建数千个特征基于一个关系表,大大提高了我们作为数据科学家的效率.__ 此外, 我们依然可以使用领域知识,甚至可以通过手动构建特征主题来加强领域知识.主要收获:
875 |
876 | * 自动化特征工程消耗1小时执行完10小时的人工特征工程.
877 |
878 | * 自动化特征工程只需要少了代码(相比于人工特征工程每个特征都要编写大量逻辑代码)即可构建数千个特征.
879 |
880 | * 总体上, 自动化特征工程性能比人工特征工程性能好(见另一个笔记).
881 |
882 |
883 |
自动化特征工程的好处是显著的,将极大地帮助我们作为一名数据科学家, 它不会减少对数据科学家的需求, 而是可以使我们更高效, 并在更短的时间内建立更好的预测管道.
884 |
--------------------------------------------------------------------------------
/output.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
324 |
--------------------------------------------------------------------------------
/output_34_0.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
324 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | bokeh>=1.0.2
2 | dask>=1.0.0
3 | distributed>=1.25.1
4 | featuretools>=0.10.1
5 | ipython>=5.8.0
6 | jupyter>=1.0.0
7 | lightgbm>=2.2.2
8 | matplotlib>=2.2.3
9 | psutil>=5.4.8
10 | pyarrow>=0.11.1
11 | scikit-learn>=0.20.2
12 | seaborn>=0.9.0
13 | tsfresh>=0.11.1
14 | umap-learn>=0.3.7
15 | graphviz>=0.10.1
16 |
--------------------------------------------------------------------------------