Modelo predictivo que muestra la probabilidad de que un CLIENTE retire el 70% o más de su saldo en los siguientes 3 meses¶

-Primer paso Se realiza la lectura de los saldos y se busca estandarizar la información para un manejo más sencillo de los datos. Realizamos una agrupación a nivel ID del cliente para saber su saldo, qué contratos y que productos tiene activos, incluyendo el historial de su saldo mensual

In [1]:
import pyarrow.parquet as pq
import pandas as pd
import re
import numpy as np
from sklearn.linear_model import LinearRegression
import math

saldos = pq.read_table('0saldos.parquet').to_pandas()
saldos = saldos.loc[saldos['TipoDocum'] == 'C ']
saldos.info()
<class 'pandas.core.frame.DataFrame'>
Index: 197217 entries, 0 to 201519
Data columns (total 26 columns):
 #   Column        Non-Null Count   Dtype  
---  ------        --------------   -----  
 0   TipoDocum     197217 non-null  object 
 1   SALDO_202101  197217 non-null  float64
 2   SALDO_2021O2  197217 non-null  float64
 3   SALDO_2021O3  197217 non-null  float64
 4   SALDO_2021O4  197217 non-null  float64
 5   SALDO_2021O5  197217 non-null  float64
 6   SALDO_2021O6  197217 non-null  float64
 7   SALDO_2021O7  197217 non-null  float64
 8   SALDO_2021O8  197217 non-null  float64
 9   SALDO_2021O9  197217 non-null  float64
 10  SALDO_202110  197217 non-null  float64
 11  SALDO_202111  197217 non-null  float64
 12  SALDO_202112  197217 non-null  float64
 13  SALDO_202201  197217 non-null  float64
 14  SALDO_202202  197217 non-null  float64
 15  SALDO_202203  197217 non-null  float64
 16  SALDO_202204  197217 non-null  float64
 17  SALDO_202205  197217 non-null  float64
 18  SALDO_202206  197217 non-null  float64
 19  SALDO_202207  197217 non-null  float64
 20  SALDO_202208  197217 non-null  float64
 21  SALDO_202209  197217 non-null  float64
 22  SALDO_202210  197217 non-null  float64
 23  Contrato      197217 non-null  int64  
 24  PlanProducto  197217 non-null  int64  
 25  NroDocum      197217 non-null  int64  
dtypes: float64(22), int64(3), object(1)
memory usage: 40.6+ MB

Detección de valores NaN

In [1]:
saldos.isna().sum()
Out[1]:
TipoDocum       0
SALDO_202101    0
SALDO_2021O2    0
SALDO_2021O3    0
SALDO_2021O4    0
SALDO_2021O5    0
SALDO_2021O6    0
SALDO_2021O7    0
SALDO_2021O8    0
SALDO_2021O9    0
SALDO_202110    0
SALDO_202111    0
SALDO_202112    0
SALDO_202201    0
SALDO_202202    0
SALDO_202203    0
SALDO_202204    0
SALDO_202205    0
SALDO_202206    0
SALDO_202207    0
SALDO_202208    0
SALDO_202209    0
SALDO_202210    0
Contrato        0
PlanProducto    0
NroDocum        0
dtype: int64
In [1]:
# Agrupamos por MultiIndex los datos por cliente
saldos.set_index(['NroDocum', 'Contrato', 'PlanProducto'], inplace=True)
regex = re.compile(r'^SALDO')
columnas_a_descartar = [columna for columna in saldos.columns if regex.match(columna)]
In [1]:
def perdidas(matrix):
    # Compare the last element with all other elements in the row (excluding the last one)
    results = [  np.max(matrix[i][matrix[i] > 0][-4:] ) - np.max(matrix[i][matrix[i] > 0][-1] ) for i in range(matrix.shape[0])]
    
    # Convert the list of results to a column vector
    result_array = np.array(results).reshape(-1, 1)
    return result_array

saldos['SALDO_202209'] = (saldos['SALDO_202208'] + saldos['SALDO_202210']) / 2
# Filtrar las columnas que coinciden con el regex
# Sumar las columnas

saldos['total'] = saldos.filter(regex=regex).sum(axis=1)
saldos['mediaSaldo'] = saldos.filter(regex=regex).mean(axis=1)

saldos = saldos.loc[saldos['total'] > 0]

saldos_array = []
for i in saldos.filter(regex=regex).columns:
    if len(saldos_array) >= 1:
        saldos_array[0] = np.hstack([saldos_array[0], np.array(saldos[i].values).reshape(len(saldos[i].values), 1)])
    else:

        saldos_array.append(np.array(saldos[i].values).reshape(len(saldos[i].values), 1))

saldos['posiblesPerdidas'] =perdidas(saldos_array[0])

saldos.drop(columns=saldos.filter(regex=regex).columns, axis=1, inplace=True)
saldos.info()
<class 'pandas.core.frame.DataFrame'>
MultiIndex: 36131 entries, (1070499713, 6283724, 3376) to (1133162696, 7738006, 5172)
Data columns (total 4 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   TipoDocum         36131 non-null  object 
 1   total             36131 non-null  float64
 2   mediaSaldo        36131 non-null  float64
 3   posiblesPerdidas  36131 non-null  float64
dtypes: float64(3), object(1)
memory usage: 11.8+ MB
In [1]:
transferencias = pq.read_table('0transferencias.parquet').to_pandas()
transferencias.sort_values(by='FechaEfectiva', ascending=True, inplace=True)
transferencias.FechaEfectiva = pd.to_datetime(transferencias.FechaEfectiva, format='%Y-%m-%d')

# Agrupar las transacciones por mes y sumar los valores
transferencias = transferencias.groupby(
    [transferencias['Contrato'], transferencias['FechaEfectiva'].dt.to_period('M')], as_index=True
).agg(
    ValorTransaccion=('ValorNeto', 'sum'),
    Productos=('PlanProducto', 'first'),
    User=('Contrato', 'first'),
    Tipo=('TipoOper', 'first'),
)

transferencias = transferencias.groupby(
    [transferencias['User']], as_index=True
).agg(
    ValorTransaccion=('ValorTransaccion', 'mean'),
    NumberOfTransactions=('ValorTransaccion', 'count'),
)
print(transferencias.info())
transferencias['Users'] = transferencias.index
transferencias
<class 'pandas.core.frame.DataFrame'>
Index: 23412 entries, 10359 to 9999898
Data columns (total 2 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   ValorTransaccion      23412 non-null  float64
 1   NumberOfTransactions  23412 non-null  int64  
dtypes: float64(1), int64(1)
memory usage: 548.7 KB
None
Out[1]:
ValorTransaccion NumberOfTransactions Users
User
10359 -2.400000e+04 5 10359
11135 1.514032e+08 1 11135
11486 4.206700e+06 1 11486
11595 5.200000e+06 23 11595
11989 -5.500000e+07 1 11989
... ... ... ...
9998936 4.387500e+08 1 9998936
9999584 1.449000e+07 4 9999584
9999852 -3.300900e+07 1 9999852
9999884 -3.338484e+07 1 9999884
9999898 5.333333e+06 3 9999898

23412 rows × 3 columns

In [1]:
def check_last_less_than_others(matrix):
    last_elements = matrix[:, -1]
    # Compare the last element with all other elements in the row (excluding the last one)
    results = [last_elements[i] < (np.max((matrix[i, -4:-3])) * 0.3) for i in range(matrix.shape[0])]
    # Convert the list of results to a column vector
    result_array = np.array(results).reshape(-1, 1)
    return result_array


saldos['activeCases'] = check_last_less_than_others(saldos_array[0])
clientes = pq.read_table('0clientes.parquet').to_pandas()
clientes = clientes.loc[clientes['TIPODOCUM'] == 'C']
clientes.drop(
    columns=['EnvioExtractos', 'TIPODOCUM'], inplace=True)

# saldos = saldos.merge(clientes, how="left", left_on=['NroDocum'], right_on=['NroDocum'])
saldos = saldos.join(clientes.set_index("NroDocum"), on="NroDocum")

saldos['edad'] = saldos['FecNacim']
saldos = saldos.astype({'edad': 'str'})
saldos['edad'] = saldos['edad'].str.slice(0, 4)
saldos = saldos.astype({'edad': 'int'})
saldos['edad'] = -(saldos['edad'] - 2022)

saldos
Out[1]:
TipoDocum total mediaSaldo posiblesPerdidas activeCases CIUDAD FecNacim edad
NroDocum Contrato PlanProducto
1070499713 6283724 3376 C 1.036830e+08 4.712866e+06 0.00 False BOGOTA D.C., BOGOTA 1941-07-24 81
1067734816 4241304 3376 C 2.613527e+09 1.187967e+08 0.00 False BOGOTA D.C., BOGOTA 1966-12-06 56
1018964721 9943050 3376 C 2.808434e+08 1.276561e+07 830075.86 False BOGOTA D.C., BOGOTA 1966-04-11 56
1169386237 9995431 3376 C 1.732767e+08 7.876213e+06 0.00 False BOGOTA D.C., BOGOTA 1962-05-26 60
1095392085 5555946 3376 C 1.256775e+09 5.712612e+07 0.00 False BOGOTA D.C., BOGOTA 1962-09-25 60
... ... ... ... ... ... ... ... ... ... ...
1081765591 7398048 5172 C 1.504645e+07 6.839297e+05 0.00 False BOGOTA D.C., BOGOTA 1974-02-12 48
1155662224 8963958 5172 C 3.006538e+08 1.366608e+07 0.00 False BARRANQUILLA, ATLANTICO 1956-07-08 66
1054358908 4117269 5172 C 4.520644e+07 2.054838e+06 0.00 False BOGOTA D.C., BOGOTA 1980-04-28 42
1051426081 3737506 5172 C 4.491944e+07 2.041793e+06 0.00 False BOGOTA D.C., BOGOTA 1948-08-17 74
1133162696 7738006 5172 C 3.002064e+06 1.364575e+05 0.00 False ENVIGADO, ANTIOQUIA 1973-05-14 49

36131 rows × 8 columns

In [1]:
saldos['Contratos'] = saldos.index.get_level_values('Contrato')
saldos = saldos.join(transferencias.set_index("Users"), on="Contratos")
saldos
# 

saldos.loc[saldos['activeCases'] == False].head(10)
saldos = saldos.drop(columns=['TipoDocum', 'FecNacim', 'NumberOfTransactions', 'CIUDAD', 'mediaSaldo', 'edad'])
# 1079454835
In [1]:
def predictions(matrix):
    mask_array = []
    for i in range(matrix.shape[0]):
        # Aqui suve a 90 sin -3
        serie_temporal = matrix[i, :-3]

        try:
            serie = pd.Series(serie_temporal)

            # Calcula el promedio móvil con una ventana de tamaño window_size
            serie_temporal = serie.rolling(window=3, min_periods=1).mean().dropna().values[-3:]
            
            tiempo = np.arange(len(serie_temporal)).reshape(-1, 1)
            # Crea y ajusta un modelo de regresión lineal
            modelo = LinearRegression()
            modelo.fit(tiempo, serie_temporal)
            # Hacer predicciones para los siguientes tres valores
            siguientes_tres_valores = np.array(
                [[len(serie_temporal)], [len(serie_temporal) + 1], [len(serie_temporal) + 2]])

            predicciones = list((modelo.predict(siguientes_tres_valores)))

            predict_2 = (predicciones[-2]) < (serie_temporal[-1] * 0.3)
            predict_3 = (predicciones[-3]) < (serie_temporal[-1] * 0.3)
            predict_1 = (predicciones[-1]) < (serie_temporal[-1] * 0.3)
            if predict_1 or predict_2 or predict_3:
                mask_array.append(True)
            else:
                mask_array.append(False)

        except:
            mask_array.append(False)

    return mask_array


def tendencia(matrix):
    mask_array = []
    for i in range(matrix.shape[0]):
        serie_temporal = matrix[i, :-3]
        tiempo = np.arange(len(serie_temporal)).reshape(-1, 1)
        modelo = LinearRegression()
        modelo.fit(tiempo, serie_temporal)
        pendiente = modelo.coef_[0]
        mask_array.append(pendiente)

    return mask_array


def drop_percent(matrix):
    drop_array = []
    for i in range(matrix.shape[0]):
        y = list(matrix[i])
        y = [i for i in y if i > 0]
        start = -2
        try:
            porcentaje_caida = ((y[-1]) / (y[start] * 0.01)) - 100
            drop_array.append(porcentaje_caida)
        except:
            drop_array.append(0)
    return drop_array


saldos['porcentajeCaida'] = drop_percent(saldos_array[0])
saldos['slope'] = tendencia(saldos_array[0])
saldos['prediction'] = predictions(saldos_array[0])

saldos
Out[1]:
total posiblesPerdidas activeCases Contratos ValorTransaccion porcentajeCaida slope prediction
NroDocum Contrato PlanProducto
1070499713 6283724 3376 1.036830e+08 0.00 False 6283724 -5.000000e+06 0.718735 -3.040252e+05 False
1067734816 4241304 3376 2.613527e+09 0.00 False 4241304 NaN 0.906037 9.221042e+04 False
1018964721 9943050 3376 2.808434e+08 830075.86 False 9943050 -1.825385e+04 -0.905232 1.173794e+05 False
1169386237 9995431 3376 1.732767e+08 0.00 False 9995431 1.443160e+06 0.471734 1.130727e+06 False
1095392085 5555946 3376 1.256775e+09 0.00 False 5555946 NaN 0.590664 1.434138e+04 False
... ... ... ... ... ... ... ... ... ... ...
1081765591 7398048 5172 1.504645e+07 0.00 False 7398048 1.000000e+07 100.000000 0.000000e+00 False
1155662224 8963958 5172 3.006538e+08 0.00 False 8963958 2.020000e+08 100.000000 0.000000e+00 False
1054358908 4117269 5172 4.520644e+07 0.00 False 4117269 3.000000e+07 100.000000 0.000000e+00 False
1051426081 3737506 5172 4.491944e+07 0.00 False 3737506 3.000000e+07 100.000000 0.000000e+00 False
1133162696 7738006 5172 3.002064e+06 0.00 False 7738006 2.000000e+06 100.000000 0.000000e+00 False

36131 rows × 8 columns

In [1]:
baja_true = saldos.loc[(saldos['activeCases'] == True) & (saldos['slope'] < 0)]
sube_true = saldos.loc[(saldos['activeCases'] == True) & (saldos['slope'] > 0)]
baja_false = saldos.loc[(saldos['activeCases'] == False) & (saldos['slope'] < 0)]
sube_false = saldos.loc[(saldos['activeCases'] == False) & (saldos['slope'] > 0)]

baja_true
Out[1]:
total posiblesPerdidas activeCases Contratos ValorTransaccion porcentajeCaida slope prediction
NroDocum Contrato PlanProducto
1105297798 2200275 4899 4.218034e+08 8.272368e+06 True 2200275 -1.284862e+06 -86.497742 -1.248519e+06 False
1187108348 7044987 8404 3.471420e+09 8.442569e+07 True 7044987 -1.399432e+08 -37.094613 -8.187250e+05 False
1108520330 3533499 8404 1.946047e+09 7.691772e+07 True 3533499 -2.585378e+07 -72.480132 -3.550158e+05 False
1022097976 8721209 8404 5.373961e+07 1.121144e+05 True 8721209 -2.601374e+05 -1.312980 -1.844221e+04 False
1165012646 5990227 3376 5.917253e+09 3.002709e+08 True 5990227 -1.913979e+07 -50.000000 -3.332961e+06 False
... ... ... ... ... ... ... ... ... ... ...
1164992875 5654444 4899 1.107758e+08 6.093500e+06 True 5654444 -6.386633e+06 -82.716618 -1.411481e+05 False
1058514762 7123959 4899 6.803875e+09 1.591713e+08 True 7123959 4.021410e+06 -50.000000 -2.150744e+06 False
1000118312 5648869 4899 1.150976e+09 3.823788e+07 True 5648869 1.571801e+05 -50.000000 -3.087818e+05 False
1080796902 408749 5172 1.537971e+08 1.779550e+06 True 408749 8.726171e+04 -50.000000 -1.513201e+05 False
1015365088 376257 4899 7.917705e+09 3.754148e+08 True 376257 7.915645e+05 -96.460457 -1.518964e+06 False

797 rows × 8 columns

Porcentaje de caida Tendencia si pronostica una caida de mas del 70%

In [1]:
a = len(baja_true.loc[baja_true.prediction == True].values)

b = len(sube_true.loc[sube_true.prediction == True].values)

verdaderos = len(saldos.loc[saldos['activeCases'] == True].index.values)

print('TOTAL DE CASOS ACTIVOS: ', verdaderos)
print('CASOS ACTIVOS CON PREDICCION -30%: ', (a + b))

a = len(baja_true.loc[baja_true.slope < 0].values)

verdaderos = len(saldos.loc[saldos['activeCases'] == True].index.values)

print('CASOS ACTIVOS SLOPE: ', (a))
TOTAL DE CASOS ACTIVOS:  1452
CASOS ACTIVOS CON PREDICCION -30%:  171
CASOS ACTIVOS SLOPE:  797
In [1]:
a = len(baja_false.loc[baja_false.prediction == True].values)

b = len(sube_false.loc[sube_false.prediction == True].values)

verdaderos = len(saldos.loc[saldos['activeCases'] == False].index.values)

print('TOTAL DE NO CASOS ACTIVOS: ', verdaderos)
print('CASOS NO ACTIVOS CON PREDICCION -30%: ', (a + b))

a = len(baja_false.loc[baja_false.slope < 0].values)

b = len(sube_false.loc[sube_false.slope < 0].values)

verdaderos = len(saldos.loc[saldos['activeCases'] == False].index.values)

print('CASOS NO ACTIVOS SLOPE: ', (a))
saldos['totalPercent'] = saldos['slope'] / saldos['total']

# Mostrar el DataFrame modificado
saldos['ValorTransaccion'] = saldos['ValorTransaccion'].fillna(saldos['total'] / 1000)
saldos['ValorTransaccion'].fillna(0, inplace=True)
saldos['totalPercent'] = saldos['totalPercent'].fillna(saldos['porcentajeCaida'] * 10)



saldos
TOTAL DE NO CASOS ACTIVOS:  34679
CASOS NO ACTIVOS CON PREDICCION -30%:  1884
CASOS NO ACTIVOS SLOPE:  21292
Out[1]:
total posiblesPerdidas activeCases Contratos ValorTransaccion porcentajeCaida slope prediction totalPercent
NroDocum Contrato PlanProducto
1070499713 6283724 3376 1.036830e+08 0.00 False 6283724 -5.000000e+06 0.718735 -3.040252e+05 False -0.002932
1067734816 4241304 3376 2.613527e+09 0.00 False 4241304 2.613527e+06 0.906037 9.221042e+04 False 0.000035
1018964721 9943050 3376 2.808434e+08 830075.86 False 9943050 -1.825385e+04 -0.905232 1.173794e+05 False 0.000418
1169386237 9995431 3376 1.732767e+08 0.00 False 9995431 1.443160e+06 0.471734 1.130727e+06 False 0.006526
1095392085 5555946 3376 1.256775e+09 0.00 False 5555946 1.256775e+06 0.590664 1.434138e+04 False 0.000011
... ... ... ... ... ... ... ... ... ... ... ...
1081765591 7398048 5172 1.504645e+07 0.00 False 7398048 1.000000e+07 100.000000 0.000000e+00 False 0.000000
1155662224 8963958 5172 3.006538e+08 0.00 False 8963958 2.020000e+08 100.000000 0.000000e+00 False 0.000000
1054358908 4117269 5172 4.520644e+07 0.00 False 4117269 3.000000e+07 100.000000 0.000000e+00 False 0.000000
1051426081 3737506 5172 4.491944e+07 0.00 False 3737506 3.000000e+07 100.000000 0.000000e+00 False 0.000000
1133162696 7738006 5172 3.002064e+06 0.00 False 7738006 2.000000e+06 100.000000 0.000000e+00 False 0.000000

36131 rows × 9 columns

Descripción del modelo y sus variables¶

El RandomForestClassifier es un método de aprendizaje supervisado que pertenece a la familia de los métodos de ensamble, específicamente a los métodos de bagging. Se construye utilizando múltiples árboles de decisión durante el entrenamiento y produce la clase que es la moda de las clases de los árboles individuales para clasificación, o el promedio de las predicciones para regresión.

Características principales:

  • Robustez: Al utilizar múltiples árboles, el clasificador es generalmente robusto frente al sobreajuste, especialmente en comparación con otros modelos como los árboles de decisión individuales.

  • Manejo de Datos: Puede manejar automáticamente tanto variables numéricas como categóricas y no requiere que las variables de entrada estén escaladas o normalizadas.

  • Importancia de las Características: Ofrece una excelente perspectiva sobre la importancia de las características, lo que puede ser útil para la selección de variables y entender qué factores son más importantes para la predicción.

  • Flexibilidad: Puede ser utilizado tanto para problemas de clasificación como de regresión, haciendo que sea una herramienta versátil en el arsenal de un científico de datos.

El clasificador trabaja construyendo un conjunto de árboles de decisión entrenados en diferentes subconjuntos del conjunto de datos original (seleccionados con reemplazo), con cada árbol dando un voto en la predicción final. Esta técnica mejora la precisión de la predicción y controla el sobreajuste.

Variable Description
Predicción con regresion Lineal Valor que indica si se detectó una predicción que baje del 70% en los 3 meses siguientes
Pendiente promedio de los saldos Indica cual es el ritmo de crecimiento de la serie temporal ( si es negativo o positivo )
Promedio de transacciones Promedio de transacciones del cliente ( si ha retirado mas de lo que ha ingresado a la cuenta )
Relación pendiente - Saldo Total Indica la relación que hay entre el saldo total y la pendiente

RandomForestClassifier¶

In [1]:
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier  # Importar RandomForest
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.preprocessing import LabelEncoder
from sklearn.impute import SimpleImputer
import pandas as pd

# Suponemos que 'data' es tu DataFrame original y que 'saldos' ya ha sido cargado correctamente
data = saldos.copy()

# Imputación de valores faltan

# (Asumiendo que necesitas transformar 'prediction' de categórica a numérica si aún no está hecho)
le = LabelEncoder()
data['prediction'] = le.fit_transform(data['prediction'])

# Separar las variables predictoras y el objetivo
X = data[['prediction', 'porcentajeCaida', 'totalPercent', 'ValorTransaccion']]
y = data['activeCases']
rd = 5

# Dividir los datos en conjuntos de entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=rd)

# Modelo de Random Forest
forest_model = RandomForestClassifier(criterion='log_loss', n_estimators=10, random_state=rd, n_jobs=3)  # Usar 100 árboles
forest_model.fit(X_train, y_train)

# Predicciones y evaluación del modelo
predicciones_forest = forest_model.predict(X_test)
reporte_clasificacion_forest = classification_report(y_test, predicciones_forest)
matriz_confusion_forest = confusion_matrix(y_test, predicciones_forest)
accuracy_forest = accuracy_score(y_test, predicciones_forest)

# Imprimir los resultados
print("Reporte de clasificación (Random Forest):\n", reporte_clasificacion_forest)
print("Matriz de confusión (Random Forest):\n", matriz_confusion_forest)
print(f'Accuracy del modelo (Random Forest): {accuracy_forest:.2f}')
Reporte de clasificación (Random Forest):
               precision    recall  f1-score   support

       False       0.99      1.00      0.99      6941
        True       0.91      0.78      0.84       286

    accuracy                           0.99      7227
   macro avg       0.95      0.89      0.92      7227
weighted avg       0.99      0.99      0.99      7227

Matriz de confusión (Random Forest):
 [[6919   22]
 [  63  223]]
Accuracy del modelo (Random Forest): 0.99
In [1]:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint

# Definición del modelo
# Definición del espacio de parámetros a probar
param_dist = {
    'n_estimators': randint(10, 50),
    'criterion': ['gini', 'entropy', ],
    'max_depth': [None, 10, 20, 30],
    'min_samples_split': randint(2, 11)
}

# Crear el objeto RandomizedSearchCV
random_search = RandomizedSearchCV(estimator=forest_model, param_distributions=param_dist, n_iter=10, cv=5, scoring='accuracy', random_state=rd)

# Ejecutar la búsqueda aleatoria
random_search.fit(X_train, y_train)

# Mejores parámetros encontrados
print("Mejores parámetros:", random_search.best_params_)

# Mejor modelo encontrado
best_forest_random = random_search.best_estimator_

# Evaluación del mejor modelo

predicciones_best_forest_random = best_forest_random.predict(X_test)
reporte_clasificacion_forest = classification_report(y_test, predicciones_forest)
matriz_confusion_forest = confusion_matrix(y_test, predicciones_forest)
accuracy_forest = accuracy_score(y_test, predicciones_forest)

# Imprimir los resultados
print("Reporte de clasificación (Random Forest):\n", reporte_clasificacion_forest)
print("Matriz de confusión (Random Forest):\n", matriz_confusion_forest)
print(f'Accuracy del modelo (Random Forest): {accuracy_forest:.2f}')



print("Accuracy del mejor modelo (Randomized):", accuracy_score(y_test, predicciones_best_forest_random))
Mejores parámetros: {'criterion': 'gini', 'max_depth': 30, 'min_samples_split': 3, 'n_estimators': 40}
Reporte de clasificación (Random Forest):
               precision    recall  f1-score   support

       False       0.99      1.00      0.99      6941
        True       0.91      0.78      0.84       286

    accuracy                           0.99      7227
   macro avg       0.95      0.89      0.92      7227
weighted avg       0.99      0.99      0.99      7227

Matriz de confusión (Random Forest):
 [[6919   22]
 [  63  223]]
Accuracy del modelo (Random Forest): 0.99
Accuracy del mejor modelo (Randomized): 0.9876850698768507
In [1]:
print("Importancia de los predictores en el modelo")
print("-------------------------------------------")
importancia_predictores = pd.DataFrame(
    {'predictor': forest_model.feature_names_in_,
     'importancia': forest_model.feature_importances_}
)
importancia_predictores.sort_values('importancia', ascending=False)
Importancia de los predictores en el modelo
-------------------------------------------
Out[1]:
predictor importancia
1 porcentajeCaida 0.593222
2 totalPercent 0.220141
3 ValorTransaccion 0.172519
0 prediction 0.014118

Predecir si un cliente retirará el 70% o más de sus ahorros en los próximos meses puede ofrecer a un banco una serie de ventajas financieras y estratégicas significativas. Aquí hay algunas de ellas:¶

Ventajas Description
Gestión de Riesgos Mejora la capacidad del banco para anticipar y prepararse para los grandes retiros, mejorando la estabilidad y la confianza en el banco.
Planificación de Liquidez Permite al banco manejar su flujo de caja y reservas de efectivo de manera más eficiente, reduciendo la necesidad de obtener liquidez costosa en el último minuto.
Retención de Clientes Ofrece al banco una oportunidad para implementar estrategias proactivas de retención de clientes, ajustando productos y servicios para evitar la salida de fondos.
Optimización de Inversiones Ayuda al banco a equilibrar su cartera de inversiones para maximizar los rendimientos sin sacrificar la liquidez necesaria para responder a los retiros.
Prevención del Fraude Permite identificar comportamientos de retiro inusuales que podrían indicar fraude, lo que protege al cliente y al banco.
Capital de Reserva Asegura que el banco cumpla con las regulaciones de capital de reserva más eficientemente, alineando las necesidades de liquidez con los requisitos regulatorios.
Estabilidad Financiera Previniendo retiros masivos, la institución mantiene su estabilidad y la del sistema financiero en su conjunto.
Segmentación de Mercado Facilita la personalización de la oferta de productos y la comunicación de marketing al comprender la probabilidad de retiro de los clientes.
Planificación Estratégica Informa decisiones estratégicas del banco en áreas como expansión operativa, inversiones y estrategias de retención de clientes.
In [1]:
dinero = saldos.copy()

dinero = dinero.loc[dinero['activeCases'] == True]
dinero = dinero.join(clientes.set_index("NroDocum"), on="NroDocum")
dinero['posiblesPerdidas']
Out[1]:
NroDocum    Contrato  PlanProducto
1097503608  2032693   3376            57521420.94
1105297798  2200275   4899             8272368.04
1113002343  9557745   8404            25719668.61
1187108348  7044987   8404            84425691.55
1108520330  3533499   8404            76917717.31
                                         ...     
1113253168  3292675   4899            29908682.79
1130196219  798784    4899                   0.00
1074554783  6698162   4899            77726154.93
1146147828  1019994   5172            14263039.89
1043158374  4104137   5172            23870328.64
Name: posiblesPerdidas, Length: 1452, dtype: float64
In [1]: