FRAUDE FINANCIERO SERVICIO DE DINERO MOVIL

A-INTRODUCCION

Hay una falta de conjuntos de datos disponibles públicamente sobre servicios financieros y especialmente en el dominio emergente de transacciones de dinero móvil.

Se muestra un conjunto de datos sinteticos generados a partir de una muestra de transacciones reales extraídas de un mes de registros financieros de un servicio de dinero movil. El conjunto de datos generados se realiza usando un simulador llamado PaySim como una aproximacion a tal problema.

Los registros originales fueron proporcionados por una empresa multinacional, que es el proveedor del servicio financiero móvil que actualmente se ejecuta en más de 14 países de todo el mundo.

se ejecuto PaySim varias veces utilizando semillas aleatorias para 743 pasos, que representan cada hora de un mes de tiempo real, que coincide con los registros originales.

B-OBJETIVO

Predecir las transaciones fraudulentas dentro de este conjunto de datos.

C-DESCRIPCION DE LAS VARIABLES DEL DATASET

step: mapea una unidad de tiempo en el mundo real. En este caso 1 paso es 1 hora de tiempo. Pasos totales 744 (simulación de 30 días).

type: CASH-IN, CASH-OUT, DÉBITO, PAGO y TRANSFERENCIA.

amount: monto de la transacción en moneda local.

nameOrig: cliente que inició la transacción.

oldbalanceOrg: saldo inicial antes de la transacción.

newbalanceOrig: nuevo saldo después de la transacción.

nameDest: cliente que es el destinatario de la transacción.

oldbalanceDest: destinatario del saldo inicial antes de la transacción.

newbalanceDest: destinatario del nuevo saldo después de la transacción.

isFraud: estas son las transacciones realizadas por los agentes fraudulentos dentro de la simulación. En este conjunto de datos específico, el comportamiento fraudulento de los agentes tiene como objetivo obtener ganancias tomando el control de las cuentas de los clientes e intentar vaciar los fondos transfiriéndolos a otra cuenta y luego cobrando del sistema.

isFlaggedFraud: el modelo comercial tiene como objetivo controlar las transferencias masivas de una cuenta a otra y marca los intentos ilegales. Un intento ilegal en este conjunto de datos es un intento de transferir más de 200.000 en una sola transacción.

0. CARGA DE LIBRERIAS, OPCIONES Y DATOS

#Carga de librerias:

library(tidyverse)
library(kableExtra)
library(corrplot)
library(ggpubr)
library(cowplot)
library(kableExtra)
library(tidymodels)
library(doParallel)
library(randomForest)
library(rpart)
library(xgboost)
library(embed)
library(rpart.plot)
library(rattle)
library(ROCR)
library(data.table)
library(RColorBrewer)
library(DataExplorer)
library(inspectdf)
library(ggpubr)
library(numbers)

options(scipen=999)#Desactiva la notacion cientifica

memory.limit(500000)
## [1] 500000
#Carga de datos:

dt <- fread('PS_20174392719_1491204439457_log.csv')

1. ANALISIS EXPLORATORIO PRELIMINAR

as.data.frame(sort(names(dt))) # Visualizacion nombre de variables datos
##    sort(names(dt))
## 1           amount
## 2   isFlaggedFraud
## 3          isFraud
## 4         nameDest
## 5         nameOrig
## 6   newbalanceDest
## 7   newbalanceOrig
## 8   oldbalanceDest
## 9    oldbalanceOrg
## 10            step
## 11            type
str(dt) # Visualizacion estructura datos 1
## Classes 'data.table' and 'data.frame':   6362620 obs. of  11 variables:
##  $ step          : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ type          : chr  "PAYMENT" "PAYMENT" "TRANSFER" "CASH_OUT" ...
##  $ amount        : num  9840 1864 181 181 11668 ...
##  $ nameOrig      : chr  "C1231006815" "C1666544295" "C1305486145" "C840083671" ...
##  $ oldbalanceOrg : num  170136 21249 181 181 41554 ...
##  $ newbalanceOrig: num  160296 19385 0 0 29886 ...
##  $ nameDest      : chr  "M1979787155" "M2044282225" "C553264065" "C38997010" ...
##  $ oldbalanceDest: num  0 0 0 21182 0 ...
##  $ newbalanceDest: num  0 0 0 0 0 ...
##  $ isFraud       : int  0 0 1 1 0 0 0 0 0 0 ...
##  $ isFlaggedFraud: int  0 0 0 0 0 0 0 0 0 0 ...
##  - attr(*, ".internal.selfref")=<externalptr>
glimpse(dt) # Visualizacion estructura datos 2
## Rows: 6,362,620
## Columns: 11
## $ step           <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1~
## $ type           <chr> "PAYMENT", "PAYMENT", "TRANSFER", "CASH_OUT", "PAYMENT"~
## $ amount         <dbl> 9839.64, 1864.28, 181.00, 181.00, 11668.14, 7817.71, 71~
## $ nameOrig       <chr> "C1231006815", "C1666544295", "C1305486145", "C84008367~
## $ oldbalanceOrg  <dbl> 170136.0, 21249.0, 181.0, 181.0, 41554.0, 53860.0, 1831~
## $ newbalanceOrig <dbl> 160296.36, 19384.72, 0.00, 0.00, 29885.86, 46042.29, 17~
## $ nameDest       <chr> "M1979787155", "M2044282225", "C553264065", "C38997010"~
## $ oldbalanceDest <dbl> 0, 0, 0, 21182, 0, 0, 0, 0, 0, 41898, 10845, 0, 0, 0, 0~
## $ newbalanceDest <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 4~
## $ isFraud        <int> 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0~
## $ isFlaggedFraud <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0~
#Visualizamos primeras lineas del dataframe

kable(head(dt,6), booktabs = T) %>%
  kable_styling(font_size=12)
step type amount nameOrig oldbalanceOrg newbalanceOrig nameDest oldbalanceDest newbalanceDest isFraud isFlaggedFraud
1 PAYMENT 9839.64 C1231006815 170136 160296.36 M1979787155 0 0 0 0
1 PAYMENT 1864.28 C1666544295 21249 19384.72 M2044282225 0 0 0 0
1 TRANSFER 181.00 C1305486145 181 0.00 C553264065 0 0 1 0
1 CASH_OUT 181.00 C840083671 181 0.00 C38997010 21182 0 1 0
1 PAYMENT 11668.14 C2048537720 41554 29885.86 M1230701703 0 0 0 0
1 PAYMENT 7817.71 C90045638 53860 46042.29 M573487274 0 0 0 0

Visualizamos la estructura de datos y sus dimensiones de forma mas grafica.

plot_intro(dt)

2. CALIDAD DE DATOS

2.1 Estadisticos Basicos

lapply(dt,summary)
## $step
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     1.0   156.0   239.0   243.4   335.0   743.0 
## 
## $type
##    Length     Class      Mode 
##   6362620 character character 
## 
## $amount
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##        0    13390    74872   179862   208721 92445517 
## 
## $nameOrig
##    Length     Class      Mode 
##   6362620 character character 
## 
## $oldbalanceOrg
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##        0        0    14208   833883   107315 59585040 
## 
## $newbalanceOrig
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##        0        0        0   855114   144258 49585040 
## 
## $nameDest
##    Length     Class      Mode 
##   6362620 character character 
## 
## $oldbalanceDest
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##         0         0    132706   1100702    943037 356015889 
## 
## $newbalanceDest
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##         0         0    214661   1224996   1111909 356179279 
## 
## $isFraud
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 0.000000 0.000000 0.000000 0.001291 0.000000 1.000000 
## 
## $isFlaggedFraud
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 0.0000000 0.0000000 0.0000000 0.0000025 0.0000000 1.0000000

2.2 Analisis de nulos(NA’s)

Visualizacion grafica de posibles NA’s.

plot_missing(dt)

data.frame(colSums(is.na(dt)))
##                colSums.is.na.dt..
## step                            0
## type                            0
## amount                          0
## nameOrig                        0
## oldbalanceOrg                   0
## newbalanceOrig                  0
## nameDest                        0
## oldbalanceDest                  0
## newbalanceDest                  0
## isFraud                         0
## isFlaggedFraud                  0

CONCLUSION: Corroboramos que no existen nulos en nuestros datos.

2.3 Analisis de ceros

Examen preliminar de nuestros datos con la funcion considerada, para comprobar si existe un problema de calidad en nuestros datos respecto al numero de ceros considerados en las variables.

contar_ceros <- function(variable) {
  temp <- transmute(dt,if_else(variable==0,1,0))
  sum(temp)
}

num_ceros <- sapply(dt,contar_ceros)
num_ceros <- data.frame(VARIABLE=names(num_ceros),CEROS = as.numeric(num_ceros),
                        stringsAsFactors = F) 
num_ceros <- num_ceros %>%
  arrange(desc(CEROS)) %>%
  mutate(PORCENTAJE = CEROS / nrow(dt) * 100)
num_ceros
##          VARIABLE   CEROS    PORCENTAJE
## 1  isFlaggedFraud 6362604 99.9997485313
## 2         isFraud 6354407 99.8709179552
## 3  newbalanceOrig 3609566 56.7308121497
## 4  oldbalanceDest 2704388 42.5043142605
## 5  newbalanceDest 2439433 38.3400705998
## 6   oldbalanceOrg 2102449 33.0437618465
## 7          amount      16  0.0002514687
## 8            step       0  0.0000000000
## 9            type       0  0.0000000000
## 10       nameOrig       0  0.0000000000
## 11       nameDest       0  0.0000000000

CONCLUSION: las variables referidas a fraude nos da una idea de que el dataset esta desbalanceado con un 99.99% de ceros, intentaremos balancear nuestros datos cuando vayamos a modelizar.

2.4 Analisis de Atipicos

2.4.1 Analisis Atipicos tipo numerico

out <- function(variable){
  t(t(head(sort(variable,decreasing = T),20))) 
}
lapply(dt,function(x){
  if(is.double(x)) out(x)
})
## $step
## NULL
## 
## $type
## NULL
## 
## $amount
##           [,1]
##  [1,] 92445517
##  [2,] 73823490
##  [3,] 71172480
##  [4,] 69886731
##  [5,] 69337316
##  [6,] 67500761
##  [7,] 66761272
##  [8,] 64234448
##  [9,] 63847993
## [10,] 63294840
## [11,] 62785417
## [12,] 61733762
## [13,] 60965276
## [14,] 60642003
## [15,] 60154456
## [16,] 59579503
## [17,] 58944753
## [18,] 58318373
## [19,] 57787801
## [20,] 57436619
## 
## $nameOrig
## NULL
## 
## $oldbalanceOrg
##           [,1]
##  [1,] 59585040
##  [2,] 57316255
##  [3,] 50399045
##  [4,] 49585040
##  [5,] 47316255
##  [6,] 45674548
##  [7,] 44892193
##  [8,] 43818855
##  [9,] 43686616
## [10,] 42542664
## [11,] 41699144
## [12,] 41690843
## [13,] 40399045
## [14,] 39585040
## [15,] 38939424
## [16,] 38563401
## [17,] 38441832
## [18,] 38364748
## [19,] 38259597
## [20,] 38166700
## 
## $newbalanceOrig
##           [,1]
##  [1,] 49585040
##  [2,] 47316255
##  [3,] 43686616
##  [4,] 43673802
##  [5,] 41690843
##  [6,] 41432359
##  [7,] 40399045
##  [8,] 39585040
##  [9,] 38946233
## [10,] 38939424
## [11,] 38563401
## [12,] 38441832
## [13,] 38364748
## [14,] 38259597
## [15,] 38166700
## [16,] 37950093
## [17,] 37919816
## [18,] 37538005
## [19,] 37316255
## [20,] 37297463
## 
## $nameDest
## NULL
## 
## $oldbalanceDest
##            [,1]
##  [1,] 356015889
##  [2,] 355553416
##  [3,] 355381434
##  [4,] 355380484
##  [5,] 355185537
##  [6,] 328194465
##  [7,] 327998074
##  [8,] 327963024
##  [9,] 327852121
## [10,] 327827763
## [11,] 324915061
## [12,] 321589798
## [13,] 311492903
## [14,] 311404901
## [15,] 302560856
## [16,] 302394178
## [17,] 302275362
## [18,] 301140972
## [19,] 300985928
## [20,] 291667716
## 
## $newbalanceDest
##            [,1]
##  [1,] 356179279
##  [2,] 356015889
##  [3,] 355553416
##  [4,] 355553416
##  [5,] 355381434
##  [6,] 355380484
##  [7,] 355185537
##  [8,] 328431698
##  [9,] 328194465
## [10,] 327998074
## [11,] 327963024
## [12,] 327852121
## [13,] 327827763
## [14,] 321589798
## [15,] 311492903
## [16,] 311404901
## [17,] 311404901
## [18,] 311404901
## [19,] 302560856
## [20,] 302394178
## 
## $isFraud
## NULL
## 
## $isFlaggedFraud
## NULL

CONCLUSION: No apreciamos nada relevante en lo referente a los atipicos numericos.

2.4.2 Analisis Atipicos tipo integer

out <- function(variable){
  t(t(table(variable))) 
}
lapply(dt,function(x){
  if(is.integer(x)) out(x)
})
## $step
##         
## variable [,1] 
##      1    2708
##      2    1014
##      3     552
##      4     565
##      5     665
##      6    1660
##      7    6837
##      8   21097
##      9   37628
##      10  35991
##      11  37241
##      12  36153
##      13  37515
##      14  41485
##      15  44609
##      16  42471
##      17  43361
##      18  49579
##      19  51352
##      20  40625
##      21  19152
##      22  12635
##      23   6144
##      24   3216
##      25   1598
##      26    440
##      27     41
##      28      4
##      29      4
##      30      8
##      31     12
##      32     12
##      33  23616
##      34  30904
##      35  29157
##      36  39774
##      37  34000
##      38  31453
##      39  23391
##      40  34270
##      41  36348
##      42  41304
##      43  45060
##      44  38523
##      45  18500
##      46  12445
##      47   8681
##      48   5693
##      49    764
##      50      6
##      51     14
##      52      8
##      53     10
##      54      4
##      55     12
##      56     18
##      57      6
##      58     20
##      59     20
##      60     12
##      61     14
##      62      8
##      63      8
##      64     18
##      65     20
##      66     24
##      67      6
##      68     18
##      69     20
##      70      6
##      71     20
##      72     14
##      73      8
##      74     22
##      75      8
##      76      4
##      77     10
##      78     10
##      79     10
##      80     10
##      81     10
##      82     10
##      83     12
##      84     16
##      85     14
##      86     18
##      87      6
##      88      8
##      89      6
##      90     16
##      91      8
##      92     10
##      93   4444
##      94  10372
##      95   6858
##      96   6350
##      97    685
##      98     14
##      99      8
##      100     6
##      101    12
##      102    12
##      103     8
##      104    14
##      105    18
##      106    18
##      107    10
##      108     8
##      109    12
##      110    10
##      111    10
##      112     2
##      113     6
##      114     4
##      115    10
##      116    14
##      117    12
##      118    12
##      119  5740
##      120  3144
##      121  1074
##      122   414
##      123    52
##      124     4
##      125    10
##      126     6
##      127    14
##      128     8
##      129 21986
##      130 32890
##      131 36375
##      132 35936
##      133 35147
##      134 32391
##      135 27556
##      136 30340
##      137 32559
##      138 31422
##      139 46054
##      140 37012
##      141 16434
##      142 12189
##      143  7609
##      144  3523
##      145  1117
##      146   522
##      147    84
##      148    12
##      149    22
##      150    14
##      151    18
##      152     6
##      153 19475
##      154 29241
##      155 29927
##      156 27603
##      157 29764
##      158 27839
##      159 27561
##      160 27765
##      161 33049
##      162 40622
##      163 46352
##      164 39102
##      165 16542
##      166 12176
##      167  7946
##      168  3824
##      169  1464
##      170   529
##      171    14
##      172    10
##      173    14
##      174    10
##      175     8
##      176    10
##      177 17336
##      178 28204
##      179 30534
##      180 31481
##      181 31749
##      182 31900
##      183 31199
##      184 32175
##      185 37323
##      186 43747
##      187 49083
##      188 39079
##      189 17674
##      190 12669
##      191  9111
##      192  4314
##      193  1511
##      194   671
##      195   141
##      196     8
##      197     4
##      198    12
##      199    10
##      200     8
##      201 17963
##      202 31949
##      203 36176
##      204 36856
##      205 33549
##      206 29777
##      207 28288
##      208 29058
##      209 30372
##      210 33004
##      211 36889
##      212 34047
##      213 14775
##      214 10746
##      215  7975
##      216  4130
##      217  3347
##      218    12
##      219    10
##      220     6
##      221    10
##      222     8
##      223     8
##      224    10
##      225 18366
##      226 27522
##      227 30871
##      228 30965
##      229 28304
##      230 26034
##      231 26968
##      232 27482
##      233 14703
##      234 34053
##      235 47491
##      236 37109
##      237 16003
##      238 10920
##      239  8554
##      240  4189
##      241  1865
##      242   637
##      243     8
##      244    12
##      245     4
##      246     4
##      247     8
##      248    10
##      249 23209
##      250 31854
##      251 35269
##      252 35590
##      253 33311
##      254 29553
##      255 28840
##      256 28570
##      257 26843
##      258 27662
##      259 43328
##      260 33674
##      261 14420
##      262 11125
##      263  8021
##      264  4042
##      265  1581
##      266   767
##      267    77
##      268     6
##      269    14
##      270    16
##      271    12
##      272    12
##      273  7871
##      274 19202
##      275 24797
##      276 26383
##      277 25925
##      278 25737
##      279 25925
##      280 25956
##      281 27778
##      282 31118
##      283 36644
##      284 33449
##      285 14266
##      286 11333
##      287  6889
##      288  4018
##      289  1437
##      290   487
##      291    44
##      292     8
##      293     4
##      294    18
##      295    10
##      296    22
##      297 20609
##      298 29579
##      299 31940
##      300 31176
##      301 30881
##      302 29127
##      303 24714
##      304 26697
##      305 34595
##      306 43615
##      307 46968
##      308 36983
##      309 15147
##      310 11393
##      311  9863
##      312  3266
##      313  1305
##      314   512
##      315    12
##      316    12
##      317    20
##      318     6
##      319     8
##      320    14
##      321 17768
##      322 27147
##      323 29884
##      324 29430
##      325 27901
##      326 26655
##      327 25183
##      328 26877
##      329 32161
##      330 36682
##      331 40186
##      332 36156
##      333 14553
##      334 12016
##      335 10456
##      336  2932
##      337   320
##      338   529
##      339    10
##      340    10
##      341    10
##      342    14
##      343    12
##      344     6
##      345 14670
##      346 26073
##      347 30222
##      348 29467
##      349 29552
##      350 28245
##      351 26927
##      352 27139
##      353 30420
##      354 40696
##      355 44787
##      356 33528
##      357 15096
##      358 12050
##      359  7991
##      360  3508
##      361  1441
##      362   504
##      363    14
##      364     6
##      365    14
##      366    10
##      367    14
##      368     6
##      369 14490
##      370 25429
##      371 28388
##      372 29515
##      373 28853
##      374 27727
##      375 26103
##      376 20122
##      377 18183
##      378 37127
##      379 41759
##      380 37693
##      381 14961
##      382 11829
##      383  7818
##      384  3588
##      385  1419
##      386   633
##      387    28
##      388    18
##      389    10
##      390    12
##      391    12
##      392    10
##      393 19269
##      394 28959
##      395 22797
##      396 34075
##      397 30348
##      398 26656
##      399 30223
##      400 30910
##      401 33468
##      402 40218
##      403 45155
##      404 36726
##      405 16604
##      406 12023
##      407  7937
##      408  8256
##      409   483
##      410    20
##      411    16
##      412     8
##      413   289
##      414    16
##      415    16
##      416  2182
##      417     6
##      418     6
##      419  1257
##      420  2620
##      421    14
##      422  1259
##      423   766
##      424    14
##      425    28
##      426  5139
##      427    14
##      428  2792
##      429  2357
##      430     8
##      431  1685
##      432     4
##      433     6
##      434     8
##      435     4
##      436   248
##      437    16
##      438    12
##      439   883
##      440    18
##      441    10
##      442  1222
##      443    10
##      444    20
##      445    64
##      446    12
##      447    10
##      448  2548
##      449  1194
##      450    16
##      451  3751
##      452   308
##      453    59
##      454    39
##      455   824
##      456    18
##      457    10
##      458   438
##      459     8
##      460    59
##      461    83
##      462    16
##      463    10
##      464    16
##      465    12
##      466   984
##      467    12
##      468    12
##      469  6016
##      470    12
##      471  2620
##      472    84
##      473    10
##      474  2728
##      475    14
##      476  3755
##      477    10
##      478  1908
##      479   904
##      480     6
##      481    14
##      482    16
##      483   317
##      484     6
##      485    12
##      486     8
##      487   912
##      488     8
##      489    14
##      490  3820
##      491    12
##      492  5119
##      493  1819
##      494     8
##      495  3256
##      496   873
##      497    20
##      498    12
##      499    10
##      500  6108
##      501    28
##      502   313
##      503  1878
##      504    10
##      505   936
##      506     8
##      507    14
##      508     6
##      509   288
##      510    10
##      511     6
##      512    12
##      513    10
##      514  8733
##      515     6
##      516    14
##      517  1391
##      518  3488
##      519    12
##      520  3037
##      521    14
##      522 18631
##      523    30
##      524  2664
##      525  8023
##      526  2470
##      527  2697
##      528   937
##      529    33
##      530   409
##      531   250
##      532    95
##      533    12
##      534   531
##      535     6
##      536    12
##      537    18
##      538  3120
##      539     4
##      540  5476
##      541    12
##      542  4475
##      543     6
##      544  9358
##      545  3562
##      546  7771
##      547  2261
##      548  5689
##      549   642
##      550  4804
##      551   949
##      552  1517
##      553     8
##      554    18
##      555   144
##      556     6
##      557    20
##      558    14
##      559    10
##      560    10
##      561    16
##      562    10
##      563  3284
##      564  3546
##      565  5425
##      566  1421
##      567  4356
##      568    14
##      569  1182
##      570  1965
##      571  4256
##      572    18
##      573  3487
##      574  2614
##      575     8
##      576   877
##      577   364
##      578   269
##      579    12
##      580    58
##      581    10
##      582   775
##      583    16
##      584   281
##      585  2047
##      586  1581
##      587  2740
##      588  4894
##      589  6245
##      590  6584
##      591    57
##      592  5373
##      593     4
##      594  6536
##      595  5434
##      596  9319
##      597  2486
##      598   934
##      599  1816
##      600    18
##      601   865
##      602     8
##      603    12
##      604     8
##      605    16
##      606    14
##      607     8
##      608  1082
##      609     8
##      610  1199
##      611    14
##      612  2677
##      613    45
##      614  1278
##      615     8
##      616     6
##      617  2680
##      618     8
##      619  2800
##      620     8
##      621  1121
##      622     4
##      623     6
##      624    10
##      625    24
##      626     6
##      627     4
##      628     6
##      629    20
##      630   150
##      631    12
##      632     8
##      633     8
##      634    10
##      635    16
##      636    12
##      637  3390
##      638    14
##      639    14
##      640  1398
##      641    10
##      642  1141
##      643    14
##      644  2267
##      645    10
##      646    20
##      647    10
##      648    14
##      649   263
##      650    16
##      651    10
##      652    14
##      653     8
##      654     8
##      655     4
##      656  1965
##      657    10
##      658    14
##      659  2500
##      660    12
##      661  4877
##      662     2
##      663    10
##      664  1125
##      665    10
##      666     6
##      667  1400
##      668     8
##      669   322
##      670  1906
##      671    18
##      672   153
##      673   445
##      674    14
##      675    30
##      676     8
##      677     8
##      678     6
##      679    72
##      680    22
##      681  4703
##      682     6
##      683  2192
##      684  4562
##      685    14
##      686  4689
##      687  8040
##      688  7885
##      689    14
##      690  4972
##      691  4809
##      692  6898
##      693     4
##      694  2664
##      695  2827
##      696     6
##      697    12
##      698    65
##      699    55
##      700     6
##      701    18
##      702    14
##      703     8
##      704    16
##      705  2362
##      706     4
##      707   309
##      708     4
##      709  2313
##      710  1788
##      711     8
##      712    23
##      713     8
##      714   681
##      715  1879
##      716   140
##      717   632
##      718   918
##      719    14
##      720    10
##      721     4
##      722    10
##      723    14
##      724    14
##      725     4
##      726    22
##      727    12
##      728    10
##      729     4
##      730    28
##      731    16
##      732    10
##      733    10
##      734     8
##      735    12
##      736    14
##      737    10
##      738    10
##      739    10
##      740     6
##      741    22
##      742    14
##      743     8
## 
## $type
## NULL
## 
## $amount
## NULL
## 
## $nameOrig
## NULL
## 
## $oldbalanceOrg
## NULL
## 
## $newbalanceOrig
## NULL
## 
## $nameDest
## NULL
## 
## $oldbalanceDest
## NULL
## 
## $newbalanceDest
## NULL
## 
## $isFraud
##         
## variable [,1]   
##        0 6354407
##        1    8213
## 
## $isFlaggedFraud
##         
## variable [,1]   
##        0 6362604
##        1      16

CONCLUSION: Nada a destacar.

2.5 Analisis de Coherencia

Nada a destacar fuera de lo normal.

2.6 Comprobacion de correlacion de variables continuas.

corr_cont <- dt%>%select(c("step", "amount", "oldbalanceOrg", "newbalanceOrig", 
                           "oldbalanceDest", "newbalanceDest"))
dat.cor <- cor(corr_cont, method = "pearson")

corrplot(dat.cor, method = "shade",
         shade.col = NA, tl.col = "black",
         tl.srt = 90,
         addCoef.col = "black", addcolorlabel = "no",
         order = "AOE")

CONCLUSION: Vemos que tienen una alta correlacion las variables newbalance y oldbalance tanto Org/Orig y Dest entre ellas repectivamente, lo solucionaremos posteriormente eliminando aquellas de las que podamos prescindir de cara al modelo de regresion logistica a posteriori a utilizar.

3. ANALISIS SECUNDARIO Y ELIMINACION DE VARIABLES SOBRANTES MODELIZACION

Vamos a analizar la penetracion de la target(fraude) en la variable ‘isFlaggedFraud’ que representa los intentos de fraude por encima(1) o por debajo(0) de 200000.

dtn <- dt
dtn$isFraud <- as.factor(dtn$isFraud)
dtn$isFlaggedFraud <- as.factor(dtn$isFlaggedFraud)

pen_fraud <- dtn%>%
  group_by(isFlaggedFraud,isFraud)%>%
  summarise(Count= n())
                
ggbarplot(pen_fraud, x = "isFlaggedFraud", y = "Count",
    fill = "isFraud", color = "isFraud", palette = "Paired",label=T,lab.pos = "out",
    lab.size=3.5, lab.col = "black")+theme(legend.position="bottom")

CONCLUSION: Podemos decir que todas las transacciones por encima de 200.000 son sistemáticamente un fraude y también que los estafadores realizan transacciones por debajo de 200.000.

Ahora vamos a hacer una analisis a nivel horario de las horas mas frecuentes en que se comete el fraude a partir de nuestros datos.

Cada paso(step) representa 1 hora del mundo real y hay un total de 743 pasos para 30 días de datos. Vamos a convertirlos en 24 horas donde cada día tiene de 1 a 24 horas y el patrón se repite de nuevo.

dtn$hour <- mod(dtn$step,24)

p <- ggdensity(dtn,x="hour", color="isFraud", fill="isFraud", palette=c("darkorchid1","aquamarine"),title="FRAUDE POR UNIDAD HORARIA EN EL MUNDO REAL",ggtheme=theme_gray())

p + scale_x_continuous(
  breaks = get_breaks(by = 2, from = 0),
  limits =  c(0, 24))

CONCLUSION: Entre las (0-9)horas aprox. apenas hay transacciones, pero practicamente todas son fraudulentas, a partir las 9h aprox. hay muchas mas transacciones, destacando en mayor proporcion como fraudulentas las que se dan entre las (21-24)horas aprox.

Vamos a analizar la variable por tipo de transaccion realizada en la que veremos la penetracion de la target (fraude) en cada tipo efectuado.

fra_tr <- dt %>% 
      group_by(type) %>% 
      summarise(fr_tran = sum(isFraud))

ggbarplot(fra_tr, x = "type", y = "fr_tran",
          fill = "type", color = "type", palette = "jco", sort.val="desc",
          sort.by.groups = FALSE)+geom_text(aes(label=fr_tran),vjust=-0.5)+
          ggtitle('TRANSACCIONES FRAUDULENTAS POR TIPO')+labs(x='TIPO DE 
          TRANSACCION', y='NUM_TRANSACCIONES_FRAUDULENTAS')

CONCLUSION: Como se puede ver del tipo de transacciones existentes los unicos 2 tipos que contienen transacciones fraudulentas son: ‘CASH_OUT’ y ‘TRANSFER’. con lo cual con posterioridad filtraremos nuestros datos solo con estos tipos de transacciones.

Parece ser tambien que los nombres de origen y destino tienen un denominador comun con respecto al prefijo de esos mismos nombres, que parecen ser catalogados como los prefijos ‘C’ y ‘M’ en cada comienzo de cada nombre.

dt2 <- dt %>% mutate(ori_name_prefx = str_sub(nameOrig,1,1), 
                    dest_name_prefx = str_sub(nameDest, 1, 1)) 

kable(head(dt2,6), booktabs = T) %>%
  kable_styling(font_size=12)
step type amount nameOrig oldbalanceOrg newbalanceOrig nameDest oldbalanceDest newbalanceDest isFraud isFlaggedFraud ori_name_prefx dest_name_prefx
1 PAYMENT 9839.64 C1231006815 170136 160296.36 M1979787155 0 0 0 0 C M
1 PAYMENT 1864.28 C1666544295 21249 19384.72 M2044282225 0 0 0 0 C M
1 TRANSFER 181.00 C1305486145 181 0.00 C553264065 0 0 1 0 C C
1 CASH_OUT 181.00 C840083671 181 0.00 C38997010 21182 0 1 0 C C
1 PAYMENT 11668.14 C2048537720 41554 29885.86 M1230701703 0 0 0 0 C M
1 PAYMENT 7817.71 C90045638 53860 46042.29 M573487274 0 0 0 0 C M
dt2%>%select(c(ori_name_prefx,dest_name_prefx))%>%table()%>%print()
##               dest_name_prefx
## ori_name_prefx       C       M
##              C 4211125 2151495

Se puede ver que los nombres en origen tienen como prefijo la letra C y en los de destino tienen como prefijo ambas la C y la M.

Vamos a ver si esto tiene alguna relacion con nuestra variable objetivo de alguna forma o correspondencia.

Convertimos para ello a factores la variable objetivo y nuestras variables prefijo.

dt2$isFraud <- factor(dt2$isFraud)                

dt2$ori_name_prefx <- factor(dt2$ori_name_prefx)

dt2$dest_name_prefx <- factor(dt2$dest_name_prefx)


 ori <- dt2%>%
  group_by(ori_name_prefx,isFraud)%>%
  summarise(Count= n())
 

 p1 <- ggbarplot(ori, x = "ori_name_prefx", y = "Count",
                fill = "isFraud", color = "isFraud", palette = "Paired",label=TRUE,
                lab.pos = "out",lab.size=3.5, lab.col = "black")+
                theme(legend.position="none")

 
 dest <- dt2%>%
   group_by(dest_name_prefx,isFraud)%>%
   summarise(Count= n())
 
 p2 <- ggbarplot(dest, x = "dest_name_prefx", y = "Count",
           fill = "isFraud", color = "isFraud", palette = "Paired",label=TRUE,
           lab.pos = "out",lab.size=3.5, lab.col = "black")+
           theme(legend.position="none")
 
 
ggarrange(p1,p2, 
          labels = "AUTO",
          common.legend = T, 
          legend = "bottom", 
          align = "hv", 
          ncol = 2)

CONCLUSION: Como vemos los nombres con los prefijos en M no tienen relacion alguna con casos de fraude, vamos ahora a descartar estas observaciones que nunca estan asociadas a fraude ya que son innecesarias para nuestros propositos en la consecucion de los modelos a realizar posteriormente.

dt2 <- dt2%>%filter(type==c('CASH_OUT','TRANSFER'))

dt2 <- dt2%>%filter(dest_name_prefx=='C')

Vamos ahora a visualizar nuestros datos antes y finalmente despues de la limpieza de estas observaciones tomando como referencia los distintos niveles de nuestra variable objetivo.

dt$isFraud <- factor(dt$isFraud)

levels(dt$isFraud) <- c("No_Fraud","Fraud")

dtd <- dt2

levels(dtd$isFraud) <- c("No_Fraud","Fraud")


fraude_antes <- dt%>%
  group_by(isFraud)%>%
  summarise(Count= n())


p3 <- ggbarplot(fraude_antes, x = "isFraud", y = "Count",
                fill = "isFraud", color = "isFraud", palette = "Paired",label=TRUE,lab.pos = "out",lab.size=3.5, lab.col = "black")+theme(legend.position="none")


fraude_despues <- dtd%>%
  group_by(isFraud)%>%
  summarise(Count= n())

p4 <-ggbarplot(fraude_despues, x = "isFraud", y = "Count",
               fill = "isFraud", color = "isFraud", palette = "Paired",label=TRUE,lab.pos = "out",lab.size=3.5, lab.col = "black")+theme(legend.position="none")

ggarrange(p3,p4, 
          labels = "AUTO",
          common.legend = T, 
          legend = "bottom", 
          align = "hv", 
          ncol = 2)

Vemos la variable objetivo en la figura ‘A’ del dataset en funcion del numero de observaciones antes de la limpieza de las mismas y en la figura ‘B’ despues de la limpieza y vemos una reduccion significativa en las observaciones resultantes, no obstante el problema principal sigue siendo que las clases estan muy desbalanceadas,lo cual habra que solucionar con posterioridad para el modelaje.

Vamos ahora a eliminar variables que consideramos con poco impacto en la prediccion del modelo y a guardar las variables que consideremos para incoporar al analisis y al modelo final hallado. Asi pues:

dt3 <- dt2%>%select(nameOrig,nameDest)

dt4 <- dt2%>%select(-step,-nameOrig,-nameDest,-isFlaggedFraud,-ori_name_prefx,
       -dest_name_prefx)

# Cambiamos tambien a factor la variable type

dt4$type <- as.factor(dt4$type)

4. MODELIZACION

FUNCIONES QUE VAMOS A APLICAR A LOS MODELOS:

Funcion para crear una matriz de confusion

confusion<-function(real,scoring,umbral){ 
  conf<-table(real,scoring>=umbral)
  if(ncol(conf)==2) return(conf) else return(NULL)
}

Funcion para calcular las metricas de los modelos: acierto, precision, cobertura y F1

metricas<-function(matriz_conf){
  acierto <- (matriz_conf[1,1] + matriz_conf[2,2]) / sum(matriz_conf) *100
  precision <- matriz_conf[2,2] / (matriz_conf[2,2] + matriz_conf[1,2]) *100
  cobertura <- matriz_conf[2,2] / (matriz_conf[2,2] + matriz_conf[2,1]) *100
  F1 <- 2*precision*cobertura/(precision+cobertura)
  salida<-c(acierto,precision,cobertura,F1)
  return(salida)
}

Funcion para probar distintos umbrales y ver el efecto sobre precision y cobertura

umbrales<-function(real,scoring){
  umbrales<-data.frame(umbral=rep(0,times=19),acierto=rep(0,times=19),
  precision=rep(0,times=19),cobertura=rep(0,times=19),F1=rep(0,times=19))
  cont <- 1
  for (cada in seq(0.05,0.95,by = 0.05)){
    datos<-metricas(confusion(real,scoring,cada))
    registro<-c(cada,datos)
    umbrales[cont,]<-registro
    cont <- cont + 1
  }
  return(umbrales)
}

Funciones que calculan la curva ROC y el AUC

roc<-function(prediction){
  r<-performance(prediction,'tpr','fpr')
  plot(r)
}

auc<-function(prediction){
  a<-performance(prediction,'auc')
  return(a@y.values[[1]])
}

DIVISION DE LOS DATOS ENTRENAMIENTO Y PRUEBA

Creamos las particiones de entrenamiento y prueba, 70% y 30% respectivamente.

Vamos a aplicar un cross-validation a todos los modelos de vfold=10

4.1 Regresion Logistica

set.seed(45)
split_inicial <- initial_split(
  data   = dt4,
  prop   = 0.7,
  strata = isFraud
)

train <- training(split_inicial)
test  <- testing(split_inicial)


# Balanceamos datos y preprocesamos:


# Hacemos preprocesamiento de datos

mod_rcp <- recipe(isFraud~., data=train)%>%
  step_corr(all_numeric_predictors(),threshold = 0.8)%>%
  step_discretize_xgb(all_numeric(), outcome = "isFraud")%>%
  themis::step_downsample(isFraud)

downsp <- mod_rcp%>%
  prep()%>%
  juice()

# hacemos ahora el modelo con los datos balanceados


mod_rl <- logistic_reg(mode = 'classification') %>%
  set_engine(engine = "glm",family=binomial(link='logit'))

mod_dwn <- recipe(isFraud~.,data=downsp)


set.seed(45)
cv_folds <- vfold_cv(
  data    = downsp,
  v       = 10,
  strata  = isFraud
)


wf_m <- workflow()%>%
  add_recipe(mod_dwn)%>%
  add_model(mod_rl)


my_metrics <- metric_set(accuracy,roc_auc,kap)
set.seed(45)
rlog <- wf_m%>%
  fit_resamples(
    resamples    = cv_folds,
    metrics      = my_metrics,
    control      = control_resamples(save_pred = TRUE)
  )
collect_metrics(rlog)
## # A tibble: 3 x 6
##   .metric  .estimator  mean     n std_err .config             
##   <chr>    <chr>      <dbl> <int>   <dbl> <chr>               
## 1 accuracy binary     0.944    10 0.00294 Preprocessor1_Model1
## 2 kap      binary     0.889    10 0.00588 Preprocessor1_Model1
## 3 roc_auc  binary     0.982    10 0.00144 Preprocessor1_Model1
show_best(rlog, metric = "roc_auc")
## # A tibble: 1 x 6
##   .metric .estimator  mean     n std_err .config             
##   <chr>   <chr>      <dbl> <int>   <dbl> <chr>               
## 1 roc_auc binary     0.982    10 0.00144 Preprocessor1_Model1
best_rlog2 <- rlog%>%
  select_best("roc_auc")

wf_final <- 
  wf_m%>%
  finalize_workflow(best_rlog2)


set.seed(45)
final_model_rl <- fit(object = wf_final, data = train)

model_extr1 <- final_model_rl%>%extract_fit_engine()


summary(model_extr1)
## 
## Call:
## stats::glm(formula = ..y ~ ., family = ~binomial(link = "logit"), 
##     data = data)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -5.0462  -0.0467  -0.0181  -0.0046   8.4904  
## 
## Coefficients:
##                     Estimate    Std. Error z value            Pr(>|z|)    
## (Intercept)    -5.7031377169  0.0406966268 -140.14 <0.0000000000000002 ***
## typeTRANSFER    1.7992980995  0.0528839738   34.02 <0.0000000000000002 ***
## amount         -0.0000190255  0.0000006229  -30.55 <0.0000000000000002 ***
## oldbalanceOrg   0.0000262188  0.0000006183   42.40 <0.0000000000000002 ***
## newbalanceOrig -0.0000281307  0.0000006292  -44.71 <0.0000000000000002 ***
## oldbalanceDest -0.0000003700  0.0000000239  -15.48 <0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 38559  on 969753  degrees of freedom
## Residual deviance: 16351  on 969748  degrees of freedom
## AIC: 16363
## 
## Number of Fisher Scoring iterations: 14

Como vemos todas las variables tienen al menos algun elemento altamente significativo(***), por lo tanto consideramos estas variables altamente predictivas y continuamos con nuestro proceso.

La Prediccion del scoring del modelo (conjunto test) sera:

rl_predict <- predict(model_extr1,test,type = "response")
head(rl_predict,20)
##                    4                    5                   13 
## 0.000708984929020051 0.003365156558490609 0.000016078819871626 
##                   14                   16                   18 
## 0.000000236123729351 0.015566069939317723 0.001252460310565901 
##                   21                   24                   28 
## 0.002420627806890194 0.001277093821118475 0.000624287896275398 
##                   31                   32                   33 
## 0.000221191256711430 0.001164488984029582 0.000016317041568318 
##                   34                   40                   61 
## 0.000076444646274630 0.002260365206929941 0.000878236801888946 
##                   66                   67                   73 
## 0.000459258967177029 0.000049883138795761 0.000015241523103905 
##                   77                   84 
## 0.000000972128235837 0.000000000005481085

Veamos graficamente la pinta que tiene:

plot(rl_predict~test$isFraud)

Calculamos ahora el pseudo R cuadrado, visualizando con anterioridad los coeficientes y significancia de acuerdo con lo que hemos decidido escoger para nuestro modelo:

psdo_R <- 1-(model_extr1$deviance/model_extr1$null.deviance)

psdo_R
## [1] 0.5759434

El valor que nos da es muy bueno, este valor nos indica que con las variables que tenemos somos capaces de explicar el 57% de los motivos por los que una transaccion, es fraudulenta, y hay aprox. un 43% que no somos capaces de explicar.

Con la funcion umbrales probamos diferentes cortes y para este caso concreto de descubrimiento de fraude vamos a intentar aumentar la cobertura, pero sin que disminuya mucho la precision, puesto que para un caso de fraude asumimos que pueda haber algo mas de errores de deteccion en pos de que haya mas cobertura para asegurar que se corte la transaccion ante la sospecha para proteger el dinero del cliente.

umb_rl<-umbrales(test$isFraud,rl_predict)
umb_rl
##    umbral  acierto precision cobertura       F1
## 1    0.05 99.63596  41.39458  62.22962 49.71751
## 2    0.10 99.75506  58.12721  54.74210 56.38389
## 3    0.15 99.78369  66.06575  51.83028 58.08858
## 4    0.20 99.79644  71.39423  49.41764 58.40708
## 5    0.25 99.80583  76.02108  48.00333 58.84753
## 6    0.30 99.81112  79.57447  46.67221 58.83587
## 7    0.35 99.81281  81.64179  45.50749 58.44017
## 8    0.40 99.81401  83.25581  44.67554 58.14835
## 9    0.45 99.81497  84.75120  43.92679 57.86301
## 10   0.50 99.81666  87.41497  42.76206 57.43017
## 11   0.55 99.81666  88.59649  42.01331 56.99774
## 12   0.60 99.81641  89.83666  41.18136 56.47461
## 13   0.65 99.81690  91.36961  40.51581 56.13833
## 14   0.70 99.81714  92.50000  40.01664 55.86527
## 15   0.75 99.81521  93.22709  38.93511 54.92958
## 16   0.80 99.81473  94.62810  38.10316 54.32977
## 17   0.85 99.81208  95.07495  36.93844 53.20551
## 18   0.90 99.81016  95.99109  35.85691 52.21078
## 19   0.95 99.80631  96.92671  34.10982 50.46154

Seleccionamos el umbral en funcion de lo anteriormente comentado.

umbral_final_rl<-0.25
umbral_final_rl
## [1] 0.25

Hallamos la matriz de confusion del umbral optimizado.

confusion(test$isFraud,rl_predict,0.25)
##     
## real  FALSE   TRUE
##    0 414226    182
##    1    625    577
rl_metricas<-filter(umb_rl,umbral==umbral_final_rl)
rl_metricas
##   umbral  acierto precision cobertura       F1
## 1   0.25 99.80583  76.02108  48.00333 58.84753

Evaluamos la ROC

#creamos el objeto prediction
rl_prediction<-prediction(rl_predict,test$isFraud)
#visualizamos la ROC
roc(rl_prediction)

Sacamos las metricas definitivas incluyendo el AUC

rl_metricas<-cbind(rl_metricas,AUC=round(auc(rl_prediction),2)*100)
print(t(rl_metricas))
##               [,1]
## umbral     0.25000
## acierto   99.80583
## precision 76.02108
## cobertura 48.00333
## F1        58.84753
## AUC       98.00000

4.2 Arbol de Decision.

set.seed(45)
split_inicial <- initial_split(
  data   = dt4,
  prop   = 0.7,
  strata = isFraud
)
train <- training(split_inicial)
test  <- testing(split_inicial)

# Balanceamos datos y preprocesamos:


# Hacemos preprocesamiento de datos


mod_rcp <- recipe(isFraud~.,
                  data = train)%>%
  step_discretize_xgb(all_numeric(), outcome = "isFraud")%>%
  themis::step_downsample(isFraud)

downsp <- mod_rcp%>%
  prep()%>%
  juice() 


# hacemos ahora el modelo con los datos balanceados

mod_rp <- decision_tree(
  mode = 'classification',
  cost_complexity=0.00001,
  tree_depth = tune(),
  min_n = tune()
)%>%
  set_engine(engine = "rpart",parms = list(split="information"))


mod_dwn <- recipe(isFraud~.,data=downsp)


set.seed(45)
cv_folds <- vfold_cv(
  data = downsp,
  v = 10,
  strata = isFraud
)

wf_rp <- workflow()%>%
  add_recipe(mod_dwn)%>%
  add_model(mod_rp)
registerDoParallel(cores = parallel::detectCores() - 1)

set.seed(45)
rp_fit <- wf_rp%>%tune_bayes(
  resamples = cv_folds,
  initial = 10,
  iter = 20,
  metrics = metric_set(roc_auc),
  control = control_bayes(save_pred=TRUE,no_improve = 10, verbose = FALSE))

stopImplicitCluster()
show_best(rp_fit, metric = "roc_auc")
## # A tibble: 5 x 9
##   tree_depth min_n .metric .estimator  mean     n std_err .config .iter
##        <int> <int> <chr>   <chr>      <dbl> <int>   <dbl> <chr>   <int>
## 1          8     2 roc_auc binary     0.989    10 0.00108 Iter4       4
## 2          8     3 roc_auc binary     0.989    10 0.00108 Iter9       9
## 3          8     4 roc_auc binary     0.989    10 0.00108 Iter10     10
## 4          9     2 roc_auc binary     0.989    10 0.00118 Iter8       8
## 5          9     3 roc_auc binary     0.989    10 0.00121 Iter13     13
mejor_hiper1 <- select_best(rp_fit, metric="roc_auc")
mejor_hiper1
## # A tibble: 1 x 3
##   tree_depth min_n .config
##        <int> <int> <chr>  
## 1          8     2 Iter4
wf_finalrp <- 
  wf_rp%>%
  finalize_workflow(mejor_hiper1)


set.seed(45)
final_model_rp <- fit(object = wf_finalrp, data = train)

model_ext <- final_model_rp%>%
  extract_fit_engine()

Visualizamos el arbol del modelo

fancyRpartPlot(model_ext)

Ahora revisamos la tabla donde podemos ver en primer termino el parametro de complejidad, error relativo,error absoluto, y desviacion estandar.

printcp(model_ext)
## 
## Classification tree:
## rpart::rpart(formula = ..y ~ ., data = data, parms = ~list(split = "information"), 
##     cp = ~0.00001, maxdepth = ~8, minsplit = min_rows(2, data))
## 
## Variables actually used in tree construction:
## [1] amount         newbalanceDest newbalanceOrig oldbalanceDest oldbalanceOrg 
## [6] type          
## 
## Root node error: 2819/969754 = 0.0029069
## 
## n= 969754 
## 
##            CP nsplit rel error  xerror     xstd
## 1  0.17949628      0   1.00000 1.00000 0.018807
## 2  0.12593118      2   0.64101 0.64101 0.015065
## 3  0.07981554      3   0.51508 0.51543 0.013512
## 4  0.01064207      5   0.35545 0.35722 0.011251
## 5  0.00709471     10   0.30223 0.30188 0.010344
## 6  0.00407946     11   0.29514 0.29869 0.010289
## 7  0.00106421     13   0.28698 0.29124 0.010160
## 8  0.00059123     15   0.28485 0.29266 0.010185
## 9  0.00053210     19   0.28237 0.29124 0.010160
## 10 0.00035474     21   0.28131 0.29088 0.010154
## 11 0.00017737     24   0.28024 0.28911 0.010123
## 12 0.00011825     40   0.27740 0.29372 0.010203
## 13 0.00001000     43   0.27705 0.29443 0.010215

En ella nos fijamos en el parametro de complejidad desde el que tomamos de partida 0.00001 abajo del todo hasta arriba de esa relacion, tenemos que mirar en la columna xerror(error absoluto), que empieza a bajar desde arriba, En el
momento que encontramos un numero donde empieza a cambiar la tendencia (a aumentar) ese seria, el parametro de complejidad que tenemos que tomar
correspondiente a ese valor, y por ahi vamos a podar el arbol y ejecutar otra vez el modelo.

En este caso el parametro de complejidad que corresponderia a lo dicho anteriormente parece que minimiza aproximadamente en 0.0001.

Tambien lo podemos interpretar viendo la representacion grafica (ejes: y- xerror x-cp) como vemos a continuacion.

plotcp(model_ext)

Cambiamos el parametro de complejidad de partida a 0.0001 e incluimos en el arbol que no tenga mas de 5 niveles.

set.seed(45)
split_inicial <- initial_split(
  data   = dt4,
  prop   = 0.7,
  strata = isFraud
)
train <- training(split_inicial)
test  <- testing(split_inicial)

# Balanceamos datos y preprocesamos:


# Hacemos preprocesamiento de datos


mod_rcp <- recipe(isFraud~.,
                  data = train)%>%
  step_discretize_xgb(all_numeric(), outcome = "isFraud")%>%
  themis::step_downsample(isFraud)

downsp <- mod_rcp%>%
  prep()%>%
  juice() 


# hacemos ahora el modelo con los datos balanceados


mod_rp <- decision_tree(
  mode = 'classification',
  cost_complexity=0.0001,
  tree_depth = 5,
  min_n = tune()
)%>%
  set_engine(engine = "rpart",parms = list(split="information"))


mod_dwn <- recipe(isFraud~.,data=downsp)


set.seed(45)
cv_folds <- vfold_cv(
  data = downsp,
  v = 10,
  strata = isFraud
)

wf_rp <- workflow()%>%
  add_recipe(mod_dwn)%>%
  add_model(mod_rp)
registerDoParallel(cores = parallel::detectCores() - 1)

set.seed(45)
rp_fit <- wf_rp%>%tune_bayes(
  resamples = cv_folds,
  initial = 10,
  iter = 20,
  metrics = metric_set(roc_auc),
  control = control_bayes(save_pred=TRUE,no_improve = 10, verbose = FALSE))

stopImplicitCluster()
show_best(rp_fit, metric = "roc_auc")
## # A tibble: 5 x 8
##   min_n .metric .estimator  mean     n std_err .config               .iter
##   <int> <chr>   <chr>      <dbl> <int>   <dbl> <chr>                 <int>
## 1     3 roc_auc binary     0.978    10 0.00175 Iter1                     1
## 2     2 roc_auc binary     0.978    10 0.00175 Iter2                     2
## 3     4 roc_auc binary     0.978    10 0.00175 Iter3                     3
## 4    10 roc_auc binary     0.977    10 0.00174 Preprocessor1_Model02     0
## 5     6 roc_auc binary     0.977    10 0.00174 Preprocessor1_Model09     0
mejor_hiper2 <- select_best(rp_fit, metric="roc_auc")
mejor_hiper2
## # A tibble: 1 x 2
##   min_n .config
##   <int> <chr>  
## 1     3 Iter1
wf_finalrp <- 
  wf_rp%>%
  finalize_workflow(mejor_hiper2)


set.seed(45)
final_model_rp <- fit(object = wf_finalrp, data = train)

model_extr2 <- final_model_rp%>%
  extract_fit_engine()

Visualizamos tabla complejidad-errores

printcp(model_extr2)
## 
## Classification tree:
## rpart::rpart(formula = ..y ~ ., data = data, parms = ~list(split = "information"), 
##     cp = ~0.0001, maxdepth = ~5, minsplit = min_rows(3, data))
## 
## Variables actually used in tree construction:
## [1] amount         newbalanceDest newbalanceOrig oldbalanceDest oldbalanceOrg 
## [6] type          
## 
## Root node error: 2819/969754 = 0.0029069
## 
## n= 969754 
## 
##            CP nsplit rel error  xerror     xstd
## 1  0.17949628      0   1.00000 1.00000 0.018807
## 2  0.12593118      2   0.64101 0.64101 0.015065
## 3  0.07981554      3   0.51508 0.51543 0.013512
## 4  0.00709471      5   0.35545 0.35722 0.011251
## 5  0.00407946      6   0.34835 0.34729 0.011094
## 6  0.00106421      8   0.34019 0.33984 0.010974
## 7  0.00053210      9   0.33913 0.33877 0.010957
## 8  0.00035474     11   0.33806 0.33913 0.010963
## 9  0.00017737     12   0.33771 0.33842 0.010951
## 10 0.00010000     16   0.33700 0.33948 0.010968

Podemos ver una clara tendencia descendente de xerror(error absoluto) hasta llegar al parametro de complejidad 0.001 de partida y tanto su error relativo como el absoluto son bastane proximos en dicho parametro y en los demas.

Por tanto consideramos como buena dicha aproximacion.

Visualizamos grafica error validacion cruzada y complejidad.

plotcp(model_extr2)

Visualizamos el arbol del modelo

fancyRpartPlot(model_extr2)

Reglas que podrian ser utilizadas para hacer implantacion del arbol. (y es Churn–la variable objetivo(dependiente) en las reglas del arbol)

rpart.rules(model_extr2, style = 'tall', cover = T,roundint=FALSE)
## ..y is 0.00 with cover 0% when
##     newbalanceDest >=      0.87
##     oldbalanceOrg >= 1010777
##     newbalanceOrig >= 449225
## 
## ..y is 0.00 with cover 0% when
##     newbalanceDest >=      0.87
##     oldbalanceOrg >= 1010777
##     newbalanceOrig is   318 to 439766
## 
## ..y is 0.00 with cover 0% when
##     newbalanceDest >= 1133242.50
##     type is TRANSFER
##     oldbalanceOrg >= 1010777
##     newbalanceOrig <   318
## 
## ..y is 0.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is CASH_OUT
##     amount <  179
##     oldbalanceDest >= 4574
## 
## ..y is 0.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is TRANSFER
##     oldbalanceDest >=   4
## 
## ..y is 0.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is TRANSFER
##     oldbalanceOrg <  50457
##     amount >= 53631
##     oldbalanceDest <   4
## 
## ..y is 0.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is TRANSFER
##     oldbalanceOrg >=  50457
##     amount < 50548
##     oldbalanceDest <   4
## 
## ..y is 0.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is CASH_OUT
##     amount >= 2810
## 
## ..y is 0.00 with cover 99% when
##     newbalanceDest >=      0.87
##     oldbalanceOrg < 1010777
## 
## ..y is 0.01 with cover 0% when
##     newbalanceDest <     0.87
##     type is CASH_OUT
##     amount is  179 to 2753
## 
## ..y is 0.97 with cover 0% when
##     newbalanceDest <     0.87
##     type is TRANSFER
##     oldbalanceOrg <  50457
##     amount < 53631
##     oldbalanceDest <   4
## 
## ..y is 1.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is TRANSFER
##     oldbalanceOrg >=  50457
##     amount >= 50548
##     oldbalanceDest <   4
## 
## ..y is 1.00 with cover 0% when
##     newbalanceDest >=      0.87
##     oldbalanceOrg >= 1010777
##     newbalanceOrig is 439766 to 449225
## 
## ..y is 1.00 with cover 0% when
##     newbalanceDest is     0.87 to 1133242.50
##     type is TRANSFER
##     oldbalanceOrg >= 1010777
##     newbalanceOrig <   318
## 
## ..y is 1.00 with cover 0% when
##     newbalanceDest >=      0.87
##     type is CASH_OUT
##     oldbalanceOrg >= 1010777
##     newbalanceOrig <   318
## 
## ..y is 1.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is CASH_OUT
##     amount is 2753 to 2810
## 
## ..y is 1.00 with cover 0% when
##     newbalanceDest <     0.87
##     type is CASH_OUT
##     amount <  179
##     oldbalanceDest < 4574

Vemos el numero de nodos final por cliente

rp_numnod <- rpart.predict(model_extr2,test,nn=T)

head(rp_numnod)
##            0            1 nn
## 4  0.9990275 0.0009725271  4
## 5  0.9994805 0.0005194805 24
## 13 0.9990275 0.0009725271  4
## 14 0.9990275 0.0009725271  4
## 16 1.0000000 0.0000000000 14
## 18 0.9990275 0.0009725271  4

Calculo de prediccion del modelo (scorings)

rp_predict <- predict(model_extr2, test, type = 'prob')[,2]

head(rp_predict,20)
##            4            5           13           14           16           18 
## 0.0009725271 0.0005194805 0.0009725271 0.0009725271 0.0000000000 0.0009725271 
##           21           24           28           31           32           33 
## 0.0009725271 0.0009725271 0.0009725271 0.0009725271 0.0005194805 0.0009725271 
##           34           40           61           66           67           73 
## 0.0005194805 0.0009725271 0.0009725271 0.0009725271 0.0009725271 0.0009725271 
##           77           84 
## 0.0009725271 0.0009725271

Veamos graficamente como se ve:

plot(rp_predict~test$isFraud)

Buscamos un umbral que maximice la coberturra sin disminuir demasiado la precision

umb_rp<-umbrales(test$isFraud,rp_predict)
umb_rp
##    umbral  acierto precision cobertura       F1
## 1    0.05 99.90159  98.53121  66.97171 79.74245
## 2    0.10 99.90159  98.53121  66.97171 79.74245
## 3    0.15 99.90159  98.53121  66.97171 79.74245
## 4    0.20 99.90159  98.53121  66.97171 79.74245
## 5    0.25 99.90159  98.53121  66.97171 79.74245
## 6    0.30 99.90159  98.53121  66.97171 79.74245
## 7    0.35 99.90159  98.53121  66.97171 79.74245
## 8    0.40 99.90159  98.53121  66.97171 79.74245
## 9    0.45 99.90159  98.53121  66.97171 79.74245
## 10   0.50 99.90159  98.53121  66.97171 79.74245
## 11   0.55 99.90159  98.53121  66.97171 79.74245
## 12   0.60 99.90159  98.53121  66.97171 79.74245
## 13   0.65 99.90159  98.53121  66.97171 79.74245
## 14   0.70 99.90159  98.53121  66.97171 79.74245
## 15   0.75 99.90159  98.53121  66.97171 79.74245
## 16   0.80 99.90159  98.53121  66.97171 79.74245
## 17   0.85 99.90159  98.53121  66.97171 79.74245
## 18   0.90 99.90159  98.53121  66.97171 79.74245
## 19   0.95 99.90159  98.53121  66.97171 79.74245

Lo hallamos directamente con la siguiente sintaxis

umbral_final_rp<-0.05
umbral_final_rp
## [1] 0.05

Hallamos la matriz de confusion del umbral optimizado

confusion(test$isFraud,rp_predict,0.05)
##     
## real  FALSE   TRUE
##    0 414396     12
##    1    397    805
rp_metricas<-filter(umb_rp,umbral==umbral_final_rp)
rp_metricas
##   umbral  acierto precision cobertura       F1
## 1   0.05 99.90159  98.53121  66.97171 79.74245

Evaluamos la ROC

#creamos el objeto prediction
rp_prediction<-prediction(rp_predict,test$isFraud)
#visualizamos la ROC
roc(rp_prediction)

Sacamos las metricas definitivas incluyendo el AUC

rp_metricas<-cbind(rp_metricas,AUC=round(auc(rp_prediction),2)*100)
print(t(rp_metricas))
##               [,1]
## umbral     0.05000
## acierto   99.90159
## precision 98.53121
## cobertura 66.97171
## F1        79.74245
## AUC       83.00000

4.3 Random Forest

set.seed(45)
split_inicial <- initial_split(
  data   = dt4,
  prop   = 0.7,
  strata = isFraud
)
train <- training(split_inicial)
test  <- testing(split_inicial)

# Balanceamos datos y preprocesamos:


# Hacemos preprocesamiento de datos


mod_rcp <- recipe(isFraud~.,
                  data = train)%>%
  step_discretize_xgb(all_numeric(), outcome = "isFraud")%>%
  themis::step_downsample(isFraud)

downsp <- mod_rcp%>%
  prep()%>%
  juice() 


# hacemos ahora el modelo con los datos balanceados


mod_rf <- rand_forest(
  mode  = "classification",
  mtry  = tune(),
  trees = 50,
  min_n = tune()
) %>%
  set_engine(engine = "randomForest",importance=T)


mod_dwn <- recipe(isFraud~.,data=downsp)


set.seed(45)
cv_folds <- vfold_cv(
  data = downsp,
  v = 10,
  strata = isFraud)


wf_rf <- workflow()%>%
  add_recipe(mod_dwn)%>%
  add_model(mod_rf)


hiperpar_grid=grid_max_entropy(
  # Rango de busqueda para cada hiperparametro
  mtry(range = c(1L, 10L), trans = NULL),
  min_n(range = c(2L, 100L), trans = NULL),
  # Numero de combinaciones totales
  size = 100
)
registerDoParallel(cores = parallel::detectCores() - 1)

set.seed(45)
rf_fit <- wf_rf%>%tune_grid(
  resamples = cv_folds,
  metrics = metric_set(roc_auc),
  control = control_resamples(save_pred = TRUE),
  grid = hiperpar_grid)

stopImplicitCluster()
show_best(rf_fit, metric = "roc_auc")
## # A tibble: 5 x 8
##    mtry min_n .metric .estimator  mean     n std_err .config              
##   <int> <int> <chr>   <chr>      <dbl> <int>   <dbl> <chr>                
## 1     2    37 roc_auc binary     0.988    10 0.00152 Preprocessor1_Model81
## 2     2    56 roc_auc binary     0.988    10 0.00129 Preprocessor1_Model70
## 3     2    29 roc_auc binary     0.988    10 0.00152 Preprocessor1_Model36
## 4     2    17 roc_auc binary     0.988    10 0.00163 Preprocessor1_Model18
## 5     2    88 roc_auc binary     0.987    10 0.00156 Preprocessor1_Model53
mejor_hiper3 <- select_best(rf_fit, metric="roc_auc")
mejor_hiper3
## # A tibble: 1 x 3
##    mtry min_n .config              
##   <int> <int> <chr>                
## 1     2    37 Preprocessor1_Model81
wf_finalrf <- 
  wf_rf%>%
  finalize_workflow(mejor_hiper3)


set.seed(45)
final_model_rf <- fit(object = wf_finalrf, data = train)

model_xtr <- final_model_rf%>%
  extract_fit_engine()

Visualizamos las variables de nuestros datos mas importantes en un grafico

varImpPlot(model_xtr)

Como hay 2 criterios, creamos una unica variable agregada para tener una mejor idea de la importancia de cada variable.

importancia <- randomForest::importance(model_xtr)[,3:4]
#normalizamos para poner las dos variables en la misma escala. los valores 
#negativos son las que menos predicen y los positivos las que mas
importancia_norm <- as.data.frame(scale(importancia))
#creamos una unica variable como suma de las otras
importancia_norm <- importancia_norm %>% mutate(
  Variable = rownames(importancia_norm),
  Imp_tot = MeanDecreaseAccuracy + MeanDecreaseGini) %>%
  mutate(Imp_tot = Imp_tot + abs(min(Imp_tot))) %>% 
  arrange(desc(Imp_tot)) %>% 
  select(Variable,Imp_tot,MeanDecreaseAccuracy,MeanDecreaseGini)
#hacemos un grafico para ver la curva de caida de importancia
ggplot(importancia_norm, aes(reorder(Variable,-Imp_tot),Imp_tot)) + 
geom_bar(stat = "identity") + theme(axis.text.x = element_text(angle = 90,size = 7))

importancia_norm
##                      Variable   Imp_tot MeanDecreaseAccuracy MeanDecreaseGini
## oldbalanceOrg   oldbalanceOrg 4.1562794            1.0929973       1.53600151
## newbalanceOrig newbalanceOrig 1.5850338            1.2630649      -1.20531164
## newbalanceDest newbalanceDest 1.5086951           -0.7711384       0.75255295
## amount                 amount 1.4615422            0.1827522      -0.24849057
## type                     type 0.4521328           -0.9829132      -0.09223457
## oldbalanceDest oldbalanceDest 0.0000000           -0.7847629      -0.74251767

Vemos el grafico y nuestros datos en la tabla de forma decreciente segun nivel de importancia, y vemos donde puede estar un corte claro, diferencia de una con respecto a la anterior, y vemos que la variable ‘oldbalanceDest’ tiene la mayor diferencia con respecto al resto en cuanto a importancia(0),

Vamos a deseleccionar de nuestros datos esta variable sobrante por debajo del corte de ‘type’ y nos quedaremos con esta y las de la parte superior.

dt6 <- dt4%>%select(-oldbalanceDest)

y ahora vamos a realizar de nuevo el modelo con las nuevas variables

set.seed(45)
split_inicial <- initial_split(
  data   = dt6,
  prop   = 0.7,
  strata = isFraud
)
train <- training(split_inicial)
test  <- testing(split_inicial)


# Balanceamos datos y preprocesamos:


# Hacemos preprocesamiento de datos


mod_rcp <- recipe(isFraud~.,
                  data = train)%>%
  step_discretize_xgb(all_numeric(), outcome = "isFraud")%>%
  themis::step_downsample(isFraud)

downsp <- mod_rcp%>%
  prep()%>%
  juice() 


# hacemos ahora el modelo con los datos balanceados

mod_rf <- rand_forest(
  mode  = "classification",
  mtry  = tune(),
  trees = 50,
  min_n = tune()
) %>%
  set_engine(engine = "randomForest",importance=T)


mod_dwn <- recipe(isFraud~.,data=downsp)


set.seed(45)
cv_folds <- vfold_cv(
  data = downsp,
  v = 10,
  strata = isFraud)

wf_rf <- workflow()%>%
  add_recipe(mod_dwn)%>%
  add_model(mod_rf)

hiperpar_grid=grid_max_entropy(
  # Rango de busqueda para cada hiperparametro
  mtry(range = c(1L, 10L), trans = NULL),
  min_n(range = c(2L, 100L), trans = NULL),
  # Numero de combinaciones totales
  size = 100
)
registerDoParallel(cores = parallel::detectCores() - 1)


set.seed(45)
rf_fit <- wf_rf%>%tune_grid(
  resamples = cv_folds,
  metrics = metric_set(roc_auc),
  control = control_resamples(save_pred = TRUE),
  grid = hiperpar_grid)

stopImplicitCluster()
show_best(rf_fit, metric = "roc_auc")
## # A tibble: 5 x 8
##    mtry min_n .metric .estimator  mean     n std_err .config              
##   <int> <int> <chr>   <chr>      <dbl> <int>   <dbl> <chr>                
## 1     2    37 roc_auc binary     0.987    10 0.00157 Preprocessor1_Model81
## 2     2    88 roc_auc binary     0.987    10 0.00159 Preprocessor1_Model53
## 3     2    36 roc_auc binary     0.987    10 0.00180 Preprocessor1_Model46
## 4     2    51 roc_auc binary     0.987    10 0.00180 Preprocessor1_Model90
## 5     2    85 roc_auc binary     0.986    10 0.00195 Preprocessor1_Model29
mejor_hiper4 <- select_best(rf_fit, metric="roc_auc")
mejor_hiper4
## # A tibble: 1 x 3
##    mtry min_n .config              
##   <int> <int> <chr>                
## 1     2    37 Preprocessor1_Model81
wf_finalrf <- 
  wf_rf%>%
  finalize_workflow(mejor_hiper4)


set.seed(45)
final_model_rf <- fit(object = wf_finalrf, data = train)

model_extr3 <- final_model_rf%>%
  extract_fit_engine()

Calculo de prediccion del modelo (scorings)

rf_predict <- predict(model_extr3, test, type = 'prob')[,2]

head(rf_predict,20)
##   4   5  13  14  16  18  21  24  28  31  32  33  34  40  61  66  67  73  77  84 
## 0.0 0.0 0.0 0.0 0.3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

Veamos graficamente como se ve:

plot(rf_predict~test$isFraud)

Buscamos un umbral que maximice la coberturra sin disminuir demasiado la precision

umb_rf<-umbrales(test$isFraud,rf_predict)
umb_rf
##    umbral  acierto precision cobertura       F1
## 1    0.05 99.82099  63.77858  88.18636 74.02235
## 2    0.10 99.86911  74.58894  83.02829 78.58268
## 3    0.15 99.89028  82.04467  79.45092 80.72697
## 4    0.20 99.89870  86.05725  77.53744 81.57549
## 5    0.25 99.90616  90.84507  75.12479 82.24044
## 6    0.30 99.90712  92.32365  74.04326 82.17913
## 7    0.35 99.91458  96.08270  73.46090 83.26261
## 8    0.40 99.91555  96.70692  73.29451 83.38855
## 9    0.45 99.91627  97.55011  72.87854 83.42857
## 10   0.50 99.91651  97.76536  72.79534 83.45255
## 11   0.55 99.91555  97.97069  72.29617 83.19770
## 12   0.60 99.91458  98.40000  71.63062 82.90804
## 13   0.65 99.91458  98.51088  71.54742 82.89157
## 14   0.70 99.91482  99.07407  71.21464 82.86544
## 15   0.75 99.91290  99.29577  70.38270 82.37585
## 16   0.80 99.91121  99.64243  69.55075 81.92063
## 17   0.85 99.90520  99.63145  67.47088 80.45635
## 18   0.90 99.87007  99.84940  55.15807 71.06109
## 19   0.95 99.85154 100.00000  48.66889 65.47286

Lo hallamos directamente con la siguiente sintaxis

umbral_final_rf<-0.25
umbral_final_rf
## [1] 0.25

Hallamos la matriz de confusion del umbral optimizado

confusion(test$isFraud,rf_predict,0.25)
##     
## real  FALSE   TRUE
##    0 414317     91
##    1    299    903
rf_metricas<-filter(umb_rf,umbral==umbral_final_rf)
rf_metricas
##   umbral  acierto precision cobertura       F1
## 1   0.25 99.90616  90.84507  75.12479 82.24044

Evaluamos la ROC

#creamos el objeto prediction
rf_prediction<-prediction(rf_predict,test$isFraud)
#visualizamos la ROC
roc(rf_prediction)

Sacamos las metricas definitivas incluyendo el AUC

rf_metricas<-cbind(rf_metricas,AUC=round(auc(rf_prediction),2)*100)
print(t(rf_metricas))
##               [,1]
## umbral     0.25000
## acierto   99.90616
## precision 90.84507
## cobertura 75.12479
## F1        82.24044
## AUC       97.00000

4.4 XGBOOST (Extreme Gradient Boosting)

set.seed(45)
split_inicial <- initial_split(
  data   = dt4,
  prop   = 0.7,
  strata = isFraud
)
train <- training(split_inicial)
test  <- testing(split_inicial)


# Balanceamos datos y preprocesamos:


# Hacemos preprocesamiento de datos


mod_rcp <- recipe(isFraud~.,
                  data = train)%>%
  step_discretize_xgb(all_numeric(), outcome = "isFraud")%>%
  step_dummy(all_nominal(), -all_outcomes())%>%
  themis::step_downsample(isFraud)

downsp <- mod_rcp%>%
  prep()%>%
  juice() 


# hacemos ahora el modelo con los datos balanceados

mod_xg <- boost_tree(mtry = tune(),
                     min_n = tune(),
                     tree_depth = tune(),
                     trees = 50,
                     learn_rate = 0.1,
                     sample_size = 0.8) %>% 
  set_engine("xgboost") %>% 
  set_mode("classification")


mod_dwn <- recipe(isFraud~.,data=downsp)


set.seed(45)
cv_folds <- vfold_cv(
  data = downsp,
  v = 10,
  strata = isFraud)


wf_xg<- workflow()%>%
  add_recipe(mod_dwn)%>%
  add_model(mod_xg)


# Parametros para tuning
params_xgb <- parameters(
  finalize(mtry(), x = train[, -1]),
  min_n(range = c(2L, 50L)),
  tree_depth(range = c(3L, 8L))
)

# Grid
set.seed(45)
grid_xgb <- params_xgb %>% 
  grid_max_entropy(size = 10)
registerDoParallel(cores = parallel::detectCores() - 1)

set.seed(2631)
xg_fit <- wf_xg%>%tune_grid(
  resamples = cv_folds,
  metrics = metric_set(roc_auc),
  grid = grid_xgb,
  control = control_grid(save_pred = TRUE))


stopImplicitCluster()
show_best(xg_fit, metric = "roc_auc")
## # A tibble: 5 x 9
##    mtry min_n tree_depth .metric .estimator  mean     n std_err .config         
##   <int> <int>      <int> <chr>   <chr>      <dbl> <int>   <dbl> <chr>           
## 1     5    12          6 roc_auc binary     0.983    10 0.00109 Preprocessor1_M~
## 2     4    19          8 roc_auc binary     0.979    10 0.00117 Preprocessor1_M~
## 3     5    11          3 roc_auc binary     0.974    10 0.00114 Preprocessor1_M~
## 4     2    12          5 roc_auc binary     0.969    10 0.00155 Preprocessor1_M~
## 5     5    31          5 roc_auc binary     0.968    10 0.00141 Preprocessor1_M~
mejor_hiper5 <- select_best(xg_fit, metric="roc_auc")
mejor_hiper5
## # A tibble: 1 x 4
##    mtry min_n tree_depth .config              
##   <int> <int>      <int> <chr>                
## 1     5    12          6 Preprocessor1_Model03
wf_finalxg <- 
  wf_rf%>%
  finalize_workflow(mejor_hiper5)


set.seed(45)
final_model_xg <- fit(object = wf_finalxg, data = train)

model_extr4 <- final_model_xg%>%
  extract_fit_engine()

Calculo de prediccion del modelo (scorings)

xg_predict <- predict(model_extr4, test, type = 'prob')[,2]

head(xg_predict,20)
##  4  5 13 14 16 18 21 24 28 31 32 33 34 40 61 66 67 73 77 84 
##  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

Veamos graficamente como se ve:

plot(xg_predict~test$isFraud)

Buscamos un umbral que maximice la coberturra sin disminuir demasiado la precision

umb_xg<-umbrales(test$isFraud,xg_predict)
umb_xg
##    umbral  acierto precision cobertura       F1
## 1    0.05 99.83807  64.73538  96.67221 77.54421
## 2    0.10 99.87873  71.89460  95.34110 81.97425
## 3    0.15 99.90352  77.71626  93.42762 84.85077
## 4    0.20 99.91121  80.11569  92.17970 85.72534
## 5    0.25 99.91747  82.86151  90.09983 86.32921
## 6    0.30 99.92156  84.65190  89.01830 86.78021
## 7    0.35 99.92637  87.08609  87.52080 87.30290
## 8    0.40 99.92902  88.66155  86.52246 87.57895
## 9    0.45 99.93022  90.28269  85.02496 87.57498
## 10   0.50 99.93143  91.64396  83.94343 87.62484
## 11   0.55 99.93287  93.99428  82.02995 87.60551
## 12   0.60 99.92998  95.23337  79.78369 86.82662
## 13   0.65 99.92806  96.02446  78.36938 86.30325
## 14   0.70 99.92565  97.14889  76.53910 85.62122
## 15   0.75 99.92637  98.17204  75.95674 85.64728
## 16   0.80 99.92373  98.57300  74.70882 84.99763
## 17   0.85 99.91964  98.98420  72.96173 84.00383
## 18   0.90 99.91506  99.41793  71.04825 82.87239
## 19   0.95 99.90905  99.63855  68.80200 81.39764

Lo hallamos directamente con la siguiente sintaxis

umbral_final_xg <- 0.45
umbral_final_xg
## [1] 0.45

Hallamos la matriz de confusion del umbral optimizado

confusion(test$isFraud,xg_predict,0.45)
##     
## real  FALSE   TRUE
##    0 414298    110
##    1    180   1022
xg_metricas<-filter(umb_xg,umbral==umbral_final_xg)
xg_metricas
##   umbral  acierto precision cobertura       F1
## 1   0.45 99.93022  90.28269  85.02496 87.57498

Evaluamos la ROC

#creamos el objeto prediction
xg_prediction<-prediction(xg_predict,test$isFraud)
#visualizamos la ROC
roc(xg_prediction)

Sacamos las metricas definitivas incluyendo el AUC

xg_metricas<-cbind(xg_metricas,AUC=round(auc(xg_prediction),2)*100)
print(t(xg_metricas))
##               [,1]
## umbral     0.45000
## acierto   99.93022
## precision 90.28269
## cobertura 85.02496
## F1        87.57498
## AUC       99.00000

4.5 Comparativa de metricas y curvas ROC

4.5.1 Comparativa de metricas

comparativa <- rbind(rl_metricas,rp_metricas,rf_metricas,xg_metricas)
rownames(comparativa) <- c('Regresion Logistica', 'Arbol Decision', 
                           'Random Forest', 'Extreme Gradiant Boosting')
                           
t(comparativa)
##           Regresion Logistica Arbol Decision Random Forest
## umbral                0.25000        0.05000       0.25000
## acierto              99.80583       99.90159      99.90616
## precision            76.02108       98.53121      90.84507
## cobertura            48.00333       66.97171      75.12479
## F1                   58.84753       79.74245      82.24044
## AUC                  98.00000       83.00000      97.00000
##           Extreme Gradiant Boosting
## umbral                      0.45000
## acierto                    99.93022
## precision                  90.28269
## cobertura                  85.02496
## F1                         87.57498
## AUC                        99.00000

4.5.2 Comparativas de Curvas ROC de los modelos

rl_auc <- rlog%>%collect_predictions(summarize=TRUE,parameters=best_rlog2)%>%
  roc_curve(isFraud,.pred_0)%>%
  mutate(model = "Regresion Logistica")

rp_auc <- rp_fit%>%collect_predictions(summarize=TRUE,parameters=mejor_hiper2)%>%
  roc_curve(isFraud,.pred_0)%>%
  mutate(model = "Arbol de Decision") 

rf_auc <- rf_fit%>%collect_predictions(summarize=TRUE,parameters=mejor_hiper4)%>%
  roc_curve(isFraud,.pred_0)%>%
  mutate(model = "Random Forest") 

xg_auc <- xg_fit%>%collect_predictions(summarize=TRUE,parameters=mejor_hiper5)%>%
  roc_curve(isFraud,.pred_0)%>%
  mutate(model = "Xgboost")

bind_rows(rl_auc,rp_auc,rf_auc,xg_auc)%>% # Dibuja las 4 curvas AUC juntas
  ggplot(aes(x=1-specificity, y = sensitivity, col = model))+ # Especifica el eje x e y dibuja la columna para usar el nombre de la metrica
  geom_path(lwd = 1.5, alpha = 0.8) + # Conecta a las 4 AUC, lwd = anchura de linea, alpha = Color transparencia del valor.
  geom_abline(lty = 3) + # abline del dibujo, lty= linea tipo
  coord_equal() + # Asegura que los rangos de los ejes sean iguales
  scale_color_viridis_d(option = "plasma", end = .6)

En base a la comparativa el modelo de Extreme Gradient Boosting ha obtenido mejores resultados, asi que seleccionamos este modelo ganador en la comparativa.

4.6 Guardado del modelo seleccionado.

Añadimos la variable scoring a nuestro dataframe en base a la prediccion del modelo seleccionado y guardamos el modelo final.

dt4$SCORING_FRAUD <- predict(model_extr4, dt4, type = 'prob')[,2]
saveRDS(model_extr4,'modelo_final.rds')

Visualizamos tabla añadiendo variables de nombres al modelo descrito

dt5 <- cbind(dt3,dt4)

kable(head(dt5,6), booktabs = T) %>%
  kable_styling(font_size=12)
nameOrig nameDest type amount oldbalanceOrg newbalanceOrig oldbalanceDest newbalanceDest isFraud SCORING_FRAUD
C1670993182 C1100439041 TRANSFER 215310.30 705.00 0 22425 0.00 0 0
C768216420 C1509514333 CASH_OUT 110414.71 26845.41 0 288800 2415.16 0 0
C512549200 C248609774 CASH_OUT 5346.89 0.00 0 652637 6453430.91 0 0
C1528834618 C476800120 CASH_OUT 82940.31 3017.87 0 132372 49864.36 0 0
C527211736 C2096057945 CASH_OUT 47458.86 209534.84 162076 52120 0.00 0 0
C1718906711 C977993101 CASH_OUT 94253.33 25203.05 0 99773 965870.05 0 0