0.1 Introducción

Problematica:

Las empresas de telecomunicaciones tienen problemas con la fuga de sus clientes a la competencia, es por esto, que la empresa busca implementar un modelo de clasificacion que les permita identificar a aquellos clientes que se van a fugar, de manera que puedan gestionar con algun mecanismo de retencion/fidelizacion de aquellos clientes. La empresa nos proporciono un conjunto de datos de datos churn-analysis.csv con informacion que nos puede ser de utilidad para identificar patrones de comportamiento de clientes que se van a fugar.

La descripcion de las columnas de la base de datos son:

state: Region del usuario.

area.code: Codigo de area.

phone.number: Numero telefonico.

international.plan: Plan internacional (yes o no).

voice.mail.plan: Plan con correo de voz (yes o no).

number.vmail.messages: Cantidad de mensajes virtuales posee.

total.day.minutes: Cantidad de minutos diarios.

total.day.calls: Cantidad de llamadas diarias.

total.day.charge: Cantidad del costo diario.

total.eve.minutes: Cantidad de minutos en la tarde.

total.eve.calls: Cantidad de llamadas en la tarde.

total.eve.charge: Cantidad de costo en la tarde.

total.night.minutes: Cantidad de minutos en la noche.

total.night.calls: Cantidad de llamadas en la noche.

total.night.charge: Cantidad de costo en la noche.

total.intl.minutes: Cantidad de minutos internacionales.

total.intl.calls: Cantidad de llamadas internacionales.

total.intl.charge: Cantidad de costo internacionales.

customer.service.calls: Cantidad de llamados a la mesa de ayuda.

churn: Fuga del cliente (True o False).

0.2 Metodología

El objetivo de este análisis fue desarrollar un modelo predictivo capaz de identificar a los clientes en riesgo de abandonar una empresa de telecomunicaciones. La metodología seguida para alcanzar este objetivo se dividió en varias fases clave, que se detallan a continuación:

  1. Comprensión del Problema: Se inició con una comprensión clara del problema empresarial y los objetivos del análisis. Esto incluyó entender la importancia de predecir la fuga de clientes y cómo esto puede impactar en la rentabilidad y sustentabilidad de la empresa.

  2. Preparación de Datos: Los datos fueron cargados y examinados para entender su estructura y contenido. Esto incluyó la identificación de las variables disponibles y sus tipos, así como una inspección inicial de posibles valores faltantes o atípicos.

  3. Análisis Exploratorio de Datos (AED): Se llevó a cabo un AED para resumir las principales características del conjunto de datos mediante resúmenes estadísticos y visualizaciones, con el fin de captar tendencias, patrones y relaciones entre las variables.

  4. Preprocesamiento de Datos: Se aplicaron técnicas de preprocesamiento como la conversión de variables categóricas en factores, la normalización de variables numéricas y la imputación de valores faltantes donde fue necesario.

  5. Evaluación de Datos Atípicos: Se implementó una estrategia para manejar datos atípicos, con el fin de minimizar su impacto en el modelo.

  6. Construcción y Selección del Modelo: Se construyeron varios modelos de clasificación, incluyendo Regresión Logística, Árboles de Decisión, Random Forest y Máquinas de Vectores de Soporte (SVM). Luego, se seleccionó el mejor modelo basado en su precisión y capacidad predictiva, utilizando la métrica AUC como referencia principal.

  7. Validación del Modelo: Se validó el modelo utilizando un conjunto de datos de prueba para evaluar su rendimiento en datos no vistos anteriormente. Se calculó la precisión y el AUC para cuantificar la capacidad predictiva del modelo.

  8. Interpretación y Conclusión: Se interpretaron los resultados en el contexto del problema empresarial y se formularon recomendaciones basadas en las conclusiones obtenidas del análisis.

Cada fase del proyecto fue documentada detalladamente para asegurar la transparencia y reproducibilidad del análisis. Además, se adoptó un enfoque iterativo para la mejora continua del modelo, considerando los comentarios y los resultados obtenidos en cada etapa del proceso.

1 - Análisis Exploratorio de Datos

# Leer los datos
df <- read.csv("churn-analysis.csv", header = TRUE, sep = ";")

#### 1- Análisis Exploratorio ####

# Estructura de los datos
print("Estructura de los datos:")
## [1] "Estructura de los datos:"
str(df)
## 'data.frame':    3333 obs. of  20 variables:
##  $ state                 : chr  "KS" "OH" "NJ" "OH" ...
##  $ area.code             : int  415 415 415 408 415 510 510 415 408 415 ...
##  $ phone.number          : chr  "382-4657" "371-7191" "358-1921" "375-9999" ...
##  $ international.plan    : chr  "no" "no" "no" "yes" ...
##  $ voice.mail.plan       : chr  "yes" "yes" "no" "no" ...
##  $ number.vmail.messages : int  25 26 0 0 0 0 24 0 0 37 ...
##  $ total.day.minutes     : num  265 162 243 299 167 ...
##  $ total.day.calls       : int  110 123 114 71 113 98 88 79 97 84 ...
##  $ total.day.charge      : num  45.1 27.5 41.4 50.9 28.3 ...
##  $ total.eve.minutes     : num  197.4 195.5 121.2 61.9 148.3 ...
##  $ total.eve.calls       : int  99 103 110 88 122 101 108 94 80 111 ...
##  $ total.eve.charge      : num  16.78 16.62 10.3 5.26 12.61 ...
##  $ total.night.minutes   : num  245 254 163 197 187 ...
##  $ total.night.calls     : int  91 103 104 89 121 118 118 96 90 97 ...
##  $ total.night.charge    : num  11.01 11.45 7.32 8.86 8.41 ...
##  $ total.intl.minutes    : num  10 13.7 12.2 6.6 10.1 6.3 7.5 7.1 8.7 11.2 ...
##  $ total.intl.calls      : int  3 3 5 7 3 6 7 6 4 5 ...
##  $ total.intl.charge     : num  2.7 3.7 3.29 1.78 2.73 1.7 2.03 1.92 2.35 3.02 ...
##  $ customer.service.calls: int  1 1 0 2 3 0 3 0 1 0 ...
##  $ churn                 : chr  "False" "False" "False" "False" ...
# Resumen estadístico
print("Resumen estadístico:")
## [1] "Resumen estadístico:"
summary(df)
##     state             area.code     phone.number       international.plan
##  Length:3333        Min.   :408.0   Length:3333        Length:3333       
##  Class :character   1st Qu.:408.0   Class :character   Class :character  
##  Mode  :character   Median :415.0   Mode  :character   Mode  :character  
##                     Mean   :437.2                                        
##                     3rd Qu.:510.0                                        
##                     Max.   :510.0                                        
##  voice.mail.plan    number.vmail.messages total.day.minutes total.day.calls
##  Length:3333        Min.   : 0.000        Min.   :  0.0     Min.   :  0.0  
##  Class :character   1st Qu.: 0.000        1st Qu.:143.7     1st Qu.: 87.0  
##  Mode  :character   Median : 0.000        Median :179.4     Median :101.0  
##                     Mean   : 8.099        Mean   :179.8     Mean   :100.4  
##                     3rd Qu.:20.000        3rd Qu.:216.4     3rd Qu.:114.0  
##                     Max.   :51.000        Max.   :350.8     Max.   :165.0  
##  total.day.charge total.eve.minutes total.eve.calls total.eve.charge
##  Min.   : 0.00    Min.   :  0.0     Min.   :  0.0   Min.   : 0.00   
##  1st Qu.:24.43    1st Qu.:166.6     1st Qu.: 87.0   1st Qu.:14.16   
##  Median :30.50    Median :201.4     Median :100.0   Median :17.12   
##  Mean   :30.56    Mean   :201.0     Mean   :100.1   Mean   :17.08   
##  3rd Qu.:36.79    3rd Qu.:235.3     3rd Qu.:114.0   3rd Qu.:20.00   
##  Max.   :59.64    Max.   :363.7     Max.   :170.0   Max.   :30.91   
##  total.night.minutes total.night.calls total.night.charge total.intl.minutes
##  Min.   : 23.2       Min.   : 33.0     Min.   : 1.040     Min.   : 0.00     
##  1st Qu.:167.0       1st Qu.: 87.0     1st Qu.: 7.520     1st Qu.: 8.50     
##  Median :201.2       Median :100.0     Median : 9.050     Median :10.30     
##  Mean   :200.9       Mean   :100.1     Mean   : 9.039     Mean   :10.24     
##  3rd Qu.:235.3       3rd Qu.:113.0     3rd Qu.:10.590     3rd Qu.:12.10     
##  Max.   :395.0       Max.   :175.0     Max.   :17.770     Max.   :20.00     
##  total.intl.calls total.intl.charge customer.service.calls    churn          
##  Min.   : 0.000   Min.   :0.000     Min.   :0.000          Length:3333       
##  1st Qu.: 3.000   1st Qu.:2.300     1st Qu.:1.000          Class :character  
##  Median : 4.000   Median :2.780     Median :1.000          Mode  :character  
##  Mean   : 4.479   Mean   :2.765     Mean   :1.563                            
##  3rd Qu.: 6.000   3rd Qu.:3.270     3rd Qu.:2.000                            
##  Max.   :20.000   Max.   :5.400     Max.   :9.000
# Verificar valores faltantes
print("Valores faltantes por columna:")
## [1] "Valores faltantes por columna:"
colSums(is.na(df))
##                  state              area.code           phone.number 
##                      0                      0                      0 
##     international.plan        voice.mail.plan  number.vmail.messages 
##                      0                      0                      0 
##      total.day.minutes        total.day.calls       total.day.charge 
##                      0                      0                      0 
##      total.eve.minutes        total.eve.calls       total.eve.charge 
##                      0                      0                      0 
##    total.night.minutes      total.night.calls     total.night.charge 
##                      0                      0                      0 
##     total.intl.minutes       total.intl.calls      total.intl.charge 
##                      0                      0                      0 
## customer.service.calls                  churn 
##                      0                      0
# Visualización básica
ggplot(df, aes(x = churn)) + 
  geom_bar() + 
  labs(title = "Distribución de Churn", x = "Churn", y = "Frecuencia")

##### Análisis de Correlaciones ####

# Convertir primero las variables character a factor
df <- df %>% mutate(across(where(is.character), as.factor))

# Luego convertir todas las variables categóricas (ahora factors) a numéricas
df_numeric <- df %>% mutate(across(where(is.factor), as.numeric))

# Calcular la matriz de correlación
correlaciones <- cor(df_numeric, use = "complete.obs")

# Visualizar la matriz de correlación
corrplot(correlaciones, method = "circle")

2 -Datos atipicos

##### AIdentificación de Variables con Baja Correlación con 'churn'  ####

# Establecer un umbral de correlación (por ejemplo, 0.05)
umbral <- 0.05

# Obtener correlaciones de todas las variables con 'churn'
correlaciones_churn <- correlaciones["churn", ]

# Identificar variables con correlación absoluta menor que el umbral
variables_baja_corr <- names(correlaciones_churn[abs(correlaciones_churn) < umbral])

# Mostrar variables con baja correlación con 'churn'
print(variables_baja_corr)
## [1] "state"               "area.code"           "phone.number"       
## [4] "total.day.calls"     "total.eve.calls"     "total.night.minutes"
## [7] "total.night.calls"   "total.night.charge"
##### Exclusión de Variables con Baja Correlación #####

# Excluir variables con baja correlación de df
df_reducido <- df %>% select(-all_of(variables_baja_corr))

# Ver la estructura del nuevo DataFrame
str(df_reducido)
## 'data.frame':    3333 obs. of  12 variables:
##  $ international.plan    : Factor w/ 2 levels "no","yes": 1 1 1 2 2 2 1 2 1 2 ...
##  $ voice.mail.plan       : Factor w/ 2 levels "no","yes": 2 2 1 1 1 1 2 1 1 2 ...
##  $ number.vmail.messages : int  25 26 0 0 0 0 24 0 0 37 ...
##  $ total.day.minutes     : num  265 162 243 299 167 ...
##  $ total.day.charge      : num  45.1 27.5 41.4 50.9 28.3 ...
##  $ total.eve.minutes     : num  197.4 195.5 121.2 61.9 148.3 ...
##  $ total.eve.charge      : num  16.78 16.62 10.3 5.26 12.61 ...
##  $ total.intl.minutes    : num  10 13.7 12.2 6.6 10.1 6.3 7.5 7.1 8.7 11.2 ...
##  $ total.intl.calls      : int  3 3 5 7 3 6 7 6 4 5 ...
##  $ total.intl.charge     : num  2.7 3.7 3.29 1.78 2.73 1.7 2.03 1.92 2.35 3.02 ...
##  $ customer.service.calls: int  1 1 0 2 3 0 3 0 1 0 ...
##  $ churn                 : Factor w/ 2 levels "False","True": 1 1 1 1 1 1 1 1 1 1 ...
print("Valores faltantes en df_reducido:")
## [1] "Valores faltantes en df_reducido:"
colSums(is.na(df_reducido))
##     international.plan        voice.mail.plan  number.vmail.messages 
##                      0                      0                      0 
##      total.day.minutes       total.day.charge      total.eve.minutes 
##                      0                      0                      0 
##       total.eve.charge     total.intl.minutes       total.intl.calls 
##                      0                      0                      0 
##      total.intl.charge customer.service.calls                  churn 
##                      0                      0                      0
# identificación de datos atípicos para 'total.day.minutes'
boxplot(df_reducido$total.day.minutes, main = "Boxplot - Total Day Minutes")

# Función para identificar outliers basada en el IQR

find_outliers <- function(data) {
  Q1 <- quantile(data, 0.25)
  Q3 <- quantile(data, 0.75)
  IQR <- Q3 - Q1
  return(data < (Q1 - 1.5 * IQR) | data > (Q3 + 1.5 * IQR))
}

# Aplicar la función a cada columna numérica y obtener un data frame de outliers
outliers <- df_reducido %>% 
  select_if(is.numeric) %>% 
  map_df(~find_outliers(.))

# Ver un sumario de los outliers por columna
summary_outliers <- colSums(outliers)
print(summary_outliers)
##  number.vmail.messages      total.day.minutes       total.day.charge 
##                      1                     25                     25 
##      total.eve.minutes       total.eve.charge     total.intl.minutes 
##                     24                     24                     46 
##       total.intl.calls      total.intl.charge customer.service.calls 
##                     78                     49                    267
#  Eliminar filas con outliers
df_sin_outliers <- df_reducido[!rowSums(outliers), ]

#df_sin_outliers

3 - Modelo propuesto

#### 3- Modelo propuesto #####
 
 
# Dividir los datos en conjuntos de entrenamiento y prueba
set.seed(123)
indices <- createDataPartition(df_sin_outliers$churn, p = 0.8, list = FALSE)
train_data <- df_sin_outliers[indices,]
test_data <- df_sin_outliers[-indices,]

 

# Preparar la receta de preprocesamiento
receta <- recipe(churn ~ ., data = train_data) %>%
  step_dummy(all_nominal(), -all_outcomes()) %>%
  step_center(all_predictors(), -all_outcomes()) %>%
  step_scale(all_predictors(), -all_outcomes())

# Aplicar la receta de preprocesamiento
train_data_prep <- prep(receta, training = train_data)
train_data_processed <- bake(train_data_prep, new_data = train_data)
test_data_processed <- bake(train_data_prep, new_data = test_data)

# Entrenar varios modelos y evaluarlos
modelos <- list()

# Modelo de Regresión Logística
modelos$log_reg <- train(churn ~ ., data = train_data_processed, method = "glm", family = "binomial")

# Árbol de Decisión
modelos$arbol_dec <- train(churn ~ ., data = train_data_processed, method = "rpart")

# Random Forest
modelos$random_forest <- train(churn ~ ., data = train_data_processed, method = "rf")

# SVM (Support Vector Machine)
modelos$svm <- train(churn ~ ., data = train_data_processed, method = "svmRadial")

# Evaluar modelos
resultados <- lapply(modelos, function(modelo) {
  predicciones <- predict(modelo, newdata = test_data_processed)
  prob_predicciones <- predict(modelo, newdata = test_data_processed, type = "prob")[,2]
  accuracy <- mean(predicciones == test_data_processed$churn)
  
  list(accuracy = accuracy)
})
## Warning in method$prob(modelFit = modelFit, newdata = newdata, submodels =
## param): kernlab class probability calculations failed; returning NAs
resultados
## $log_reg
## $log_reg$accuracy
## [1] 0.9119171
## 
## 
## $arbol_dec
## $arbol_dec$accuracy
## [1] 0.9360967
## 
## 
## $random_forest
## $random_forest$accuracy
## [1] 0.955095
## 
## 
## $svm
## $svm$accuracy
## [1] 0.9360967
##### modelo propuesto -- random_forest-- #####
#random_forest
#[1] 0.955095

4 - ramdom_forest

#### 4- ramdom_forest ####

# Evaluar el modelo Random Forest y calcular Accuracy y AUC
modelo_rf <- modelos$random_forest
predicciones_rf <- predict(modelo_rf, newdata = test_data_processed)
prob_predicciones_rf <- predict(modelo_rf, newdata = test_data_processed, type = "prob")[,2]

# Asegúrate de que churn está en los mismos niveles y orden que las predicciones
test_data_churn_factor <- factor(test_data_processed$churn, levels = levels(predicciones_rf))

# Calcula la precisión
accuracy_rf <- mean(predicciones_rf == test_data_churn_factor)

# Calcula el AUC
if (is.numeric(prob_predicciones_rf)) {
  roc_curve_rf <- roc(response = test_data_churn_factor, predictor = prob_predicciones_rf)
  auc_rf <- auc(roc_curve_rf)
} else {
  warning("Las probabilidades predichas no son numéricas para Random Forest.")
  auc_rf <- NA
}
## Setting levels: control = False, case = True
## Setting direction: controls < cases
# Imprimir los resultados para Random Forest
print(paste("Accuracy para Random Forest:", accuracy_rf))
## [1] "Accuracy para Random Forest: 0.955094991364421"
print(paste("AUC para Random Forest:", auc_rf))
## [1] "AUC para Random Forest: 0.840454657315124"
# Opcional: Visualizar la curva ROC
plot(roc_curve_rf, main = "Curva ROC - Random Forest")

5 - Conclusion (2 puntos): Se comentan los resultados obtenidos en funcion del contexto del problema.

El análisis de la fuga de clientes realizado mediante el modelo de Random Forest ha proporcionado resultados prometedores. La precisión del modelo, un impresionante 95.5%, junto con un AUC de 0.840, indica una fuerte capacidad predictiva. Estas métricas sugieren que el modelo es bastante confiable en la identificación correcta de los clientes que podrían abandonar la empresa.

La elevada precisión indica que el modelo acertó en la gran mayoría de las predicciones realizadas sobre el conjunto de prueba. Por otro lado, el AUC cercano a 1 demuestra que el modelo tiene una alta capacidad para distinguir entre los clientes que se fugan y los que no. Esto es crucial para la empresa, ya que le permite enfocar sus esfuerzos de retención y fidelización de manera más efectiva, destinando recursos de forma eficiente a aquellos clientes identificados como de alto riesgo de fuga.

Cabe mencionar que, aunque el modelo funciona bien con los datos actuales, es importante continuar con la validación y ajuste del modelo a medida que se disponga de nuevos datos. El comportamiento del cliente puede cambiar con el tiempo, y el modelo puede necesitar ajustes para mantener su precisión.

En conclusión, el modelo de Random Forest se muestra como una herramienta valiosa para la toma de decisiones estratégicas en la retención de clientes. Implementar este modelo podría traducirse en una mejora significativa en la gestión de la relación con los clientes y, en última instancia, en la rentabilidad de la empresa.