SVM Churn — Interpretación Completa
Bank Churn · Kaggle · SVM + GridSearchCV

Qué hicimos, por qué
y qué nos dicen los resultados

Interpretación técnica y detallada del notebook completo. Cada decisión, cada salida, cada número explicado desde cero.

10.000
Clientes
14
Features finales
20,38%
Tasa de churn
0.8622
AUC modelo
00
¿Qué estamos intentando resolver?
El problema de negocio antes de tocar código

El punto de partida es un problema de negocio concreto: un banco quiere saber qué clientes están en riesgo de abandonar su servicio antes de que lo hagan. Esto es lo que se llama predicción de churn, y tiene un valor económico directo: retener a un cliente existente es mucho más barato que captar uno nuevo.

Para resolverlo usamos un algoritmo de Machine Learning llamado SVM (Support Vector Machine). La idea conceptual es la siguiente: cada cliente es un punto en un espacio multidimensional donde cada dimensión es una de sus características — su edad, su saldo, su país, si es miembro activo… El SVM busca la mejor frontera posible que separe los puntos «se va» de los puntos «se queda».

💡
Analogía para entender el SVM

Imagina que tienes una mesa con bolitas rojas (clientes que se fueron) y azules (clientes que se quedaron) mezcladas. El SVM busca la mejor superficie para separarlas, maximizando el espacio libre entre la frontera y las bolitas más cercanas a ella. Esa distancia se llama margen, y maximizarla es lo que hace que el modelo generalice bien a datos nuevos. Las bolitas justo en el borde son los vectores de soporte: los casos más difíciles de clasificar, y son los que definen la frontera.

El dataset contiene información de 10.000 clientes de un banco: perfil demográfico, situación financiera y comportamiento. La variable que queremos predecir es Exited: 0 si el cliente se quedó, 1 si abandonó el banco.

01
Carga y calidad del dato
df.shape · df.info() · nulos · duplicados

Lo primero que haces cuando recibes un dataset es entender qué tienes delante y si está en condiciones de usarse. No puedes construir un modelo sobre datos corruptos — basura entra, basura sale.

Salida — df.shape + info
Shape: (10000, 18)
Filas: 10,000 | Columnas: 18

RangeIndex: 10000 entries, 0 to 9999
Data columns (total 18 columns):
 CreditScore         10000 non-null  int64
 Geography           10000 non-null  object
 Gender              10000 non-null  object
 Age                 10000 non-null  int64
 Balance             10000 non-null  float64
 NumOfProducts       10000 non-null  int64
 Exited              10000 non-null  int64
 ...

Todas las columnas tienen exactamente 10.000 valores no nulos: no hay ningún dato faltante. Los tipos son coherentes — números en int64 o float64, textos en object.

Salida — nulos y duplicados
No hay valores nulos en el dataset.
No hay filas duplicadas en el dataset.
¿Por qué importa esto para el SVM?

El SVM calcula distancias entre puntos en el espacio de features. Si hay valores nulos ese cálculo falla o produce resultados incorrectos. Con filas duplicadas el modelo sobrepondera ciertas muestras, lo que sesga la frontera de decisión hacia esos casos repetidos.

02
Desbalance de clases
El problema del 80/20 que condiciona todo lo que viene después
Salida — distribución de Exited
Exited
0    7962   ← No churn (79.62%)
1    2038   ← Churn    (20.38%)

Por cada cliente que se va, hay casi cuatro que se quedan. Esto no es un detalle menor — es uno de los problemas más comunes en clasificación binaria y condiciona todas las decisiones técnicas que vienen después.

💡
Por qué el desbalance es un problema real

Imagina que entrenas un modelo y te dice que predice con 80% de accuracy. Suena bien. Pero si ese modelo simplemente dice «no churn» para absolutamente todos los clientes, también acertaría el 80% de las veces —porque el 80% realmente no se va. El modelo habría aprendido a hacer el vago. No habría aprendido nada útil sobre los clientes que sí se van, que son exactamente los que te interesa detectar.

Implicación directa sobre las métricas

Por esta razón nunca usamos accuracy como métrica principal en este proyecto. Usamos ROC-AUC y Average Precision para seleccionar y evaluar el modelo, y prestamos atención especial al recall de la clase churn porque es la métrica más relevante desde negocio.

03
Exploración visual
Histogramas · Tasas de churn · Heatmap de correlación · Boxplots

El EDA visual responde tres preguntas fundamentales antes de modelar: ¿qué variables se comportan diferente entre churners y no churners? ¿Hay relaciones problemáticas entre features? ¿Hay outliers relevantes? Las respuestas determinan qué features entran al modelo, cuáles se transforman y cómo.


Histogramas de variables numéricas por clase. Para cada variable numérica trazamos dos distribuciones superpuestas: una para los clientes que se fueron (rojo) y otra para los que se quedaron (azul). Si las distribuciones se solapan completamente, la variable no discrimina entre ambas clases. Si se separan, tiene potencial predictivo.

Distribución de variables numéricas por Exited
Histogramas variables numéricas
VariableObservaciónSeñal predictiva
AgeLos churners tienden a ser mayores — pico alrededor de 45-50 años vs 30-35 en no churners.Alta
NumOfProductsLos clientes con 3 o 4 productos tienen churn casi total. Con 1 o 2, mucho menor.Alta
BalancePico muy pronunciado en exactamente 0, con una distribución bimodal llamativa.Media
CreditScoreDistribuciones casi idénticas entre las dos clases.Baja
EstimatedSalaryDistribución prácticamente uniforme, sin separación entre clases.Baja
TenureDistribución similar entre clases, sin separación clara.Baja

Tasas de churn por variable categórica. En vez de contar clientes por categoría — que sería engañoso con el desbalance 80/20 — calculamos la proporción de Exited=1 dentro de cada categoría. Así comparamos en la misma escala sin que el volumen absoluto distorsione la lectura.

Tasa de churn por variable categórica
Tasas de churn por categórica
VariableObservaciónSeñal predictiva
GeographyAlemania tiene una tasa de churn que duplica la de España y Francia.Alta
GenderLas mujeres abandonan el banco en mayor proporción que los hombres.Media
IsActiveMemberLos clientes no activos tienen una tasa de churn notablemente superior.Media
HasCrCardLa tasa de churn es prácticamente idéntica entre quienes tienen tarjeta y quienes no.Nula → descartada

Heatmap de correlación. Mide la correlación lineal entre pares de variables numéricas. Un valor cercano a 1 o -1 indica que dos variables se mueven juntas, lo que puede crear multicolinealidad y distorsionar el modelo. El resultado muestra correlaciones bajas entre todas las features numéricas — cada una aporta información independiente. Las correlaciones más altas con Exited corresponden a Age y NumOfProducts, confirmando lo que ya indicaban los histogramas.

Matriz de correlación — variables numéricas + target
Heatmap de correlación

Boxplots. Complementan los histogramas mostrando mediana, rango intercuartílico y outliers de forma compacta por clase. Confirman la separación en Age, el comportamiento no lineal de NumOfProducts, y permiten cuantificar los outliers que se abordan en la siguiente sección.

Boxplots de variables numéricas por Exited
Boxplots variables numéricas
04
Selección de features
Qué entra al modelo, qué se descarta y por qué

No todas las columnas del dataset son útiles para el modelo. Meter features irrelevantes no solo no ayuda — añade ruido, aumenta el coste computacional y puede perjudicar la capacidad de generalización.

Descartadas:

RowNumber CustomerId Surname Card Type HasCrCard

RowNumber, CustomerId y Surname son identificadores sin ninguna relación causal con el churn. Card Type y HasCrCard mostraron en el EDA tasas de churn prácticamente idénticas entre todas sus categorías — no discriminan.

Features finales que entran al modelo:

Age NumOfProducts CreditScore Gender Tenure Balance IsActiveMember EstimatedSalary Satisfaction Score Point Earned has_balance Geography_Germany Geography_Spain
Outliers — decisión documentada

El análisis IQR detectó 359 outliers en Age (3.6%), 60 en NumOfProducts (0.6%) y 15 en CreditScore (0.1%). Se mantienen: el StandardScaler que aplicamos después amortigua su impacto en las distancias que calcula el SVM, y en un contexto de laboratorio no justifican eliminar datos reales.

05
Feature engineering
Creando has_balance a partir de una señal visual

El feature engineering consiste en crear variables nuevas a partir de las existentes cuando detectas un patrón que el modelo podría no capturar bien por sí solo. No es inventar datos — es reformular la información disponible de una forma más útil para el algoritmo.

En el histograma de Balance vimos algo llamativo: una cantidad muy elevada de clientes con saldo exactamente igual a 0. No es el extremo inferior de una distribución continua — es un pico discreto en el cero exacto, que indica que «no tener saldo» es una categoría de cliente diferenciada con comportamiento propio respecto al churn.

Balance — el pico en cero que motivó has_balance
Distribución Balance
💡
¿Por qué el modelo no captaría esto solo?

Si metes Balance como variable numérica continua, el modelo ve 0 como «un valor bajo de saldo». Pero la diferencia real no es entre saldo bajo y saldo alto — es entre «tiene saldo» y «no tiene saldo en absoluto». Esa distinción binaria tiene sentido de negocio: un cliente sin saldo en cuenta tiene un perfil de riesgo diferente. La variable has_balance captura exactamente eso con un 0 o un 1, y Balance original se mantiene también porque aporta información complementaria sobre cuánto saldo tiene quien sí tiene.

06
Encoding de variables categóricas
Label encoding · One-Hot Encoding · drop_first

Los algoritmos de ML, incluido el SVM, trabajan con números. Las variables categóricas necesitan convertirse a representaciones numéricas antes de entrar al modelo. Pero no todas las conversiones son iguales — elegir la incorrecta puede introducir información falsa.

Label encoding para Gender. Solo tiene dos valores: Male y Female. La convertimos directamente a 0 y 1. Funciona porque es binaria — no hay un orden que el modelo pueda malinterpretar.

¿Por qué label encoding no sirve para Geography?

Si codificas Spain=0, France=1, Germany=2, el modelo interpreta que Germany > France > Spain matemáticamente, como si hubiera una jerarquía entre países. Eso es información falsa que puede distorsionar la frontera de decisión. Geography no tiene orden — son categorías equivalentes.

One-Hot Encoding para Geography con drop_first=True: creamos una columna binaria por país, pero eliminamos una de las tres columnas resultantes para evitar multicolinealidad perfecta. Si Germany=0 y Spain=0, el modelo infiere France sin necesidad de una columna explícita.

07
Train / Test split
80/20 estratificado · por qué el orden importa

Antes de entrenar el modelo, dividimos el dataset en dos partes que nunca se mezclan: train (80% → 8.000 filas), con el que el modelo aprende, y test (20% → 2.000 filas), reservado como simulacro de datos futuros para evaluar si lo aprendido generaliza.

💡
La analogía del examen

El train son los apuntes y ejercicios con los que estudias. El test es el examen final — preguntas que nunca has visto antes. Si el modelo pudiera ver el test durante el entrenamiento, sería como estudiar con las respuestas del examen en la mano: los resultados serían artificialmente buenos y no reflejarían lo que el modelo realmente sabe hacer con datos nuevos.

El parámetro stratify=y es crítico con desbalance. Sin él, la división aleatoria podría generar un test donde, por azar, haya muy pocos churners. Con stratify, la proporción 80/20 de churn se replica exactamente en train y en test.

Salida — shapes resultantes
Train: (8000, 14) | Test: (2000, 14)
Features: ['CreditScore', 'Gender', 'Age', 'Tenure', 'Balance',
           'NumOfProducts', 'IsActiveMember', 'EstimatedSalary',
           'Satisfaction Score', 'Point Earned', 'has_balance',
           'Geography_Germany', 'Geography_Spain']
08
Scaling con StandardScaler
Por qué el SVM lo requiere · fit en train, transform en test
💡
Por qué el SVM necesita scaling obligatoriamente

El SVM calcula distancias entre puntos en el espacio de features para encontrar la frontera óptima. Si EstimatedSalary va de 0 a 200.000 y NumOfProducts va de 1 a 4, la distancia estará completamente dominada por el salario aunque NumOfProducts sea mucho más informativo. Sin scaling el modelo no puede comparar features en igualdad de condiciones. El StandardScaler lleva todo a media 0 y desviación típica 1, sin distorsionar la distribución relativa de los datos.

1
fit_transform sobre X_train
El scaler aprende la media y desviación típica exclusivamente de los datos de entrenamiento. Luego transforma X_train con esos parámetros.
2
Solo transform sobre X_test
Aplica los mismos parámetros aprendidos en train al test. No recalcula nada del test.
Antipatrón crítico — data leakage por scaling

Si haces fit_transform sobre todo el dataset antes del split, estás filtrando información del futuro al modelo. Las métricas resultantes serían optimistas y falsas. El orden correcto es siempre: split primero, luego fit solo en train.

09
PCA — Evaluado y descartado
12 componentes para el 90% de varianza · reducción insuficiente

PCA transforma las features originales en combinaciones lineales ordenadas por la cantidad de varianza que explican. El objetivo es reducir dimensiones manteniendo la mayor información posible.

Salida — varianza explicada acumulada
90% de varianza explicada con 12 componentes
95% de varianza explicada con 12 componentes
PCA — Varianza explicada acumulada por número de componentes
PCA varianza explicada
¿Por qué descartamos PCA?

Para conservar el 90% de la varianza necesitamos 12 de 14 componentes — una reducción real de apenas 2 features. El coste de esa reducción es perder toda interpretabilidad: las componentes principales son combinaciones abstractas de todas las features originales, y ya no podemos decir qué variables importan más. Para ganar 2 dimensiones, no merece la pena. Decisión: entrenar directamente sobre las 14 features escaladas.

10
Baseline SVM
El punto de partida antes de optimizar nada

Antes de buscar los mejores hiperparámetros, entrenamos un modelo con los valores por defecto. Esto da un punto de referencia claro: si después de todo el trabajo de tuning solo mejoramos 0.01 puntos de AUC, el esfuerzo apenas aportó. Si mejoramos 0.10 puntos, el tuning fue esencial.

Usamos class_weight='balanced' desde el inicio para compensar el desbalance 80/20 — sin esto el modelo ignoraría casi completamente la clase churn. Y probability=True para obtener scores continuos entre 0 y 1, necesarios para la curva ROC y para ajustar el threshold posteriormente.

Salida — Baseline SVM (RBF, parámetros por defecto)
              precision    recall  f1-score   support

    No churn       0.93      0.79      0.86      1592
       Churn       0.49      0.78      0.60       408

    accuracy                           0.79      2000
   macro avg       0.71      0.78      0.73      2000
weighted avg       0.84      0.79      0.80      2000

ROC-AUC: 0.8548

Un AUC de 0.8548 ya desde el baseline es un punto de partida sólido. El modelo detecta el 78% de los churners reales (recall), aunque genera bastantes falsas alarmas — de cada 2 clientes que marca como churn, solo 1 realmente lo es (precision 0.49). Ese es el equilibrio típico en problemas con desbalance: para detectar más churners reales hay que asumir más falsas alarmas, y el threshold es el dial que controla ese equilibrio.

¿Qué features están tirando del modelo?

Dado lo que mostró el EDA, las variables que más contribuyen a este resultado son probablemente Age, NumOfProducts, IsActiveMember y Geography. El SVM no da importancias de feature directamente como un árbol de decisión, pero la señal que detectamos visualmente en el EDA es la que el modelo está aprendiendo a explotar.

11
GridSearchCV
20 combinaciones · 5 folds · C=100, gamma=0.01

Los hiperparámetros son parámetros que configuramos antes de entrenar y que el modelo no puede aprender por sí solo. Para el SVM con kernel RBF, los dos críticos son C y gamma.

💡
C y gamma — qué controla cada uno

C es el control de rigidez. C bajo permite que la frontera ignore puntos mal clasificados para quedar suave y amplia — más tolerante pero puede subajustar. C alto obliga a clasificar casi todos los puntos correctamente aunque la frontera quede retorcida — puede sobreajustar. Gamma controla el radio de influencia de cada punto de soporte. Gamma bajo extiende esa influencia lejos, generando fronteras suaves. Gamma alto la concentra localmente, generando fronteras más irregulares que pueden memorizar el ruido.

Salida — GridSearch (20 combinaciones × 5 folds = 100 fits)
Fitting 5 folds for each of 20 candidates, totalling 100 fits
Mejores parámetros: {'C': 100, 'gamma': 0.01}
Mejor ROC-AUC (CV): 0.8442

El ganador es C=100, gamma=0.01. C alto indica que el modelo necesita ajustarse con bastante precisión a los datos de entrenamiento para capturar la señal — tiene sentido en un problema donde los patrones no son triviales. Gamma bajo genera una frontera global y suave, lo que ayuda a generalizar a datos nuevos. La combinación es coherente: estricto en los errores de entrenamiento, pero con influencia amplia para no sobreajustar localmente.

¿Por qué scoring=’roc_auc’ en el GridSearch?

Con desbalance 80/20, un modelo que predice siempre «no churn» tendría 80% de accuracy en cada fold — parecería el mejor modelo sin serlo. ROC-AUC mide la capacidad discriminativa global del modelo independientemente del threshold y del desbalance, por lo que es la métrica correcta para guiar la búsqueda de hiperparámetros.

12
Evaluación del mejor modelo
Classification report · Confusion matrix · AUC 0.8622

Aplicamos el mejor modelo al test set — 2.000 clientes que nunca ha visto — y medimos cuánto acierta y, sobre todo, de qué tipo son sus errores.

Predice: No churn
Predice: Churn
Real: No churn
1258
Verdadero Neg.
334
Falso Positivo
Real: Churn
89
Falso Negativo
319
Verdadero Pos.
Confusion Matrix — Mejor SVM post GridSearch
Confusion Matrix
Salida — Classification report
              precision    recall  f1-score   support

    No churn       0.93      0.79      0.85      1592
       Churn       0.48      0.78      0.60       408

    accuracy                           0.79      2000
   macro avg       0.71      0.78      0.73      2000
weighted avg       0.84      0.79      0.80      2000

ROC-AUC (test): 0.8622

El modelo detecta 319 de los 408 churners reales del test — un recall del 78%. Los 89 que se escapa son clientes que se van sin que el sistema los haya identificado. Por otro lado, de los 653 clientes que el modelo marca como churn, 334 en realidad no se iban a ir — esas son las falsas alarmas que generan campañas de retención innecesarias.

El falso negativo es el error más caro desde negocio

Un cliente que sí se va pero el modelo predice que se queda es una oportunidad perdida — no actúas sobre él y lo pierdes. Los falsos positivos generan coste en campañas de retención innecesarias, pero ese coste suele ser menor que perder un cliente. Por eso en problemas de churn el recall es la métrica que más atenemos al criterio de negocio.

13
Curva Precision-Recall
Average Precision: 0.6974 · complemento a la curva ROC

Además de la curva ROC, evaluamos el modelo con la curva Precision-Recall y el Average Precision. Con desbalance importante, la curva ROC puede resultar engañosamente optimista porque el gran volumen de verdaderos negativos (los 7.962 clientes que no hacen churn) infla artificialmente el denominador del FPR y hace que la curva parezca mejor de lo que es para la clase que realmente nos importa.

Salida — Average Precision
Average Precision: 0.6974
Curva Precision-Recall — con baseline de clasificador aleatorio
Curva Precision-Recall
💡
Cómo leer esta curva

El eje X es el recall — de todos los churners reales, qué porcentaje detecto. El eje Y es la precision — de los que clasifico como churn, qué porcentaje realmente lo son. La línea roja punteada es el baseline: un clasificador aleatorio que acertaría el 20% de las veces (la proporción de churners). Cuanto más arriba y a la derecha esté nuestra curva respecto al baseline, mejor discrimina el modelo sobre la clase churn. El Average Precision de 0.6974 resume el área bajo esa curva — representa una mejora sustancial sobre el azar.

14
Curva ROC y ajuste de threshold
Threshold óptimo: 0.2522 · por qué el 0.5 por defecto no es el mejor

La curva ROC visualiza el comportamiento del modelo para todos los posibles umbrales de decisión simultáneamente. Por defecto, el modelo clasifica como churn cuando la probabilidad estimada supera 0.5. Pero ese 0.5 es arbitrario y no tiene en cuenta ni el desbalance ni el coste relativo de cada tipo de error.

💡
Qué representa la curva ROC

Imagina un dial entre 0 y 1. En 0, clasificas todo como churn: detectas todos los churners reales (TPR=1) pero también generas infinitas falsas alarmas (FPR=1). En 1, al revés — cero falsas alarmas pero tampoco detectas nada. La curva ROC traza el camino entre esos dos extremos para cada valor posible del dial. El AUC mide cuán arriba y a la izquierda está esa curva: 1.0 sería discriminación perfecta, 0.5 sería azar puro.

Salida — threshold óptimo (índice de Youden)
Threshold óptimo (Youden): 0.2522
TPR en ese punto: 0.7475
FPR en ese punto: 0.1715
Curva ROC — AUC 0.8622 con threshold óptimo marcado
Curva ROC
Salida — clasificación con threshold ajustado a 0.2522
              precision    recall  f1-score   support

    No churn       0.93      0.83      0.88      1592
       Churn       0.53      0.75      0.62       408

    accuracy                           0.81      2000
   macro avg       0.73      0.79      0.75      2000
weighted avg       0.85      0.81      0.82      2000

Bajar el threshold de 0.5 a 0.2522 tiene un efecto claro en el conjunto: la accuracy global sube de 0.79 a 0.81, la precision de churn sube de 0.48 a 0.53 y el F1 de churn mejora de 0.60 a 0.62. El threshold ajustado ofrece un mejor equilibrio global aunque el recall de churn baje ligeramente de 0.78 a 0.75 — es el trade-off inherente al ajuste.

AUC baseline
0.8548
Sin tuning
AUC CV
0.8442
Sobre folds de train
AUC test
0.8622
C=100, γ=0.01
Average Precision
0.6974
Clase churn
Threshold óptimo
0.2522
Índice de Youden
Recall churn
0.75
Con threshold ajustado
15
Conclusión del experimento
Lo que los números nos dicen y dónde hay margen de mejora

El modelo SVM con kernel RBF, C=100 y gamma=0.01, entrenado sobre las 14 features de comportamiento financiero y perfil demográfico de los clientes, alcanza un AUC de 0.8622 sobre el conjunto de test. Detecta correctamente el 75% de los clientes en riesgo de abandono cuando se usa el threshold óptimo de 0.2522.

Las features que más contribuyen a ese resultado son las que el EDA ya señalaba como relevantes: la edad del cliente, el número de productos contratados, si es miembro activo y el país de procedencia — Alemania en particular concentra una tasa de churn significativamente más alta.

Lo que el modelo hace bien

Un AUC de 0.8622 es un resultado sólido para un problema de churn bancario con desbalance 80/20, trabajando únicamente con features de comportamiento observable — sin señales artificiales. El pipeline completo es robusto: split estratificado, scaling correcto, cross-validation estratificada en el GridSearch, y evaluación con métricas adecuadas al desbalance.

Dónde hay margen de mejora

La precision de churn (0.48-0.53) implica que por cada churner real detectado el modelo genera casi una falsa alarma. Posibles vías de mejora: técnicas de resampling como SMOTE para el desbalance, explorar otros kernels o algoritmos más interpretables como gradient boosting, profundizar en el feature engineering especialmente en interacciones entre Age y NumOfProducts, o ajustar el threshold en función del coste real de cada tipo de error en el negocio.

Resumen del pipeline completo

Carga y verificación de calidad → análisis del desbalance 80/20 → EDA visual (histogramas 2×4, tasas de churn por categórica, heatmap de correlación, boxplots) → selección de features (5 columnas descartadas) → feature engineering (has_balance) → encoding (label para Gender, OHE con drop_first para Geography) → train/test split estratificado 80/20 → StandardScaler fit solo en train → PCA evaluado y descartado → baseline SVM con AUC 0.8548 → GridSearchCV con 20 combinaciones y 5 folds, ganador C=100, gamma=0.01 → evaluación con classification report y confusion matrix → Average Precision 0.6974 y curva PR → curva ROC con AUC 0.8622 y ajuste de threshold a 0.2522.