Spaces:
Sleeping
Sleeping
Juan Acevedo commited on
Commit ·
e39b21b
1
Parent(s): 7b1e84b
add
Browse files
app.py
CHANGED
|
@@ -28,6 +28,115 @@ df_viz['Cluster'] = cluster_predictions
|
|
| 28 |
X_scaled = pipeline.named_steps['scaler'].transform(df_features)
|
| 29 |
X_pca = pipeline.named_steps['pca'].transform(X_scaled)
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
def create_cluster_plot():
|
| 32 |
"""Crear el gráfico de scatter de los clusters"""
|
| 33 |
plt.figure(figsize=(10, 6))
|
|
@@ -66,7 +175,8 @@ def get_cluster_summary(selected_cluster):
|
|
| 66 |
for var in important_vars:
|
| 67 |
if var in cluster_data.columns:
|
| 68 |
mean_val = cluster_data[var].mean()
|
| 69 |
-
|
|
|
|
| 70 |
|
| 71 |
return pd.DataFrame(summary_data, columns=['Variable', 'Promedio'])
|
| 72 |
|
|
@@ -117,7 +227,24 @@ def predict_new_customer(customer_id, year_birth, income, kidhome, teenhome, rec
|
|
| 117 |
# Predecir cluster (el pipeline maneja internamente el scaling y PCA)
|
| 118 |
predicted_cluster = pipeline.predict(new_customer)[0]
|
| 119 |
|
| 120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
def update_cluster_info(selected_cluster):
|
| 123 |
"""Actualizar información cuando se selecciona un cluster"""
|
|
@@ -142,6 +269,11 @@ with gr.Blocks(title="Customer Personality Analysis", theme=gr.themes.Soft()) as
|
|
| 142 |
El modelo utiliza **K-Means con PCA** para segmentar clientes en 2 grupos basándose en sus patrones de compra y comportamiento.
|
| 143 |
""")
|
| 144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
with gr.Row():
|
| 146 |
with gr.Column():
|
| 147 |
# Gráfico de clusters
|
|
|
|
| 28 |
X_scaled = pipeline.named_steps['scaler'].transform(df_features)
|
| 29 |
X_pca = pipeline.named_steps['pca'].transform(X_scaled)
|
| 30 |
|
| 31 |
+
# Diccionario para traducir variables
|
| 32 |
+
VARIABLE_NAMES = {
|
| 33 |
+
'Year_Birth': 'Año de Nacimiento',
|
| 34 |
+
'Income': 'Ingresos (log)',
|
| 35 |
+
'Kidhome': 'Niños en Casa',
|
| 36 |
+
'Teenhome': 'Adolescentes en Casa',
|
| 37 |
+
'Recency': 'Días desde Última Compra',
|
| 38 |
+
'MntWines': 'Gastos en Vinos (log)',
|
| 39 |
+
'MntFruits': 'Gastos en Frutas (log)',
|
| 40 |
+
'MntMeatProducts': 'Gastos en Carnes (log)',
|
| 41 |
+
'MntFishProducts': 'Gastos en Pescados (log)',
|
| 42 |
+
'MntSweetProducts': 'Gastos en Dulces (log)',
|
| 43 |
+
'MntGoldProds': 'Gastos en Productos Premium (log)',
|
| 44 |
+
'NumWebPurchases': 'Compras Web',
|
| 45 |
+
'NumCatalogPurchases': 'Compras por Catálogo',
|
| 46 |
+
'NumStorePurchases': 'Compras en Tienda',
|
| 47 |
+
'NumWebVisitsMonth': 'Visitas Web por Mes'
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
def analyze_clusters():
|
| 51 |
+
"""Analizar y describir las características de cada cluster"""
|
| 52 |
+
cluster_descriptions = {}
|
| 53 |
+
|
| 54 |
+
for cluster in [0, 1]:
|
| 55 |
+
cluster_data = df_viz[df_viz['Cluster'] == cluster]
|
| 56 |
+
|
| 57 |
+
# Calcular promedios de variables clave
|
| 58 |
+
avg_income = np.exp(cluster_data['Income'].mean()) - 1 # Deshacer log1p
|
| 59 |
+
avg_wines = np.exp(cluster_data['MntWines'].mean()) - 1
|
| 60 |
+
avg_meat = np.exp(cluster_data['MntMeatProducts'].mean()) - 1
|
| 61 |
+
avg_gold = np.exp(cluster_data['MntGoldProds'].mean()) - 1
|
| 62 |
+
avg_web_purchases = cluster_data['NumWebPurchases'].mean()
|
| 63 |
+
avg_store_purchases = cluster_data['NumStorePurchases'].mean()
|
| 64 |
+
avg_kids = cluster_data['Kidhome'].mean()
|
| 65 |
+
avg_teens = cluster_data['Teenhome'].mean()
|
| 66 |
+
|
| 67 |
+
cluster_descriptions[cluster] = {
|
| 68 |
+
'avg_income': avg_income,
|
| 69 |
+
'avg_wines': avg_wines,
|
| 70 |
+
'avg_meat': avg_meat,
|
| 71 |
+
'avg_gold': avg_gold,
|
| 72 |
+
'avg_web': avg_web_purchases,
|
| 73 |
+
'avg_store': avg_store_purchases,
|
| 74 |
+
'avg_kids': avg_kids,
|
| 75 |
+
'avg_teens': avg_teens,
|
| 76 |
+
'count': len(cluster_data)
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
return cluster_descriptions
|
| 80 |
+
|
| 81 |
+
def get_cluster_interpretation():
|
| 82 |
+
"""Generar interpretación de los clusters"""
|
| 83 |
+
cluster_info = analyze_clusters()
|
| 84 |
+
|
| 85 |
+
interpretation = """
|
| 86 |
+
## 🧠 Interpretación de los Clusters
|
| 87 |
+
|
| 88 |
+
Basándose en el análisis de los datos, nuestro modelo ha identificado dos segmentos principales de clientes:
|
| 89 |
+
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
for cluster in [0, 1]:
|
| 93 |
+
info = cluster_info[cluster]
|
| 94 |
+
|
| 95 |
+
# Determinar el perfil del cluster
|
| 96 |
+
if info['avg_income'] > 50000:
|
| 97 |
+
income_level = "ingresos altos"
|
| 98 |
+
elif info['avg_income'] > 30000:
|
| 99 |
+
income_level = "ingresos medios"
|
| 100 |
+
else:
|
| 101 |
+
income_level = "ingresos bajos"
|
| 102 |
+
|
| 103 |
+
if info['avg_wines'] > 200:
|
| 104 |
+
wine_spending = "alto gasto en vinos"
|
| 105 |
+
elif info['avg_wines'] > 50:
|
| 106 |
+
wine_spending = "gasto moderado en vinos"
|
| 107 |
+
else:
|
| 108 |
+
wine_spending = "bajo gasto en vinos"
|
| 109 |
+
|
| 110 |
+
if info['avg_web'] > info['avg_store']:
|
| 111 |
+
channel_pref = "prefieren compras online"
|
| 112 |
+
else:
|
| 113 |
+
channel_pref = "prefieren compras en tienda física"
|
| 114 |
+
|
| 115 |
+
family_status = ""
|
| 116 |
+
if info['avg_kids'] + info['avg_teens'] > 1:
|
| 117 |
+
family_status = "con familias más grandes"
|
| 118 |
+
elif info['avg_kids'] + info['avg_teens'] > 0.5:
|
| 119 |
+
family_status = "con hijos"
|
| 120 |
+
else:
|
| 121 |
+
family_status = "sin hijos o familias pequeñas"
|
| 122 |
+
|
| 123 |
+
interpretation += f"""
|
| 124 |
+
### 🎯 **Cluster {cluster}** ({info['count']} clientes)
|
| 125 |
+
|
| 126 |
+
**Perfil:** Clientes con {income_level}, {wine_spending}, {family_status} y que {channel_pref}.
|
| 127 |
+
|
| 128 |
+
**Características principales:**
|
| 129 |
+
- 💰 Ingresos promedio: ${info['avg_income']:,.0f}
|
| 130 |
+
- 🍷 Gasto en vinos: ${info['avg_wines']:.0f}
|
| 131 |
+
- 🥩 Gasto en carnes: ${info['avg_meat']:.0f}
|
| 132 |
+
- ✨ Productos premium: ${info['avg_gold']:.0f}
|
| 133 |
+
- 🛒 Compras web: {info['avg_web']:.1f} | Tienda: {info['avg_store']:.1f}
|
| 134 |
+
- 👶 Niños: {info['avg_kids']:.1f} | Adolescentes: {info['avg_teens']:.1f}
|
| 135 |
+
|
| 136 |
+
"""
|
| 137 |
+
|
| 138 |
+
return interpretation
|
| 139 |
+
|
| 140 |
def create_cluster_plot():
|
| 141 |
"""Crear el gráfico de scatter de los clusters"""
|
| 142 |
plt.figure(figsize=(10, 6))
|
|
|
|
| 175 |
for var in important_vars:
|
| 176 |
if var in cluster_data.columns:
|
| 177 |
mean_val = cluster_data[var].mean()
|
| 178 |
+
var_name = VARIABLE_NAMES.get(var, var)
|
| 179 |
+
summary_data.append([var_name, round(mean_val, 2)])
|
| 180 |
|
| 181 |
return pd.DataFrame(summary_data, columns=['Variable', 'Promedio'])
|
| 182 |
|
|
|
|
| 227 |
# Predecir cluster (el pipeline maneja internamente el scaling y PCA)
|
| 228 |
predicted_cluster = pipeline.predict(new_customer)[0]
|
| 229 |
|
| 230 |
+
# Obtener descripción del cluster
|
| 231 |
+
cluster_info = analyze_clusters()
|
| 232 |
+
info = cluster_info[predicted_cluster]
|
| 233 |
+
|
| 234 |
+
result = f"""
|
| 235 |
+
## 🎯 Resultado de la Predicción
|
| 236 |
+
|
| 237 |
+
**El cliente ID {customer_id} pertenece al Cluster {predicted_cluster}**
|
| 238 |
+
|
| 239 |
+
### 📊 Características del Cluster {predicted_cluster}:
|
| 240 |
+
- 👥 Total de clientes similares: {info['count']}
|
| 241 |
+
- 💰 Ingresos promedio del grupo: ${info['avg_income']:,.0f}
|
| 242 |
+
- 🍷 Gasto promedio en vinos: ${info['avg_wines']:.0f}
|
| 243 |
+
- 🥩 Gasto promedio en carnes: ${info['avg_meat']:.0f}
|
| 244 |
+
- 🛒 Compras web vs tienda: {info['avg_web']:.1f} vs {info['avg_store']:.1f}
|
| 245 |
+
"""
|
| 246 |
+
|
| 247 |
+
return result
|
| 248 |
|
| 249 |
def update_cluster_info(selected_cluster):
|
| 250 |
"""Actualizar información cuando se selecciona un cluster"""
|
|
|
|
| 269 |
El modelo utiliza **K-Means con PCA** para segmentar clientes en 2 grupos basándose en sus patrones de compra y comportamiento.
|
| 270 |
""")
|
| 271 |
|
| 272 |
+
# Agregar interpretación de clusters
|
| 273 |
+
gr.Markdown(get_cluster_interpretation())
|
| 274 |
+
|
| 275 |
+
gr.Markdown("---")
|
| 276 |
+
|
| 277 |
with gr.Row():
|
| 278 |
with gr.Column():
|
| 279 |
# Gráfico de clusters
|