Último proyecto, y volvemos a casa. Después de viviendas, imágenes, sentimientos y fraude, cerramos el círculo donde empezó todo: MercaFresh. El encargo es el clásico del aprendizaje no supervisado: sin etiqueta alguna, descubrir qué segmentos naturales de clientes existen, ponerles nombre de negocio, proponer una acción para cada uno y dejar el sistema listo para producción. Practicarás de punta a punta el módulo 5 completo — K-means, jerárquico, DBSCAN, PCA y t-SNE — sobre un dataset sintético pero realista de ~2.000 clientes generado en el propio código, construyendo las features RFM y los ratios que aprendiste en 03-06 "Ingeniería de características". Y como es el proyecto que cierra el módulo, terminaremos con una recapitulación de los cinco proyectos y el puente hacia el módulo 10.
Contenido
- Definición del problema: ¿qué es "éxito" sin etiquetas?
- Generación del dataset de clientes
- De pedidos a features: RFM y ratios
- EDA y escalado
- K-means: elegir k con codo y silhouette
- Contraste: clustering jerárquico y DBSCAN
- Visualización: PCA y t-SNE
- Perfilado: de centroides a nombres de negocio
- Acciones por segmento y cómo medirlas
- De los segmentos a producción
- Recapitulación del módulo 9
Definición del problema: ¿qué es "éxito" sin etiquetas?
En los proyectos 1–4 había una verdad contra la que medirse. Aquí no: nadie sabe cuántos segmentos "de verdad" hay. El éxito se define distinto, como aprendiste en el módulo 5:
- Validez interna: clusters compactos y separados (silhouette, 05-01).
- Estabilidad: los segmentos no deben cambiar radicalmente con otra semilla u otra muestra.
- Accionabilidad: cada segmento debe ser describible en una frase y asociable a una acción de marketing distinta. Un clustering estadísticamente perfecto pero inexplicable a negocio es un fracaso.
Objetivo concreto: entre 3 y 8 segmentos (menos no discrimina, más no se gestiona), perfilados y con plan de acción.
Generación del dataset de clientes
Simulamos el histórico de pedidos de ~2.000 clientes con arquetipos de comportamiento mezclados y ruido — así luego podremos juzgar si los algoritmos los redescubren. En la realidad, esta tabla saldría de una consulta SQL al histórico de pedidos:
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
n = 2000
# Arquetipos latentes (proporciones): VIP, habitual, ocasional, dormido, nuevo
arquetipo = rng.choice(5, size=n, p=[0.08, 0.32, 0.30, 0.20, 0.10])
# Parámetros por arquetipo: (recencia_media_dias, pedidos_año, ticket_medio)
params = {
0: (7, 90, 85), # VIP: compra semanal, cesta grande
1: (12, 45, 45), # habitual: quincenal, cesta media
2: (35, 12, 30), # ocasional
3: (160, 4, 38), # dormido: hace meses que no compra
4: (10, 3, 25), # nuevo: reciente pero poca historia
}
filas = []
for a in arquetipo:
r_mu, f_mu, m_mu = params[a]
recencia = max(1, rng.gamma(2, r_mu / 2))
pedidos = max(1, rng.poisson(f_mu))
ticket = max(8, rng.normal(m_mu, m_mu * 0.3))
frescos = np.clip(rng.normal([0.55, 0.45, 0.30, 0.35, 0.40][a], 0.12), 0, 1)
antiguedad = (rng.uniform(15, 90) if a == 4
else rng.uniform(180, 1400))
filas.append([recencia, pedidos, ticket, frescos, antiguedad])
clientes = pd.DataFrame(
filas, columns=["recencia_dias", "pedidos_12m", "ticket_medio_eur",
"pct_frescos", "antiguedad_dias"]
)
print(clientes.describe().round(1))Nota honesta: conocemos los arquetipos porque los hemos sembrado, pero no se los daremos a ningún algoritmo — están aquí solo para que tú, al final, puedas comparar lo descubierto con lo sembrado. El clustering trabajará a ciegas, como en la realidad.
De pedidos a features: RFM y ratios
Sobre las columnas base construimos el vector de segmentación, reeditando el RFM de 03-06:
X = pd.DataFrame({
"recencia": clientes["recencia_dias"], # R
"frecuencia": clientes["pedidos_12m"], # F
"monetario": clientes["pedidos_12m"] * clientes["ticket_medio_eur"], # M anual
"ticket_medio": clientes["ticket_medio_eur"],
"pct_frescos": clientes["pct_frescos"],
"pedidos_por_mes_activo": clientes["pedidos_12m"]
/ (clientes["antiguedad_dias"] / 30).clip(lower=1),
})El último ratio es ingeniería con intención: un cliente con 3 pedidos y 20 años de antigüedad no es lo mismo que uno con 3 pedidos y 3 semanas — el ratio de intensidad los separa, distinguiendo nuevos prometedores de ocasionales crónicos.
EDA y escalado
import matplotlib.pyplot as plt
X.hist(bins=40, figsize=(12, 6)); plt.tight_layout(); plt.show()
print(X.corr().round(2))Verás asimetrías fuertes en recencia, frecuencia y monetario (colas a la derecha, el patrón de 03-03 "Transformación de datos") — les aplicamos log para que los clusters no los dominen unos pocos extremos — y después estandarizamos, porque K-means mide distancias euclídeas y sin escalar mandaría la variable de mayor magnitud (monetario, en cientos de euros), como aprendiste en 03-05 y sufriste en 05-01:
from sklearn.preprocessing import StandardScaler
X_t = X.copy()
for col in ["recencia", "frecuencia", "monetario"]:
X_t[col] = np.log1p(X_t[col])
esc = StandardScaler()
X_esc = esc.fit_transform(X_t)K-means: elegir k con codo y silhouette
El protocolo de 05-01 "Clustering: K-means", completo:
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
inercias, siluetas = [], []
ks = range(2, 11)
for k in ks:
km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(X_esc)
inercias.append(km.inertia_)
siluetas.append(silhouette_score(X_esc, km.labels_))
fig, ejes = plt.subplots(1, 2, figsize=(12, 4))
ejes[0].plot(ks, inercias, "o-"); ejes[0].set_title("Codo (inercia)")
ejes[1].plot(ks, siluetas, "o-"); ejes[1].set_title("Silhouette medio")
for e in ejes: e.set_xlabel("k")
plt.show()Con estos datos, el codo se dobla hacia k = 4–5 y la silhouette suele hacer máximo local en k = 5 (≈ 0,30–0,40; en datos reales de clientes, valores así son normales — los segmentos humanos no son esferas nítidas). Elegimos k = 5, y anotamos el argumento: coincide codo, silhouette razonable y un número gestionable para marketing. La decisión de k siempre es mitad métrica, mitad negocio.
km = KMeans(n_clusters=5, n_init=10, random_state=42).fit(X_esc)
clientes["segmento"] = km.labels_
print(clientes["segmento"].value_counts().sort_index())Contraste: clustering jerárquico y DBSCAN
Un solo algoritmo es una sola opinión. El jerárquico de 05-02 aporta el dendrograma — la foto de cómo se agrupan los clientes a todas las escalas:
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster
enlace = linkage(X_esc, method="ward")
plt.figure(figsize=(11, 4))
dendrogram(enlace, truncate_mode="lastp", p=30, no_labels=True)
plt.title("Dendrograma (Ward, truncado)"); plt.ylabel("Distancia")
plt.show()
jer = fcluster(enlace, t=5, criterion="maxclust")
print(pd.crosstab(clientes["segmento"], jer))Si el dendrograma muestra 4–5 ramas gruesas y la tabla cruzada revela que los clusters jerárquicos coinciden en gran medida con los de K-means, tenemos convergencia de evidencia: la estructura es real, no un artefacto del algoritmo.
DBSCAN (05-04) responde otra pregunta: ¿hay clientes que no pertenecen a ningún grupo?
from sklearn.cluster import DBSCAN
db = DBSCAN(eps=0.9, min_samples=10).fit(X_esc)
print(pd.Series(db.labels_).value_counts().head())
print("Clientes-ruido:", (db.labels_ == -1).sum())Los etiquetados como −1 son clientes-ruido: combinaciones raras (ticket enorme con frecuencia mínima, por ejemplo) que no encajan en ningún segmento. K-means los habría forzado dentro del centroide más cercano, contaminando su perfil. En la práctica conviene revisarlos aparte: algunos son errores de datos (03-01), otros son oportunidades singulares — cuentas de empresa, por ejemplo. Ajusta eps con criterio: demasiado pequeño y todo es ruido; demasiado grande y todo es un único cluster.
Visualización: PCA y t-SNE
Seis dimensiones no se dibujan; las proyectamos con las dos lentes del módulo 5 — la lineal y global (PCA, 05-03) y la no lineal centrada en vecindarios (t-SNE, 05-05):
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_esc)
X_tsne = TSNE(n_components=2, perplexity=30,
random_state=42).fit_transform(X_esc)
fig, ejes = plt.subplots(1, 2, figsize=(13, 5))
for eje, datos, titulo in [(ejes[0], X_pca, "PCA"), (ejes[1], X_tsne, "t-SNE")]:
sc = eje.scatter(datos[:, 0], datos[:, 1], c=clientes["segmento"],
cmap="tab10", s=8, alpha=0.6)
eje.set_title(titulo)
plt.colorbar(sc, label="Segmento"); plt.show()
print("Varianza explicada PCA:", pca.explained_variance_ratio_.round(2))Recuerda las reglas de lectura de 05-05: en PCA las distancias globales significan algo (y pca.components_ te dice qué combinación de features es cada eje); en t-SNE solo la pertenencia a grupos es interpretable — ni tamaños ni distancias entre islas. Si los colores de K-means aparecen como regiones coherentes en ambas proyecciones, el clustering gana otra confirmación visual.
Perfilado: de centroides a nombres de negocio
La fase que convierte el análisis en producto — tabla de medias por segmento en unidades originales, no escaladas:
perfil = clientes.groupby("segmento").agg(
n=("segmento", "size"),
recencia=("recencia_dias", "mean"),
pedidos=("pedidos_12m", "mean"),
ticket=("ticket_medio_eur", "mean"),
pct_frescos=("pct_frescos", "mean"),
antiguedad=("antiguedad_dias", "mean"),
).round(1)
perfil["gasto_anual"] = (perfil["pedidos"] * perfil["ticket"]).round(0)
print(perfil.to_string())Con estas cifras se bautiza cada segmento. Un resultado típico (tus números variarán; los nombres los pones mirando tu tabla, no esta):
| Segmento | n | Recencia | Pedidos/año | Ticket | Gasto anual | Nombre de negocio |
|---|---|---|---|---|---|---|
| 0 | ≈ 160 | 8 días | 88 | 84 € | ≈ 7.400 € | VIP |
| 1 | ≈ 640 | 13 días | 44 | 46 € | ≈ 2.000 € | Habituales |
| 2 | ≈ 600 | 36 días | 12 | 31 € | ≈ 370 € | Ocasionales |
| 3 | ≈ 400 | 165 días | 4 | 37 € | ≈ 150 € | Dormidos |
| 4 | ≈ 200 | 11 días | 3 | 26 € | ≈ 80 € | Nuevos |
Y el momento de la verdad de nuestro experimento: cruza clientes["segmento"] con los arquetipos sembrados (pd.crosstab(clientes["segmento"], arquetipo)). Verás una correspondencia fuerte pero imperfecta — fronteras borrosas entre habituales y ocasionales, nuevos repartidos —, que es exactamente lo que pasa con segmentos reales: el clustering recupera la estructura, no la partición exacta.
Acciones por segmento y cómo medirlas
Un segmento sin acción es un gráfico bonito. La tabla que marketing espera:
| Segmento | Acción propuesta | Métrica de éxito |
|---|---|---|
| VIP | Programa fidelidad premium, envío gratis | Retención a 12 meses |
| Habituales | Suscripción de cesta recurrente | Conversión a suscripción |
| Ocasionales | Campañas en sus categorías más compradas | Frecuencia de pedido |
| Dormidos | Email de reactivación con cupón | Tasa de reactivación a 30 días |
| Nuevos | Onboarding: 3 primeras compras guiadas | Supervivencia al 4º pedido |
¿Y cómo sabremos si la acción funciona? Con el test A/B de 02-04 "Inferencia estadística": dentro de cada segmento, un grupo aleatorio recibe la acción y otro no, y se contrasta la diferencia en la métrica de éxito con su test de hipótesis. Sin grupo de control, cualquier mejora es atribuible a la estacionalidad o al azar — el clustering propone, la inferencia dispone.
De los segmentos a producción
Cerramos con el oficio de 08-02 "Implementación de modelos en producción". El pipeline transformaciones + escalado + K-means se serializa y se ejecuta como scoring batch: cada noche (o cada semana — los segmentos se mueven despacio), se recalculan las features de cada cliente y se le asigna segmento con predict, escribiendo el resultado donde el CRM lo lea:
import joblib
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer
def log_rfm(X):
X = X.copy()
for col in ["recencia", "frecuencia", "monetario"]:
X[col] = np.log1p(X[col])
return X
pipeline_seg = Pipeline([
("log", FunctionTransformer(log_rfm)),
("esc", StandardScaler()),
("km", KMeans(n_clusters=5, n_init=10, random_state=42)),
]).fit(X)
joblib.dump(pipeline_seg, "segmentador_mercafresh.joblib")
# En el job nocturno:
modelo = joblib.load("segmentador_mercafresh.joblib")
clientes["segmento"] = modelo.predict(X)Dos vigilancias del monitoreo de 08-03 aplican también aquí: el tamaño de cada segmento en el tiempo (si los dormidos crecen un 30 %, es una alarma de negocio, no del modelo) y la deriva de los centroides cuando se reentrena — si los perfiles cambian de significado, los nombres y las campañas deben revisarse con ellos. Y el recordatorio de 08-04: segmentar para tratar distinto a los clientes exige revisar que ningún segmento sea un proxy de un colectivo protegido.
Errores Comunes y Consejos
- Olvidar el escalado (o el log). Sin estandarizar,
monetariodicta los clusters él solo; sin log, los cuatro clientes extremos se convierten en "segmentos" de cuatro personas. Es el error número uno en segmentación. - Elegir k solo por la métrica. Una silhouette de 0,41 con k = 2 puede ser peor negocio que 0,35 con k = 5: dos segmentos no dan juego comercial. Métrica y accionabilidad deciden juntas.
- Perfilar en unidades escaladas. Un centroide de "−0,3 recencias estándar" no se lo puedes contar a marketing. El perfilado siempre en unidades originales.
- Tratar los segmentos como verdades eternas. Son una foto: los clientes migran entre segmentos y esa migración (¿cuántos habituales caen a dormidos cada mes?) es a menudo más valiosa que la foto misma.
- Lanzar acciones sin grupo de control. Sin A/B no hay atribución; habrás gastado el presupuesto en confirmar tus sesgos.
Retos para ampliar
- Más features, mejores segmentos. Añade al vector variables de comportamiento: porcentaje de compras con cupón, diversidad de categorías, tasa de devoluciones, canal (app/web). Pista: repite codo + silhouette desde cero — con más dimensiones el k óptimo puede cambiar — y vigila con PCA cuánta varianza aportan de verdad las nuevas columnas.
- Clustering de productos. Gira la matriz: en vez de clientes descritos por compras, productos descritos por quién los compra (o por co-aparición en cestas). Pista: la matriz producto × cliente es enorme y dispersa — reduce primero con el PCA de 05-03 (o
TruncatedSVD, su variante para matrices dispersas) y agrupa después; los grupos resultantes alimentan recomendaciones del tipo "quienes compran esto...". - Panel de seguimiento. Construye con matplotlib un informe mensual automático: tamaño de cada segmento en el tiempo, gasto medio por segmento y matriz de migraciones mes a mes. Pista: guarda cada scoring batch con su fecha; la migración es un
pd.crosstab(segmento_mes_anterior, segmento_actual)— y conviértelo en el monitoreo permanente que pedía 08-03.
Recapitulación del módulo 9
Cinco proyectos, cinco problemas distintos, un mismo método. La tabla que resume lo que has practicado:
| Proyecto | Tipo de problema | Dataset | Técnicas protagonistas | Lección central |
|---|---|---|---|---|
| 1. Viviendas | Regresión | California Housing | Pipeline, Ridge/Lasso, Random Forest, HistGradientBoosting, CV, residuos | El EDA anticipa qué modelo ganará |
| 2. Imágenes | Clasificación multiclase | digits + Fashion-MNIST | SVM, PCA, MLP vs. CNN, dropout, early stopping | Empieza simple; que los datos justifiquen cada capa |
| 3. Sentimientos | Clasificación de texto | Reseñas (ficticio) | Limpieza, TF-IDF, n-gramas, MultinomialNB, interpretación de pesos | La representación importa tanto como el algoritmo |
| 4. Fraude | Clasificación desbalanceada | Sintético (make_classification) | class_weight, remuestreo, curva PR, AUC-PR, umbral por costes | Separa el modelo (probabilidades) de la decisión (euros) |
| 5. Segmentación | No supervisado | MercaFresh sintético | RFM, K-means, jerárquico, DBSCAN, PCA/t-SNE, perfilado, A/B | Sin etiquetas, el éxito es estabilidad + accionabilidad |
Y transversal a los cinco: definir el problema y la métrica antes de tocar datos, apartar el test al principio, encapsular en Pipeline, comparar contra un baseline con validación cruzada honesta, analizar los errores en lugar de solo contarlos, y traducir el resultado al idioma de negocio.
Conclusión
El círculo está cerrado: MercaFresh abrió el curso enseñándote cada pieza por separado y lo cierra viéndote ensamblarlas todas sin ayuda — has descubierto sus segmentos de clientes desde cero, los has validado con tres algoritmos y dos proyecciones, les has puesto nombre, acción y grupo de control, y los has dejado sirviéndose en batch con su monitoreo previsto. Ya no eres la persona que empezó el módulo 1: tienes un método completo y cinco proyectos que lo demuestran, listos para convertirse en el embrión de tu portafolio. Lo que queda no es aprender más lecciones, sino seguir aprendiendo por tu cuenta: en el módulo 10 te dejamos el mapa — libros, cursos, comunidades y herramientas — para que el final de este curso sea solo el principio de tu camino en el machine learning.
Curso de Machine Learning
Módulo 1: Introducción al Machine Learning
- ¿Qué es el Machine Learning?
- Historia y evolución del Machine Learning
- Tipos de Machine Learning
- Aplicaciones del Machine Learning
- El flujo de trabajo de un proyecto de Machine Learning
Módulo 2: Fundamentos de Estadística y Probabilidad
- Conceptos básicos de estadística
- Distribuciones de probabilidad
- Correlación y covarianza
- Inferencia estadística
- Teorema de Bayes
Módulo 3: Preprocesamiento de Datos
- Limpieza de datos
- Manejo de datos faltantes
- Transformación de datos
- Codificación de variables categóricas
- Normalización y estandarización
- Ingeniería de características
Módulo 4: Algoritmos de Machine Learning Supervisado
- Regresión lineal
- Regresión logística
- Árboles de decisión
- Máquinas de soporte vectorial (SVM)
- K-Vecinos más cercanos (K-NN)
- Naive Bayes
- Redes neuronales
Módulo 5: Algoritmos de Machine Learning No Supervisado
- Clustering: K-means
- Clustering jerárquico
- Análisis de componentes principales (PCA)
- Análisis de agrupamiento DBSCAN
- Visualización de datos con t-SNE y UMAP
Módulo 6: Evaluación y Validación de Modelos
- División de datos: entrenamiento, validación y prueba
- Métricas de evaluación
- Validación cruzada
- Curva ROC y AUC
- Overfitting y underfitting
Módulo 7: Técnicas Avanzadas y Optimización
- Regularización: Ridge, Lasso y Elastic Net
- Ensemble Learning
- Gradient Boosting
- Redes neuronales profundas (Deep Learning)
- Optimización de hiperparámetros
Módulo 8: Implementación y Despliegue de Modelos
- Frameworks y bibliotecas populares
- Implementación de modelos en producción
- Mantenimiento y monitoreo de modelos
- Consideraciones éticas y de privacidad
Módulo 9: Proyectos Prácticos
- Proyecto 1: Predicción de precios de viviendas
- Proyecto 2: Clasificación de imágenes
- Proyecto 3: Análisis de sentimientos en redes sociales
- Proyecto 4: Detección de fraudes
- Proyecto 5: Segmentación de clientes
