Modelado de Churn para una Empresa de Telecomunicaciones

Author

Ing Gabriel Chávez Camargo

Published

October 14, 2024

PROBLEMA DE NEGOCIO

Una empresa de telecomunicaciones desea mejorar la retención de sus clientes, identificando aquellos que tienen más chances de abandonar el servicio (Churn). Como científico de datos, tu objetivo será limpiar y preparar un conjunto de datos para el entrenamiento de un modelo de Churn. A continuación, se presentan las preguntas clave que guiarán el proceso de limpieza de datos:

  1. ¿Qué insights podemos obtener del análisis exploratorio inicial del conjunto de datos?

  2. ¿Qué transformaciones básicas son necesarias para preparar los datos?

  3. ¿Cómo podemos identificar y tratar los datos duplicados y los valores nulos?

  4. ¿Cómo manejamos los outliers presentes en el dataset?

  5. ¿Qué técnicas aplicamos para procesar las variables categóricas?

1. Configuración del ambiente

Code
import time
import numpy as np
import pandas as pd
pd.set_option('display.max_columns', None)
import seaborn as sns
import matplotlib.pyplot as plt
import json
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
from sklearn.feature_selection import RFECV
from sklearn.feature_selection import RFE
from sklearn.decomposition import PCA
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import OrdinalEncoder, MinMaxScaler
from imblearn.over_sampling import RandomOverSampler
global datos_churn

2. Obtención y Tratamiento de Datos

##2.1 Cargando las Bases de datos

Code
datos_churn = pd.read_json("base_clientes.json")
datos_churn.head()
id_cliente Churn cliente telefono internet cuenta
0 0002-ORFBO no {'genero': 'femenino', 'anciano': 0, 'pareja':... {'servicio_telefono': 'si', 'varias_lineas': '... {'servicio_internet': 'DSL', 'seguridad_online... {'contrato': None, 'facturacion_electronica': ...
1 0003-MKNFE no {'genero': 'masculino', 'anciano': 0, 'pareja'... {'servicio_telefono': 'si', 'varias_lineas': '... {'servicio_internet': 'DSL', 'seguridad_online... {'contrato': 'mensual', 'facturacion_electroni...
2 0004-TLHLJ si {'genero': 'masculino', 'anciano': 0, 'pareja'... {'servicio_telefono': 'si', 'varias_lineas': '... {'servicio_internet': 'fibra optica', 'segurid... {'contrato': 'mensual', 'facturacion_electroni...
3 0011-IGKFF si {'genero': 'masculino', 'anciano': 1, 'pareja'... {'servicio_telefono': 'si', 'varias_lineas': '... {'servicio_internet': 'fibra optica', 'segurid... {'contrato': 'mensual', 'facturacion_electroni...
4 0013-EXCHZ si {'genero': 'femenino', 'anciano': 1, 'pareja':... {'servicio_telefono': 'si', 'varias_lineas': '... {'servicio_internet': 'fibra optica', 'segurid... {'contrato': 'mensual', 'facturacion_electroni...

Cargamos los datos que se encuentran en formato JSON

Code
datos_churn.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7344 entries, 0 to 7343
Data columns (total 6 columns):
 #   Column      Non-Null Count  Dtype 
---  ------      --------------  ----- 
 0   id_cliente  7344 non-null   object
 1   Churn       7344 non-null   object
 2   cliente     7344 non-null   object
 3   telefono    7344 non-null   object
 4   internet    7344 non-null   object
 5   cuenta      7344 non-null   object
dtypes: object(6)
memory usage: 344.4+ KB

Observamos que tenemos 7344 valores totales, pero adentro encontramos las diferentes columnas.

Code
# Conversion de datos df
def lectura_datos(data):

  with open ('base_clientes.json',encoding='utf-8') as f:
    json_bruto=json.load(f)
    datos_churn=pd.json_normalize(json_bruto)
    return datos_churn
  
  
datos_churn=lectura_datos(datos_churn)
Code
datos_churn.head()
id_cliente Churn cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total
0 0002-ORFBO no femenino 0 si si 9.0 si no DSL no si no si si no None None None NaN None
1 0003-MKNFE no masculino 0 no no 9.0 si si DSL no no no no no si mensual no cheque 59.9 542.4
2 0004-TLHLJ si masculino 0 no no 4.0 si no fibra optica no no si no no no mensual si cheque electronico 73.9 280.85
3 0011-IGKFF si masculino 1 si no 13.0 si no fibra optica no si si no si si mensual si cheque electronico 98.0 1237.85
4 0013-EXCHZ si femenino 1 si no 3.0 si no fibra optica no no no si si no mensual si cheque 83.9 267.4

2.2 Tratamiento de datos

Code
with open('./Diccionario.txt', 'r', encoding='utf-8') as file:
    contenido = file.read()
print(contenido)
La base de datos contiene columnas además del ID de los clientes y el churn:

Cliente:
género: género (masculino y femenino)
anciano: información sobre si un cliente tiene o no una edad igual o mayor a 65 años
pareja: si el cliente tiene o no una pareja
dependientes: si el cliente tiene o no dependientes
tiempo_servicio: meses de contrato del cliente

Servicio de telefonía:
servicio_telefono: suscripción al servicio telefónico
varias_lineas: suscripción a más de una línea telefónica

Servicio de internet:
servicio_internet: suscripción a un proveedor de internet
seguridad_online: suscripción adicional a seguridad en línea
backup_online: suscripción adicional a copias de seguridad en línea
proteccion_dispositivo: suscripción adicional a protección en el dispositivo
soporte_tecnico: suscripción adicional a soporte técnico, menos tiempo de espera
tv_streaming: suscripción a TV por cable
peliculas_streaming: suscripción a streaming de películas

Cuenta:
contrato: tipo de contrato
factura_electronica: si el cliente prefiere recibir la factura en línea
metodo_pago: forma de pago
cobros_mensuales: total de todos los servicios del cliente por mes
cobros_totales: total gastado por el cliente

Obtenemos la descripción de Cada una de las columnas

Code
datos_churn.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7344 entries, 0 to 7343
Data columns (total 21 columns):
 #   Column                           Non-Null Count  Dtype  
---  ------                           --------------  -----  
 0   id_cliente                       7344 non-null   object 
 1   Churn                            7344 non-null   object 
 2   cliente.genero                   7344 non-null   object 
 3   cliente.anciano                  7344 non-null   int64  
 4   cliente.pareja                   7344 non-null   object 
 5   cliente.dependientes             7344 non-null   object 
 6   cliente.tiempo_servicio          7336 non-null   float64
 7   telefono.servicio_telefono       7344 non-null   object 
 8   telefono.varias_lineas           7344 non-null   object 
 9   internet.servicio_internet       7344 non-null   object 
 10  internet.seguridad_online        7344 non-null   object 
 11  internet.backup_online           7344 non-null   object 
 12  internet.proteccion_dispositivo  7344 non-null   object 
 13  internet.soporte_tecnico         7344 non-null   object 
 14  internet.tv_streaming            7344 non-null   object 
 15  internet.peliculas_streaming     7344 non-null   object 
 16  cuenta.contrato                  7312 non-null   object 
 17  cuenta.facturacion_electronica   7326 non-null   object 
 18  cuenta.metodo_pago               7317 non-null   object 
 19  cuenta.cobros.mensual            7326 non-null   float64
 20  cuenta.cobros.Total              7328 non-null   object 
dtypes: float64(2), int64(1), object(18)
memory usage: 1.2+ MB
Code
datos_churn.isna().sum()/datos_churn.shape[1]
id_cliente                         0.000000
Churn                              0.000000
cliente.genero                     0.000000
cliente.anciano                    0.000000
cliente.pareja                     0.000000
cliente.dependientes               0.000000
cliente.tiempo_servicio            0.380952
telefono.servicio_telefono         0.000000
telefono.varias_lineas             0.000000
internet.servicio_internet         0.000000
internet.seguridad_online          0.000000
internet.backup_online             0.000000
internet.proteccion_dispositivo    0.000000
internet.soporte_tecnico           0.000000
internet.tv_streaming              0.000000
internet.peliculas_streaming       0.000000
cuenta.contrato                    1.523810
cuenta.facturacion_electronica     0.857143
cuenta.metodo_pago                 1.285714
cuenta.cobros.mensual              0.857143
cuenta.cobros.Total                0.761905
dtype: float64

La columna con mas valores faltantes se encuetra en cuenta.contrato con 1.5% de los datos.

Code
columnas=datos_churn.columns

for columnas in columnas:
    print('Columna',columnas)
    print(datos_churn[columnas].value_counts())
    print('---------------------------------')
Columna id_cliente
id_cliente
0793-TWELN    2
7176-WIONM    2
2371-JQHZZ    2
1193-RTSLK    2
2192-CKRLV    2
             ..
3401-URHDA    1
3400-ESFUW    1
3399-BMLVW    1
3398-ZOUAA    1
9995-HOTOH    1
Name: count, Length: 7267, dtype: int64
---------------------------------
Columna Churn
Churn
no    5223
si    1895
       226
Name: count, dtype: int64
---------------------------------
Columna cliente.genero
cliente.genero
masculino    3713
femenino     3631
Name: count, dtype: int64
---------------------------------
Columna cliente.anciano
cliente.anciano
0    6147
1    1197
Name: count, dtype: int64
---------------------------------
Columna cliente.pareja
cliente.pareja
no    3793
si    3551
Name: count, dtype: int64
---------------------------------
Columna cliente.dependientes
cliente.dependientes
no    5143
si    2201
Name: count, dtype: int64
---------------------------------
Columna cliente.tiempo_servicio
cliente.tiempo_servicio
1.0       639
72.0      374
2.0       249
3.0       209
4.0       188
         ... 
321.0       1
254.0       1
1000.0      1
1080.0      1
512.0       1
Name: count, Length: 84, dtype: int64
---------------------------------
Columna telefono.servicio_telefono
telefono.servicio_telefono
si    6629
no     715
Name: count, dtype: int64
---------------------------------
Columna telefono.varias_lineas
telefono.varias_lineas
no                          3535
si                          3094
sin servicio de telefono     715
Name: count, dtype: int64
---------------------------------
Columna internet.servicio_internet
internet.servicio_internet
fibra optica    3234
DSL             2510
no              1600
Name: count, dtype: int64
---------------------------------
Columna internet.seguridad_online
internet.seguridad_online
no                          3643
si                          2101
sin servicio de internet    1600
Name: count, dtype: int64
---------------------------------
Columna internet.backup_online
internet.backup_online
no                          3214
si                          2530
sin servicio de internet    1600
Name: count, dtype: int64
---------------------------------
Columna internet.proteccion_dispositivo
internet.proteccion_dispositivo
no                          3227
si                          2517
sin servicio de internet    1600
Name: count, dtype: int64
---------------------------------
Columna internet.soporte_tecnico
internet.soporte_tecnico
no                          3624
si                          2120
sin servicio de internet    1600
Name: count, dtype: int64
---------------------------------
Columna internet.tv_streaming
internet.tv_streaming
no                          2926
si                          2818
sin servicio de internet    1600
Name: count, dtype: int64
---------------------------------
Columna internet.peliculas_streaming
internet.peliculas_streaming
no                          2893
si                          2851
sin servicio de internet    1600
Name: count, dtype: int64
---------------------------------
Columna cuenta.contrato
cuenta.contrato
mensual     4039
dos años    1755
un año      1518
Name: count, dtype: int64
---------------------------------
Columna cuenta.facturacion_electronica
cuenta.facturacion_electronica
si    4344
no    2982
Name: count, dtype: int64
---------------------------------
Columna cuenta.metodo_pago
cuenta.metodo_pago
cheque electronico                     2459
cheque                                 1678
transferencia bancaria (automatica)    1600
tarjeta de credito (automatico)        1580
Name: count, dtype: int64
---------------------------------
Columna cuenta.cobros.mensual
cuenta.cobros.mensual
20.05     65
19.85     46
19.90     46
19.55     46
19.70     45
          ..
117.60     1
33.65      1
23.45      1
116.55     1
67.85      1
Name: count, Length: 1585, dtype: int64
---------------------------------
Columna cuenta.cobros.Total
cuenta.cobros.Total
20.2       11
           11
19.55      10
19.9        9
19.75       9
           ..
499.4       1
1160.75     1
1396        1
435         1
3707.6      1
Name: count, Length: 6516, dtype: int64
---------------------------------

Encontramos algo interesante en la columna Cobros toal tenemos 11 columnas sin ningun tipo de valor al igual que nuestra variable objetivo CHURN tuene 226 registros vacios.

Code
datos_churn[datos_churn['Churn'] == '']
id_cliente Churn cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total
30 0047-ZHDTW femenino 0 no no 11.0 si si fibra optica si no no no no no mensual si transferencia bancaria (automatica) 79.00 929.3
75 0120-YZLQA masculino 0 no no 71.0 si no no sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet dos años si tarjeta de credito (automatico) 19.90 1355.1
96 0154-QYHJU masculino 0 no no 29.0 si no DSL si si no si no no un año si cheque electronico 58.75 1696.2
98 0162-RZGMZ femenino 1 no no 5.0 si no DSL si si no si no no mensual no tarjeta de credito (automatico) 59.90 287.85
175 0274-VVQOQ masculino 1 si no 65.0 si si fibra optica no si si no si si un año si transferencia bancaria (automatica) 103.15 6792.45
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
7211 9920-GNDMB masculino 0 no no 9.0 si si fibra optica no no no no no no mensual si cheque electronico 76.25 684.85
7239 9955-RVWSC femenino 0 si si 67.0 si no no sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet dos años si transferencia bancaria (automatica) 19.25 1372.9
7247 9966-VYRTZ femenino 0 si si 31.0 si no no sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet mensual si cheque 19.55 658.95
7267 6532-YOHZY masculino 0 si si 45.0 si si fibra optica no si si si si si dos años si transferencia bancaria (automatica) 109.75 4900.65
7342 3601-UTZXO masculino 0 si si 41.0 si no no sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet un año no transferencia bancaria (automatica) 19.50 798.2

226 rows × 21 columns

Code
datos_churn[datos_churn['cuenta.cobros.Total'] == ' '][['cliente.tiempo_servicio','cuenta.contrato','cuenta.facturacion_electronica','cuenta.cobros.mensual','cuenta.cobros.Total']]
cliente.tiempo_servicio cuenta.contrato cuenta.facturacion_electronica cuenta.cobros.mensual cuenta.cobros.Total
975 0.0 dos años no 56.05
1775 0.0 dos años no 20.00
1955 0.0 dos años si 61.90
2075 0.0 dos años si 19.70
2232 0.0 dos años no 20.25
2308 0.0 dos años no 25.35
2930 0.0 dos años no 73.35
3134 0.0 dos años no 25.75
3203 0.0 dos años si 52.55
4169 0.0 dos años no 80.85
5599 0.0 dos años no 19.85

Podriamos suponer que las columna tiempo_servicio se podria rellenar con 24 que refiere a dos años pero no sabemos el cliente en realidad, cuanto tiempo tiene con la empresa.

Code
datos_con_nulos = datos_churn[datos_churn.isna().any(axis=1)][['cliente.tiempo_servicio', 'cuenta.contrato',
       'cuenta.facturacion_electronica', 'cuenta.metodo_pago',
       'cuenta.cobros.mensual', 'cuenta.cobros.Total']].head(15)

datos_con_nulos
cliente.tiempo_servicio cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total
0 9.0 None None None NaN None
9 NaN dos años si cheque 90.45 5957.9
181 NaN mensual si cheque 29.30 355.9
186 NaN mensual si tarjeta de credito (automatico) 63.95 318.1
452 67.0 None None tarjeta de credito (automatico) NaN 6886.25
773 NaN dos años si transferencia bancaria (automatica) 101.05 5971.25
994 21.0 None si None NaN 1565.7
1645 18.0 None no None 20.05 None
1646 23.0 None no None NaN None
1647 55.0 None si tarjeta de credito (automatico) NaN None
1649 9.0 mensual None cheque NaN 890.6
2086 13.0 mensual si None 59.90 788.35
2087 40.0 None None None NaN None
2089 50.0 None None cheque 103.05 5153.5
3624 NaN dos años no transferencia bancaria (automatica) 76.10 1054.8
Code
datos_con_nulos.shape
(15, 6)

Podemos observar que los nulos no tienen un patron claro para poder remplazarlos, y en algunos casos algunas columnas estan casi vacias.

Code
datos_churn.describe()
cliente.anciano cliente.tiempo_servicio cuenta.cobros.mensual
count 7344.000000 7336.000000 7326.000000
mean 0.162990 33.271265 64.683770
std 0.369382 35.776684 30.143033
min 0.000000 0.000000 18.250000
25% 0.000000 9.000000 35.362500
50% 0.000000 29.000000 70.300000
75% 0.000000 56.000000 89.887500
max 1.000000 1080.000000 118.750000

En la columna cliente.tiempo_servicio obtenemos usuarios con 180 meses lo veremos mejor en un BOXPLOT

Code
# Configurar el tamaño de la figura
plt.figure(figsize=(12, 6))

# Crear los subplots
plt.subplot(1, 3, 1)
sns.boxplot(x='cliente.anciano', data=datos_churn)
plt.title('Boxplot de cliente.anciano')

plt.subplot(1, 3, 2)
sns.boxplot(x='cliente.tiempo_servicio', data=datos_churn)
plt.title('Boxplot de cliente.tiempo_servicio')

plt.subplot(1, 3, 3)
sns.boxplot(x='cuenta.cobros.mensual', data=datos_churn)
plt.title('Boxplot de cuenta.cobros.mensual')

# Ajustar el layout para que no se superpongan los gráficos
plt.tight_layout()

# Mostrar el gráfico
plt.show()

3 Tratamiento de Datos

3.1 Preprocesamiento

Code
def preprocesamiento(datos_churn):


  #=====================================================================
                            #Limpieza de outliers
  #=====================================================================
    # Calcular cuartiles e IQR
  primer_cuartil = datos_churn['cliente.tiempo_servicio'].quantile(0.25)
  tercer_cuartil = datos_churn['cliente.tiempo_servicio'].quantile(0.75)
  IQR = tercer_cuartil - primer_cuartil

  # Calcular los límites para considerar los valores atípicos
  limite_inferior = primer_cuartil - 1.5 * IQR
  limite_superior = tercer_cuartil + 1.5 * IQR

  # Filtrar los valores que están dentro de los límites
  datos_churn = datos_churn[datos_churn['cliente.tiempo_servicio'].between(limite_inferior, limite_superior)]
    #=====================================================================
                #Relleno de los valores "" en las columnas con nan 
  #=====================================================================
  
    #Primera columna en los datos churn cambairemos los valores '' por nan para despues eliminarlos.
  datos_churn.loc[:, 'Churn'] = datos_churn['Churn'].replace('', np.nan)

  #Segunda Columna en los datos cuenta.cobros.Total cambairemos los valores '' por nan para despues eliminarlos.
  datos_churn.loc[:, 'cuenta.cobros.Total'] = datos_churn['cuenta.cobros.Total'].replace(' ', np.nan)
  
   #=====================================================================
                            #Limpieza de duplicados y nulos
  #=====================================================================
  datos_churn=datos_churn.dropna()
  datos_churn=datos_churn.drop_duplicates().reset_index(drop=True)   
  
  datos_churn['cuenta.cobros.Total']=datos_churn['cuenta.cobros.Total'].astype('float')
    ## Nueva variables agregadas
  datos_churn['ratio_gasto_tiempo'] = (datos_churn['cuenta.cobros.Total'] / datos_churn['cliente.tiempo_servicio']).round(2)

  # Calcula el gasto total adicional
  datos_churn['gasto_total_adicional'] = datos_churn['cuenta.cobros.Total'] - (datos_churn['cuenta.cobros.mensual'] * datos_churn['cliente.tiempo_servicio'])
  datos_churn['gasto_adicional'] = datos_churn['gasto_total_adicional'].apply(lambda x: x if x > 0 else 0)
    # Elimina la columna 'gasto_total_adicional' si ya no es necesaria
  datos_churn.drop(columns=['gasto_total_adicional'], inplace=True)
  return datos_churn
Code
datos_churn=preprocesamiento(datos_churn)
datos_churn.head()
id_cliente Churn cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total ratio_gasto_tiempo gasto_adicional
0 0003-MKNFE no masculino 0 no no 9.0 si si DSL no no no no no si mensual no cheque 59.9 542.40 60.27 3.3
1 0004-TLHLJ si masculino 0 no no 4.0 si no fibra optica no no si no no no mensual si cheque electronico 73.9 280.85 70.21 0.0
2 0011-IGKFF si masculino 1 si no 13.0 si no fibra optica no si si no si si mensual si cheque electronico 98.0 1237.85 95.22 0.0
3 0013-EXCHZ si femenino 1 si no 3.0 si no fibra optica no no no si si no mensual si cheque 83.9 267.40 89.13 15.7
4 0013-MHZWF no femenino 0 no si 9.0 si no DSL no no no si si si mensual si tarjeta de credito (automatico) 69.4 571.45 63.49 0.0
Code
    #=====================================================================
                        #Verificación de los Datos
#=====================================================================
datos_churn.info()
# Configurar el tamaño de la figura
plt.figure(figsize=(15, 9))

# Crear los subplots
plt.subplot(1, 3, 1)
sns.boxplot(x='cuenta.cobros.Total', data=datos_churn)
plt.title('Boxplot de cuenta.cobros.Total')

plt.subplot(1, 3, 2)
sns.boxplot(x='cliente.tiempo_servicio', data=datos_churn)
plt.title('Boxplot de cliente.tiempo_servicio')

plt.subplot(1, 3, 3)
sns.boxplot(x='ratio_gasto_tiempo', data=datos_churn)
plt.title('Boxplot de cliente.tiempo_servicio')
plt.show()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6977 entries, 0 to 6976
Data columns (total 23 columns):
 #   Column                           Non-Null Count  Dtype  
---  ------                           --------------  -----  
 0   id_cliente                       6977 non-null   object 
 1   Churn                            6977 non-null   object 
 2   cliente.genero                   6977 non-null   object 
 3   cliente.anciano                  6977 non-null   int64  
 4   cliente.pareja                   6977 non-null   object 
 5   cliente.dependientes             6977 non-null   object 
 6   cliente.tiempo_servicio          6977 non-null   float64
 7   telefono.servicio_telefono       6977 non-null   object 
 8   telefono.varias_lineas           6977 non-null   object 
 9   internet.servicio_internet       6977 non-null   object 
 10  internet.seguridad_online        6977 non-null   object 
 11  internet.backup_online           6977 non-null   object 
 12  internet.proteccion_dispositivo  6977 non-null   object 
 13  internet.soporte_tecnico         6977 non-null   object 
 14  internet.tv_streaming            6977 non-null   object 
 15  internet.peliculas_streaming     6977 non-null   object 
 16  cuenta.contrato                  6977 non-null   object 
 17  cuenta.facturacion_electronica   6977 non-null   object 
 18  cuenta.metodo_pago               6977 non-null   object 
 19  cuenta.cobros.mensual            6977 non-null   float64
 20  cuenta.cobros.Total              6977 non-null   float64
 21  ratio_gasto_tiempo               6977 non-null   float64
 22  gasto_adicional                  6977 non-null   float64
dtypes: float64(5), int64(1), object(17)
memory usage: 1.2+ MB

3.2 Normalización de Datos

Code
#Observamos los valores unicos de cada columna

for col in datos_churn.columns:
    print(f"Columna: {col}")
    print(datos_churn[col].unique())
    print("-" * 30)
Columna: id_cliente
['0003-MKNFE' '0004-TLHLJ' '0011-IGKFF' ... '9992-UJOEL' '9993-LHIEB'
 '9995-HOTOH']
------------------------------
Columna: Churn
['no' 'si']
------------------------------
Columna: cliente.genero
['masculino' 'femenino']
------------------------------
Columna: cliente.anciano
[0 1]
------------------------------
Columna: cliente.pareja
['no' 'si']
------------------------------
Columna: cliente.dependientes
['no' 'si']
------------------------------
Columna: cliente.tiempo_servicio
[ 9.  4. 13.  3. 71. 63.  7. 54. 72.  5. 56. 34.  1. 45. 50. 23. 55. 26.
 69. 37. 49. 66. 67. 20. 43. 59. 12. 27.  2. 25. 29. 14. 35. 64. 39. 40.
 11.  6. 30. 70. 57. 58. 16. 32. 33. 10. 21. 61. 15. 44. 22. 24. 19. 47.
 62. 46. 52.  8. 60. 48. 28. 41. 53. 68. 31. 36. 17. 18. 65. 51. 38. 42.]
------------------------------
Columna: telefono.servicio_telefono
['si' 'no']
------------------------------
Columna: telefono.varias_lineas
['si' 'no' 'sin servicio de telefono']
------------------------------
Columna: internet.servicio_internet
['DSL' 'fibra optica' 'no']
------------------------------
Columna: internet.seguridad_online
['no' 'si' 'sin servicio de internet']
------------------------------
Columna: internet.backup_online
['no' 'si' 'sin servicio de internet']
------------------------------
Columna: internet.proteccion_dispositivo
['no' 'si' 'sin servicio de internet']
------------------------------
Columna: internet.soporte_tecnico
['no' 'si' 'sin servicio de internet']
------------------------------
Columna: internet.tv_streaming
['no' 'si' 'sin servicio de internet']
------------------------------
Columna: internet.peliculas_streaming
['si' 'no' 'sin servicio de internet']
------------------------------
Columna: cuenta.contrato
['mensual' 'dos años' 'un año']
------------------------------
Columna: cuenta.facturacion_electronica
['no' 'si']
------------------------------
Columna: cuenta.metodo_pago
['cheque' 'cheque electronico' 'tarjeta de credito (automatico)'
 'transferencia bancaria (automatica)']
------------------------------
Columna: cuenta.cobros.mensual
[59.9  73.9  98.   ... 91.75 68.8  67.85]
------------------------------
Columna: cuenta.cobros.Total
[ 542.4   280.85 1237.85 ...  742.9  4627.65 3707.6 ]
------------------------------
Columna: ratio_gasto_tiempo
[60.27 70.21 95.22 ... 46.38 69.07 58.85]
------------------------------
Columna: gasto_adicional
[ 3.3   0.   15.7  ... 33.3  25.95  1.5 ]
------------------------------
Code
datos_churn.drop('id_cliente',axis=1,inplace=True)
datos_churn.head()
Churn cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total ratio_gasto_tiempo gasto_adicional
0 no masculino 0 no no 9.0 si si DSL no no no no no si mensual no cheque 59.9 542.40 60.27 3.3
1 si masculino 0 no no 4.0 si no fibra optica no no si no no no mensual si cheque electronico 73.9 280.85 70.21 0.0
2 si masculino 1 si no 13.0 si no fibra optica no si si no si si mensual si cheque electronico 98.0 1237.85 95.22 0.0
3 si femenino 1 si no 3.0 si no fibra optica no no no si si no mensual si cheque 83.9 267.40 89.13 15.7
4 no femenino 0 no si 9.0 si no DSL no no no si si si mensual si tarjeta de credito (automatico) 69.4 571.45 63.49 0.0

Eliminamos la columna id ya que esta columna no se la podemos pasar al modelo.

Code
datos_churn['Churn']=datos_churn['Churn'].apply(lambda x: '0' if x == 'no' else '1').astype('int64')
datos_churn.head(1)
Churn cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total ratio_gasto_tiempo gasto_adicional
0 0 masculino 0 no no 9.0 si si DSL no no no no no si mensual no cheque 59.9 542.4 60.27 3.3

Convertimos nuestra variable CHURN

Code
datos_churn.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6977 entries, 0 to 6976
Data columns (total 22 columns):
 #   Column                           Non-Null Count  Dtype  
---  ------                           --------------  -----  
 0   Churn                            6977 non-null   int64  
 1   cliente.genero                   6977 non-null   object 
 2   cliente.anciano                  6977 non-null   int64  
 3   cliente.pareja                   6977 non-null   object 
 4   cliente.dependientes             6977 non-null   object 
 5   cliente.tiempo_servicio          6977 non-null   float64
 6   telefono.servicio_telefono       6977 non-null   object 
 7   telefono.varias_lineas           6977 non-null   object 
 8   internet.servicio_internet       6977 non-null   object 
 9   internet.seguridad_online        6977 non-null   object 
 10  internet.backup_online           6977 non-null   object 
 11  internet.proteccion_dispositivo  6977 non-null   object 
 12  internet.soporte_tecnico         6977 non-null   object 
 13  internet.tv_streaming            6977 non-null   object 
 14  internet.peliculas_streaming     6977 non-null   object 
 15  cuenta.contrato                  6977 non-null   object 
 16  cuenta.facturacion_electronica   6977 non-null   object 
 17  cuenta.metodo_pago               6977 non-null   object 
 18  cuenta.cobros.mensual            6977 non-null   float64
 19  cuenta.cobros.Total              6977 non-null   float64
 20  ratio_gasto_tiempo               6977 non-null   float64
 21  gasto_adicional                  6977 non-null   float64
dtypes: float64(5), int64(2), object(15)
memory usage: 1.2+ MB

3.2.2 División de los Datos

Code
features = datos_churn.drop('Churn', axis=1) #x
target = datos_churn['Churn'] #y
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.20, random_state=12345)
Code
X_test.head()
cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total ratio_gasto_tiempo gasto_adicional
5598 masculino 0 no no 47.0 si si fibra optica no si si no si si un año si cheque electronico 103.10 4889.30 104.03 43.6
5942 femenino 0 si no 57.0 si si fibra optica si no no no si si mensual si cheque electronico 99.65 5497.05 96.44 0.0
2149 femenino 0 si no 68.0 si si no sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet sin servicio de internet dos años no tarjeta de credito (automatico) 25.80 1911.50 28.11 157.1
4505 femenino 0 no no 6.0 si si fibra optica si no no no no no mensual si cheque electronico 79.05 434.50 72.42 0.0
28 masculino 0 si si 37.0 si no fibra optica no no no no si si un año no tarjeta de credito (automatico) 91.20 3247.55 87.77 0.0

3.2.3 Transformación de Variables

Code
columns_object=datos_churn.select_dtypes(include='object').columns
enc = OrdinalEncoder()
X_train[columns_object] = enc.fit_transform(X_train[columns_object]).astype('int64')
X_test[columns_object] = enc.transform(X_test[columns_object]).astype('int64')
X_test.head()
cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total ratio_gasto_tiempo gasto_adicional
5598 1 0 0 0 47.0 1 1 1 0 1 1 0 1 1 2 1 1 103.10 4889.30 104.03 43.6
5942 0 0 1 0 57.0 1 1 1 1 0 0 0 1 1 1 1 1 99.65 5497.05 96.44 0.0
2149 0 0 1 0 68.0 1 1 2 2 2 2 2 2 2 0 0 2 25.80 1911.50 28.11 157.1
4505 0 0 0 0 6.0 1 1 1 1 0 0 0 0 0 1 1 1 79.05 434.50 72.42 0.0
28 1 0 1 1 37.0 1 0 1 0 0 0 0 1 1 2 0 2 91.20 3247.55 87.77 0.0

3.2.3 Balanceo de Clases

Code
os_us = RandomOverSampler()
X_train_res, y_train_res = os_us.fit_resample(X_train, y_train)
resultado=pd.concat([X_train_res, y_train_res], axis=1)
resultado['Churn'].value_counts()
Churn
0    4110
1    4110
Name: count, dtype: int64

3.2.5 Escalamiento de los Datos

Code
columns=['cliente.tiempo_servicio','cuenta.cobros.mensual','cuenta.cobros.Total','ratio_gasto_tiempo', 'gasto_adicional']

min_max = MinMaxScaler()
X_train[columns] = min_max.fit_transform(X_train[columns])
X_test[columns] = min_max.transform(X_test[columns])
X_test.head()
cliente.genero cliente.anciano cliente.pareja cliente.dependientes cliente.tiempo_servicio telefono.servicio_telefono telefono.varias_lineas internet.servicio_internet internet.seguridad_online internet.backup_online internet.proteccion_dispositivo internet.soporte_tecnico internet.tv_streaming internet.peliculas_streaming cuenta.contrato cuenta.facturacion_electronica cuenta.metodo_pago cuenta.cobros.mensual cuenta.cobros.Total ratio_gasto_tiempo gasto_adicional
5598 1 0 0 0 0.647887 1 1 1 0 1 1 0 1 1 2 1 1 0.844279 0.562024 0.838524 0.118623
5942 0 0 1 0 0.788732 1 1 1 1 0 0 0 1 1 1 1 1 0.809950 0.632154 0.767965 0.000000
2149 0 0 1 0 0.943662 1 1 2 2 2 2 2 2 2 0 0 2 0.075124 0.218405 0.132751 0.427425
4505 0 0 0 0 0.070423 1 1 1 1 0 0 0 0 0 1 1 1 0.604975 0.047969 0.544669 0.000000
28 1 0 1 1 0.507042 1 0 1 0 0 0 0 1 1 2 0 2 0.725871 0.372577 0.687366 0.000000
Code
X_train.info()
<class 'pandas.core.frame.DataFrame'>
Index: 5581 entries, 4619 to 4578
Data columns (total 21 columns):
 #   Column                           Non-Null Count  Dtype  
---  ------                           --------------  -----  
 0   cliente.genero                   5581 non-null   int64  
 1   cliente.anciano                  5581 non-null   int64  
 2   cliente.pareja                   5581 non-null   int64  
 3   cliente.dependientes             5581 non-null   int64  
 4   cliente.tiempo_servicio          5581 non-null   float64
 5   telefono.servicio_telefono       5581 non-null   int64  
 6   telefono.varias_lineas           5581 non-null   int64  
 7   internet.servicio_internet       5581 non-null   int64  
 8   internet.seguridad_online        5581 non-null   int64  
 9   internet.backup_online           5581 non-null   int64  
 10  internet.proteccion_dispositivo  5581 non-null   int64  
 11  internet.soporte_tecnico         5581 non-null   int64  
 12  internet.tv_streaming            5581 non-null   int64  
 13  internet.peliculas_streaming     5581 non-null   int64  
 14  cuenta.contrato                  5581 non-null   int64  
 15  cuenta.facturacion_electronica   5581 non-null   int64  
 16  cuenta.metodo_pago               5581 non-null   int64  
 17  cuenta.cobros.mensual            5581 non-null   float64
 18  cuenta.cobros.Total              5581 non-null   float64
 19  ratio_gasto_tiempo               5581 non-null   float64
 20  gasto_adicional                  5581 non-null   float64
dtypes: float64(5), int64(16)
memory usage: 959.2 KB

3.3 Creación del Modelo Random Forest

Code
def pronosticar(train_x, train_y):
  model = RandomForestClassifier(random_state=50)
  model.fit(train_x, train_y)
  return model
Code
model = pronosticar(X_train, y_train)
model.score(X_test, y_test)
0.7951289398280802

Obtenemos 0.797 modelo totalmente limpio.

4. Reducción de Dimensionalidad

4.1 Métodos Gráficos

Code
columns_object=datos_churn.select_dtypes(include='object').columns
enc = OrdinalEncoder()
datos_churn[columns_object] = enc.fit_transform(datos_churn[columns_object]).astype('int64')
datos_churn.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6977 entries, 0 to 6976
Data columns (total 22 columns):
 #   Column                           Non-Null Count  Dtype  
---  ------                           --------------  -----  
 0   Churn                            6977 non-null   int64  
 1   cliente.genero                   6977 non-null   int64  
 2   cliente.anciano                  6977 non-null   int64  
 3   cliente.pareja                   6977 non-null   int64  
 4   cliente.dependientes             6977 non-null   int64  
 5   cliente.tiempo_servicio          6977 non-null   float64
 6   telefono.servicio_telefono       6977 non-null   int64  
 7   telefono.varias_lineas           6977 non-null   int64  
 8   internet.servicio_internet       6977 non-null   int64  
 9   internet.seguridad_online        6977 non-null   int64  
 10  internet.backup_online           6977 non-null   int64  
 11  internet.proteccion_dispositivo  6977 non-null   int64  
 12  internet.soporte_tecnico         6977 non-null   int64  
 13  internet.tv_streaming            6977 non-null   int64  
 14  internet.peliculas_streaming     6977 non-null   int64  
 15  cuenta.contrato                  6977 non-null   int64  
 16  cuenta.facturacion_electronica   6977 non-null   int64  
 17  cuenta.metodo_pago               6977 non-null   int64  
 18  cuenta.cobros.mensual            6977 non-null   float64
 19  cuenta.cobros.Total              6977 non-null   float64
 20  ratio_gasto_tiempo               6977 non-null   float64
 21  gasto_adicional                  6977 non-null   float64
dtypes: float64(5), int64(17)
memory usage: 1.2 MB

4.1.1 Reducción Gráfico de Líneas

Code
df_int=datos_churn[datos_churn.select_dtypes(include='int').columns]

df_melted = df_int.melt(id_vars='Churn', var_name='Variables', value_name='Valores')

# Grafico con Seaborn
plt.figure(figsize=(12, 6))
sns.lineplot(data=df_melted, x='Variables', y='Valores', hue='Churn', marker='o')

plt.xticks(rotation=90)  # Rotar etiquetas del eje x
plt.ylim(df_melted['Valores'].min(), df_melted['Valores'].max())
plt.title('Comparación de variables por Churn')
plt.show()

Tenemos que la columna telefono.varias_lineas y internet.servicio_internet tienen mucha correlación.

Code
train_x = X_train.drop(columns=["telefono.varias_lineas",'internet.servicio_internet'])
test_x = X_test.drop(columns=["telefono.varias_lineas",'internet.servicio_internet'])
model = pronosticar(train_x, y_train)
model.score(test_x, y_test)
0.8058739255014327

4.1.2 Reducción Gráficos Variables continuas

Code
min_max_gra=X_test.loc[:,['cliente.tiempo_servicio','cuenta.cobros.mensual','cuenta.cobros.Total','ratio_gasto_tiempo', 'gasto_adicional']]

fig=plt.figure(figsize=(15,3))
ax1=fig.add_subplot(1,5,1)
ax2=fig.add_subplot(1,5,2)
ax3=fig.add_subplot(1,5,3)
ax4=fig.add_subplot(1,5,4)
ax5=fig.add_subplot(1,5,5)

ax1.set_title("cliente.tiempo_servicio")
ax1.plot(min_max_gra['cliente.tiempo_servicio'],linewidth=0,marker='*',color='red',markersize=4)


ax2.set_title("cuenta.cobros.mensual")
ax2.plot(min_max_gra['cuenta.cobros.mensual'],linewidth=0,marker='*',color='red',markersize=4)

ax3.set_title("cuenta.cobros.Total")
ax3.plot(min_max_gra['cuenta.cobros.Total'],linewidth=0,marker='*',color='red',markersize=4)

ax4.set_title("ratio_gasto_tiempo")
ax4.plot(min_max_gra['ratio_gasto_tiempo'],linewidth=0,marker='*',color='red',markersize=4)

ax5.set_title("gasto_adicional")
ax5.plot(min_max_gra['gasto_adicional'],linewidth=0,marker='*',color='red',markersize=4)

plt.show()



fig=plt.figure(figsize=(15,3))
ax1=fig.add_subplot(1,5,1)
ax2=fig.add_subplot(1,5,2)
ax3=fig.add_subplot(1,5,3)
ax4=fig.add_subplot(1,5,4)
ax5=fig.add_subplot(1,5,5)

ax1.set_title("cliente.tiempo_servicio")
ax1.hist(min_max_gra['cliente.tiempo_servicio'],color='red',bins=100)


ax2.set_title("cuenta.cobros.mensual")
ax2.hist(min_max_gra['cuenta.cobros.mensual'],color='red',bins=100)

ax3.set_title("cuenta.cobros.Total")
ax3.hist(min_max_gra['cuenta.cobros.Total'],color='red',bins=100)

ax4.set_title("ratio_gasto_tiempo")
ax4.hist(min_max_gra['ratio_gasto_tiempo'],color='red',bins=100)

ax5.set_title("gasto_adicional")
ax5.hist(min_max_gra['gasto_adicional'],color='red',bins=100)

plt.show()

Code
train_x = X_train.drop(columns=['cuenta.cobros.Total','gasto_adicional'])
test_x = X_test.drop(columns=['cuenta.cobros.Total','gasto_adicional'])
model = pronosticar(train_x, y_train)
model.score(test_x, y_test)
0.8087392550143266

4.1.3 Reducción Mapa de Calor

Code
def mapa_calor(df):
    grafico = df.corr()
    plt.figure( figsize = (17,15))
    sns.heatmap(grafico, annot = True, fmt = ".1f")
    return grafico

matriz = mapa_calor(datos_churn)

Code
matriz2 = matriz>0.80
matriz2 = matriz2.sum()
matriz2[matriz2>1]
cliente.tiempo_servicio         2
internet.tv_streaming           2
internet.peliculas_streaming    2
cuenta.cobros.mensual           2
cuenta.cobros.Total             2
ratio_gasto_tiempo              2
dtype: int64
Code
train_x = X_train.drop(columns=["internet.tv_streaming",'internet.peliculas_streaming'])
test_x = X_test.drop(columns=["internet.tv_streaming",'internet.peliculas_streaming'])
model = pronosticar(train_x, y_train)
model.score(test_x, y_test)
0.8008595988538681

5. Features Importances

Implementamos métodos de selección de características usando SelectKBest, RFE y PCA para reducir la dimensionalidad del conjunto de datos.

5.1 SelectKbest

Este método selecciona las mejores características según su relevancia estadística usando la prueba chi-cuadrado. Es útil cuando se trabaja con datos categóricos y selecciona las características más relevantes para el modelo.

Code
def pronosticar_kbest(train_x, test_x, train_y, test_y):
    start_time = time.time()
  
    train_x = np.abs(train_x)
    test_x = np.abs(test_x)

    selector = SelectKBest(score_func=chi2, k=18)
    train_x_selected = selector.fit_transform(train_x, train_y)
    test_x_selected = selector.transform(test_x)

    model = RandomForestClassifier(random_state=1234)
    model.fit(train_x_selected, train_y)
    test_score = model.score(test_x_selected, test_y)

    print(f"Accuracy en prueba con SelectKBest y chi2: {test_score}")
    end_time = time.time()
    print(f"Tiempo de ejecución: {end_time - start_time:.4f} segundos")
    
pronosticar_kbest(X_train, X_test, y_train, y_test)
Accuracy en prueba con SelectKBest y chi2: 0.8044412607449857
Tiempo de ejecución: 0.7632 segundos

5.2 RFE (Recursive Feature Elimination)

Este algoritmo selecciona características eliminando recursivamente las menos importantes según el modelo. Usa un clasificador (en este caso, RandomForest) para identificar y eliminar las características menos relevantes, dejando solo las más útiles.

Code
def pronosticar_RFE(train_x, test_x, train_y, test_y):
    start_time = time.time()
    model = RandomForestClassifier(random_state=50)
    mejor_n = 0
    mejor_score = 0
    mejores_columnas = []

    for n in range(1, train_x.shape[1] + 1):
        rfe = RFE(estimator=model, n_features_to_select=n)
        rfe.fit(train_x, train_y)
        train_x_rfe = rfe.transform(train_x)
        
        # Calcular el score con validación cruzada
        scores = cross_val_score(model, train_x_rfe, train_y, cv=5, scoring='accuracy')
        score = scores.mean()

        if score > mejor_score:
            mejor_n = n
            mejor_score = score
            mejores_columnas = list(train_x.columns[rfe.support_])
    
    # Imprimir resultados
    print(f"Mejor número de características: {mejor_n}")
    print(f"Mejor accuracy: {mejor_score:.4f}")
    print("Mejores columnas seleccionadas:", mejores_columnas)
    
    # Entrenar el modelo final con las mejores características
    mejor_model = RandomForestClassifier(random_state=50)
    mejor_model.fit(train_x[mejores_columnas], train_y)

    # Evaluar en el conjunto de prueba
    test_x_rfe = test_x[mejores_columnas]
    test_accuracy = mejor_model.score(test_x_rfe, test_y)
    print(f"Accuracy en el conjunto de prueba: {test_accuracy:.4f}")
       # Calcular tiempo de ejecución
    end_time = time.time()
    print(f"Tiempo de ejecución: {end_time - start_time:.4f} segundos")
    
pronosticar_RFE(X_train, X_test, y_train, y_test)
Mejor número de características: 21
Mejor accuracy: 0.7922
Mejores columnas seleccionadas: ['cliente.genero', 'cliente.anciano', 'cliente.pareja', 'cliente.dependientes', 'cliente.tiempo_servicio', 'telefono.servicio_telefono', 'telefono.varias_lineas', 'internet.servicio_internet', 'internet.seguridad_online', 'internet.backup_online', 'internet.proteccion_dispositivo', 'internet.soporte_tecnico', 'internet.tv_streaming', 'internet.peliculas_streaming', 'cuenta.contrato', 'cuenta.facturacion_electronica', 'cuenta.metodo_pago', 'cuenta.cobros.mensual', 'cuenta.cobros.Total', 'ratio_gasto_tiempo', 'gasto_adicional']
Accuracy en el conjunto de prueba: 0.7951
Tiempo de ejecución: 377.8390 segundos

5.3 PCA (Principal Component Analysis):

Este método transforma las características originales en un conjunto de nuevas variables no correlacionadas llamadas componentes principales, que capturan la mayor parte de la variabilidad de los datos, reduciendo la dimensionalidad sin perder mucha información.

Code
def pronosticar_PCA(train_x, test_x, train_y, test_y,):
    n = 12
    pca = PCA(n_components=n)

    # Ajustar y transformar los datos con PCA
    train_x_pca = pca.fit_transform(train_x)
    model = RandomForestClassifier(random_state=50)
    model.fit(train_x_pca, train_y)
    test_x_pca = pca.transform(test_x)

    # Imprimir resultados de PCA
    print("Utilizando PCA:")
    print(f"El número de características seleccionadas es {n} con un score de {model.score(test_x_pca, test_y) * 100:.2f}%")

    # Calcular y mostrar el total de varianza explicada
    total_varianza_explicada = sum(pca.explained_variance_ratio_)
    print(f"Total de varianza explicada por el PCA: {total_varianza_explicada * 100:.2f}%")

    # Visualizar el explained variance ratio
    plt.figure(figsize=(10, 6))
    plt.bar(range(1, n + 1), pca.explained_variance_ratio_, alpha=0.7, color='blue')
    plt.ylabel('Explained Variance Ratio')
    plt.xlabel('Componentes Principales')
    plt.title('Proporción de Varianza Explicada por Componente Principal')
    plt.xticks(np.arange(1, n + 1, 1))
    plt.grid(axis='y', linestyle='--', alpha=0.7)
    plt.show()

    # Obtener las cargas de los componentes
    loadings = pca.components_.T * np.sqrt(pca.explained_variance_)
    component_names = [f'PC{i+1}' for i in range(n)]
    loadings_df = pd.DataFrame(loadings, index=train_x.columns, columns=component_names)

    # Imprimir las cargas de los componentes
    print("Cargas de los Componentes:")
    print(loadings_df.round(3))
    
pronosticar_PCA(X_train, X_test, y_train, y_test)
Utilizando PCA:
El número de características seleccionadas es 12 con un score de 77.58%
Total de varianza explicada por el PCA: 93.10%
Cargas de los Componentes:
                                   PC1    PC2    PC3    PC4    PC5    PC6  \
cliente.genero                   0.001 -0.011  0.007  0.031 -0.010  0.030   
cliente.anciano                 -0.069  0.016 -0.021  0.005 -0.134 -0.025   
cliente.pareja                   0.035  0.133  0.138 -0.027 -0.042  0.399   
cliente.dependientes             0.081  0.048  0.109 -0.019  0.092  0.311   
cliente.tiempo_servicio          0.048  0.172  0.118 -0.009 -0.058  0.101   
telefono.servicio_telefono       0.064 -0.003 -0.177 -0.145 -0.034  0.067   
telefono.varias_lineas          -0.197  0.128  0.464  0.332 -0.160 -0.108   
internet.servicio_internet       0.585 -0.026 -0.204 -0.066 -0.244 -0.024   
internet.seguridad_online        0.696  0.022  0.056  0.032  0.197  0.001   
internet.backup_online           0.674  0.038  0.028  0.040  0.037  0.011   
internet.proteccion_dispositivo  0.688  0.048  0.044  0.048 -0.018  0.005   
internet.soporte_tecnico         0.705  0.024  0.067  0.023  0.143 -0.029   
internet.tv_streaming            0.668  0.028 -0.006  0.040 -0.175  0.001   
internet.peliculas_streaming     0.665  0.035 -0.008  0.047 -0.171 -0.017   
cuenta.contrato                 -0.118 -0.119 -0.384  0.501  0.033  0.141   
cuenta.facturacion_electronica  -0.151  0.027 -0.049 -0.017 -0.265  0.017   
cuenta.metodo_pago              -0.090  1.050 -0.153  0.021  0.064 -0.068   
cuenta.cobros.mensual           -0.175  0.059 -0.035 -0.053 -0.143  0.064   
cuenta.cobros.Total             -0.039  0.114  0.055 -0.017 -0.090  0.084   
ratio_gasto_tiempo              -0.163  0.055 -0.032 -0.049 -0.133  0.059   
gasto_adicional                 -0.005  0.018  0.008  0.002 -0.014  0.015   

                                   PC7    PC8    PC9   PC10   PC11   PC12  
cliente.genero                  -0.497  0.002 -0.018 -0.002  0.006  0.009  
cliente.anciano                 -0.005 -0.026  0.107  0.050  0.107  0.019  
cliente.pareja                   0.021  0.006  0.054 -0.017  0.061  0.008  
cliente.dependientes             0.011  0.030 -0.050 -0.179 -0.044 -0.045  
cliente.tiempo_servicio          0.001 -0.034  0.015  0.177 -0.010  0.049  
telefono.servicio_telefono      -0.003 -0.007  0.018  0.062  0.004  0.027  
telefono.varias_lineas           0.010 -0.004  0.061 -0.059  0.027  0.018  
internet.servicio_internet      -0.010 -0.005  0.178 -0.166  0.099  0.010  
internet.seguridad_online        0.009 -0.098 -0.002  0.012  0.196  0.165  
internet.backup_online          -0.013 -0.245  0.172  0.025 -0.208 -0.052  
internet.proteccion_dispositivo -0.003  0.065 -0.031  0.125  0.120 -0.287  
internet.soporte_tecnico         0.004 -0.036 -0.138  0.034 -0.012  0.059  
internet.tv_streaming            0.006  0.132 -0.084 -0.032 -0.110  0.057  
internet.peliculas_streaming     0.014  0.134 -0.087  0.008 -0.058  0.070  
cuenta.contrato                  0.023 -0.007 -0.005  0.039  0.002  0.012  
cuenta.facturacion_electronica   0.015 -0.277 -0.243 -0.057  0.047 -0.037  
cuenta.metodo_pago              -0.009  0.011 -0.006 -0.044 -0.001 -0.010  
cuenta.cobros.mensual            0.003  0.018  0.014  0.092 -0.014  0.042  
cuenta.cobros.Total              0.002 -0.015  0.012  0.151 -0.015  0.046  
ratio_gasto_tiempo               0.003  0.017  0.013  0.087 -0.013  0.040  
gasto_adicional                  0.002 -0.001 -0.001  0.028  0.001  0.011