GMARTINEZMILLA commited on
Commit
84f39ab
1 Parent(s): e099be1

feat: update filter per manufacturer

Browse files
Files changed (1) hide show
  1. app.py +168 -165
app.py CHANGED
@@ -300,11 +300,12 @@ elif page == "🕵️ Análisis de Cliente":
300
  </p>
301
  """, unsafe_allow_html=True)
302
 
303
- # Customer selection
304
  customer_code = st.selectbox(
305
  "Escribe o selecciona el código de tu cliente",
306
- df['CLIENTE'].unique(),
307
- format_func=lambda x: str(x),
 
308
  help="Start typing to search for a specific customer code"
309
  )
310
 
@@ -313,187 +314,189 @@ elif page == "🕵️ Análisis de Cliente":
313
  with st.spinner("Estamos identificando el grupo del cliente..."):
314
  # Find Customer's Cluster
315
  customer_match = customer_clusters[customer_clusters['cliente_id'] == customer_code]
 
 
316
  if not customer_match.empty:
317
  cluster = customer_match['cluster_id'].values[0]
318
 
319
  with st.spinner(f"Seleccionando el modelo predictivo..."):
 
320
  model_path = f'models/modelo_cluster_{cluster}.txt'
321
  gbm = lgb.Booster(model_file=model_path)
322
 
323
  with st.spinner("Preparando los datos..."):
324
  # Load predict data for that cluster
325
  predict_data = pd.read_csv(f'predicts/predict_cluster_{cluster}.csv')
 
 
326
  predict_data['cliente_id'] = predict_data['cliente_id'].astype(str)
327
 
328
- # Filter customer data after customer code is selected
329
- customer_code_str = str(customer_code)
330
- customer_data = predict_data[predict_data['cliente_id'] == customer_code_str]
331
-
332
- if not customer_data.empty:
333
- st.success(f"Datos para el cliente {customer_code_str} encontrados.")
334
- results = customer_data.copy()
335
-
336
- # Manufacturer selection after customer analysis
337
- manufacturer_options = ['Todos'] + sorted(nombres_proveedores['nombre'].unique())
338
- selected_manufacturer = st.selectbox("Selecciona el fabricante", manufacturer_options)
339
-
340
- if selected_manufacturer != 'Todos':
341
- manufacturer_code = nombres_proveedores[nombres_proveedores['nombre'] == selected_manufacturer]['codigo'].values[0]
342
- results_filtered = results[results['marca_id_encoded'] == manufacturer_code]
343
- historical_data_filtered = historical_data[historical_data['marca_id_encoded'] == manufacturer_code]
344
- else:
345
- results_filtered = results
346
- historical_data_filtered = historical_data
347
-
348
- with st.spinner("Generando predicciones de venta..."):
349
- if not results_filtered.empty:
350
- # Define features consistently with the training process
351
- lag_features = [f'precio_total_lag_{lag}' for lag in range(1, 25)]
352
- features = lag_features + ['mes', 'marca_id_encoded', 'año', 'cluster_id']
353
-
354
- # Prepare data for prediction
355
- X_predict = results_filtered[features]
356
-
357
- # Convert categorical features to 'category' dtype
358
- categorical_features = ['mes', 'marca_id_encoded', 'cluster_id']
359
- for feature in categorical_features:
360
- X_predict[feature] = X_predict[feature].astype('category')
361
-
362
- # Make Prediction for the selected customer
363
- y_pred = gbm.predict(X_predict, num_iteration=gbm.best_iteration)
364
-
365
- # Reassemble the results
366
- results_filtered['ventas_predichas'] = y_pred
367
-
368
- # Load actual data from df_agg_2024
369
- actual_sales = df_agg_2024[df_agg_2024['cliente_id'] == customer_code_str]
370
-
371
- if not actual_sales.empty:
372
- # Merge predictions with actual sales
373
- results_filtered = results_filtered.merge(
374
- actual_sales[['cliente_id', 'marca_id_encoded', 'fecha_mes', 'precio_total']],
375
- on=['cliente_id', 'marca_id_encoded', 'fecha_mes'],
376
- how='left'
377
- )
378
- results_filtered.rename(columns={'precio_total': 'ventas_reales'}, inplace=True)
379
- else:
380
- # If no actual sales data for 2024, create the column with 0s
381
- results_filtered['ventas_reales'] = 0
382
 
383
- # Ensure any missing sales data is filled with 0
384
- results_filtered['ventas_reales'].fillna(0, inplace=True)
 
 
 
 
 
 
385
 
386
- # Define the cutoff date for the last 12 months
387
- fecha_inicio = pd.to_datetime("2023-01-01")
388
- fecha_corte = pd.to_datetime("2024-09-01")
 
 
 
 
389
 
390
- # Filtrar los datos históricos por cliente y por el rango de fechas (2023)
391
- fecha_inicio_2023 = pd.to_datetime("2023-01-01")
392
- fecha_fin_2023 = pd.to_datetime("2023-12-31")
393
 
394
- datos_historicos = historical_data_filtered[
395
- (historical_data_filtered['cliente_id'] == customer_code_str) &
396
- (historical_data_filtered['fecha_mes'] >= fecha_inicio_2023) &
397
- (historical_data_filtered['fecha_mes'] <= fecha_fin_2023)
398
- ].groupby('fecha_mes')['precio_total'].sum().reset_index()
 
 
 
 
 
 
 
399
 
400
- # Renombrar la columna 'precio_total' a 'ventas_historicas' si no está vacía
401
- if not datos_historicos.empty:
402
- datos_historicos.rename(columns={'precio_total': 'ventas_historicas'}, inplace=True)
403
- else:
404
- # Si los datos históricos están vacíos, generar fechas de 2023 con ventas_historicas = 0
405
- fechas_2023 = pd.date_range(start='2023-01-01', end='2023-12-31', freq='M')
406
- datos_historicos = pd.DataFrame({'fecha_mes': fechas_2023, 'ventas_historicas': [0] * len(fechas_2023)})
407
 
408
- # Filtrar los datos de predicciones y ventas reales para 2024
409
- datos_cliente_total = results_filtered.groupby('fecha_mes').agg({
410
- 'ventas_reales': 'sum',
411
- 'ventas_predichas': 'sum'
412
- }).reset_index()
 
413
 
414
- # Asegurarnos de que fecha_mes en datos_cliente_total es datetime
415
- datos_cliente_total['fecha_mes'] = pd.to_datetime(datos_cliente_total['fecha_mes'], errors='coerce')
416
-
417
- # Generar un rango de fechas para 2024 si no hay predicciones
418
- fechas_2024 = pd.date_range(start='2024-01-01', end='2024-12-31', freq='M')
419
- fechas_df_2024 = pd.DataFrame({'fecha_mes': fechas_2024})
420
-
421
- # Combinar datos históricos con predicciones y ventas reales usando un merge
422
- datos_combinados = pd.merge(datos_historicos, datos_cliente_total, on='fecha_mes', how='outer').sort_values('fecha_mes')
423
-
424
- # Rellenar los NaN: 0 en ventas_historicas donde faltan predicciones, y viceversa
425
- datos_combinados['ventas_historicas'].fillna(0, inplace=True)
426
- datos_combinados['ventas_predichas'].fillna(0, inplace=True)
427
- datos_combinados['ventas_reales'].fillna(0, inplace=True)
428
-
429
- # Crear la gráfica con Plotly
430
- fig = go.Figure()
431
-
432
- # Graficar ventas históricas
433
- fig.add_trace(go.Scatter(
434
- x=datos_combinados['fecha_mes'],
435
- y=datos_combinados['ventas_historicas'],
436
- mode='lines+markers',
437
- name='Ventas Históricas',
438
- line=dict(color='blue')
439
- ))
440
-
441
- # Graficar ventas predichas
442
- fig.add_trace(go.Scatter(
443
- x=datos_combinados['fecha_mes'],
444
- y=datos_combinados['ventas_predichas'],
445
- mode='lines+markers',
446
- name='Ventas Predichas',
447
- line=dict(color='orange')
448
- ))
449
-
450
- # Graficar ventas reales
451
- fig.add_trace(go.Scatter(
452
- x=datos_combinados['fecha_mes'],
453
- y=datos_combinados['ventas_reales'],
454
- mode='lines+markers',
455
- name='Ventas Reales',
456
- line=dict(color='green')
457
- ))
458
-
459
- # Personalizar el layout para enfocarse en 2023 y 2024
460
- fig.update_layout(
461
- title=f"Ventas Históricas, Predichas y Reales para Cliente {customer_code}",
462
- xaxis_title="Fecha",
463
- yaxis_title="Ventas (€)",
464
- height=600,
465
- xaxis_range=[fecha_inicio_2023, pd.to_datetime("2024-09-30")], # Ajustar el rango del eje x a 2023-2024
466
- legend_title="Tipo de Ventas",
467
- hovermode="x unified"
468
- )
469
-
470
- # Mostrar la gráfica en Streamlit
471
- st.plotly_chart(fig)
472
-
473
- # Calculate metrics for 2024 data
474
- datos_2024 = datos_combinados[datos_combinados['fecha_mes'].dt.year == 2024]
475
- actual = datos_2024['ventas_reales']
476
- predicted = datos_2024['ventas_predichas']
477
-
478
- def calculate_mape(y_true, y_pred):
479
- mask = y_true != 0
480
- return np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100
481
-
482
- mae = mean_absolute_error(actual, predicted)
483
- mse = mean_squared_error(actual, predicted)
484
- rmse = np.sqrt(mse)
485
- mape = calculate_mape(actual, predicted)
486
- smape = np.mean(2 * np.abs(actual - predicted) / (np.abs(actual) + np.abs(predicted))) * 100
487
-
488
- # Display metrics
489
- st.subheader("Métricas de Predicción (2024)")
490
- col1, col2, col3, col4 = st.columns(4)
491
- col1.metric("MAE", f"{mae:.2f} €", help="Promedio de la diferencia absoluta entre las predicciones y los valores reales.")
492
- col2.metric("MAPE", f"{mape:.2f}%", help="Porcentaje promedio de error en las predicciones.")
493
- col3.metric("RMSE", f"{rmse:.2f} €", help="Medida de la desviación estándar de los residuos de predicción.")
494
- col4.metric("SMAPE", f"{smape:.2f}%", help="Alternativa al MAPE que maneja mejor los valores cercanos a cero.")
495
  else:
496
- st.warning(f"No se encontraron datos filtrados para el cliente {customer_code_str}.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
 
498
  # Split space into two columns
499
  col1, col2 = st.columns(2)
@@ -864,4 +867,4 @@ elif page == "💡 Recomendación de Artículos":
864
  # else:
865
  # st.warning("⚠️ No recommendations found for the provided basket.")
866
  # else:
867
- # st.warning("⚠️ Please select at least one article and set its quantity.")
 
300
  </p>
301
  """, unsafe_allow_html=True)
302
 
303
+ # Combine text input and dropdown into a single searchable selectbox
304
  customer_code = st.selectbox(
305
  "Escribe o selecciona el código de tu cliente",
306
+ df['CLIENTE'].unique(), # All customer codes
307
+
308
+ format_func=lambda x: str(x), # Ensures the values are displayed as strings
309
  help="Start typing to search for a specific customer code"
310
  )
311
 
 
314
  with st.spinner("Estamos identificando el grupo del cliente..."):
315
  # Find Customer's Cluster
316
  customer_match = customer_clusters[customer_clusters['cliente_id'] == customer_code]
317
+ time.sleep(1)
318
+
319
  if not customer_match.empty:
320
  cluster = customer_match['cluster_id'].values[0]
321
 
322
  with st.spinner(f"Seleccionando el modelo predictivo..."):
323
+ # Load the Corresponding Model
324
  model_path = f'models/modelo_cluster_{cluster}.txt'
325
  gbm = lgb.Booster(model_file=model_path)
326
 
327
  with st.spinner("Preparando los datos..."):
328
  # Load predict data for that cluster
329
  predict_data = pd.read_csv(f'predicts/predict_cluster_{cluster}.csv')
330
+
331
+ # Convert cliente_id to string
332
  predict_data['cliente_id'] = predict_data['cliente_id'].astype(str)
333
 
334
+ with st.spinner("Filtrando data..."):
335
+ # Filter for the specific customer
336
+ customer_code_str = str(customer_code)
337
+ customer_data = predict_data[predict_data['cliente_id'] == customer_code_str]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
 
339
+ with st.spinner("Geneerando predicciones de venta..."):
340
+ if not customer_data.empty:
341
+ # Define features consistently with the training process
342
+ lag_features = [f'precio_total_lag_{lag}' for lag in range(1, 25)]
343
+ features = lag_features + ['mes', 'marca_id_encoded', 'año', 'cluster_id']
344
+
345
+ # Prepare data for prediction
346
+ X_predict = customer_data[features]
347
 
348
+ # Convert categorical features to 'category' dtype
349
+ categorical_features = ['mes', 'marca_id_encoded', 'cluster_id']
350
+ for feature in categorical_features:
351
+ X_predict[feature] = X_predict[feature].astype('category')
352
+
353
+ # Make Prediction for the selected customer
354
+ y_pred = gbm.predict(X_predict, num_iteration=gbm.best_iteration)
355
 
356
+ # Reassemble the results
357
+ results = customer_data[['cliente_id', 'marca_id_encoded', 'fecha_mes']].copy()
358
+ results['ventas_predichas'] = y_pred
359
 
360
+ # Load actual data from df_agg_2024
361
+ actual_sales = df_agg_2024[df_agg_2024['cliente_id'] == customer_code_str]
362
+
363
+ if not actual_sales.empty:
364
+ # Merge predictions with actual sales
365
+ results = results.merge(actual_sales[['cliente_id', 'marca_id_encoded', 'fecha_mes', 'precio_total']],
366
+ on=['cliente_id', 'marca_id_encoded', 'fecha_mes'],
367
+ how='left')
368
+ results.rename(columns={'precio_total': 'ventas_reales'}, inplace=True)
369
+ else:
370
+ # If no actual sales data for 2024, fill 'ventas_reales' with 0
371
+ results['ventas_reales'] = 0
372
 
373
+ # Ensure any missing sales data is filled with 0
374
+ results['ventas_reales'].fillna(0, inplace=True)
 
 
 
 
 
375
 
376
+ # Define the cutoff date for the last 12 months
377
+ fecha_inicio = pd.to_datetime("2023-01-01")
378
+ fecha_corte = pd.to_datetime("2024-09-01")
379
+
380
+ # Convertir fecha_mes a datetime en el DataFrame historical_data
381
+ historical_data['fecha_mes'] = pd.to_datetime(historical_data['fecha_mes'], errors='coerce')
382
 
383
+ # Ensure cliente_id is of type string and strip any leading/trailing whitespace
384
+ historical_data['cliente_id'] = historical_data['cliente_id'].astype(str).str.strip()
385
+ customer_code_str = str(customer_code).strip() # Ensure the customer code is also properly formatted
386
+
387
+ filtered_historical_data = historical_data[historical_data['cliente_id'] == customer_code_str]
388
+
389
+
390
+ # Filtrar los datos históricos por cliente y por el rango de fechas (2023)
391
+ fecha_inicio_2023 = pd.to_datetime("2023-01-01")
392
+ fecha_fin_2023 = pd.to_datetime("2023-12-31")
393
+
394
+ datos_historicos = historical_data[
395
+ (historical_data['cliente_id'] == customer_code_str) &
396
+ (historical_data['fecha_mes'] >= fecha_inicio_2023) &
397
+ (historical_data['fecha_mes'] <= fecha_fin_2023)
398
+ ].groupby('fecha_mes')['precio_total'].sum().reset_index()
399
+
400
+ # Renombrar la columna 'precio_total' a 'ventas_historicas' si no está vacía
401
+ if not datos_historicos.empty:
402
+ datos_historicos.rename(columns={'precio_total': 'ventas_historicas'}, inplace=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
  else:
404
+ # Si los datos históricos están vacíos, generar fechas de 2023 con ventas_historicas = 0
405
+ fechas_2023 = pd.date_range(start='2023-01-01', end='2023-12-31', freq='M')
406
+ datos_historicos = pd.DataFrame({'fecha_mes': fechas_2023, 'ventas_historicas': [0] * len(fechas_2023)})
407
+
408
+ # Filtrar los datos de predicciones y ventas reales para 2024
409
+ datos_cliente_total = results.groupby('fecha_mes').agg({
410
+ 'ventas_reales': 'sum',
411
+ 'ventas_predichas': 'sum'
412
+ }).reset_index()
413
+
414
+ # Asegurarnos de que fecha_mes en datos_cliente_total es datetime
415
+ datos_cliente_total['fecha_mes'] = pd.to_datetime(datos_cliente_total['fecha_mes'], errors='coerce')
416
+
417
+ # Generar un rango de fechas para 2024 si no hay predicciones
418
+ fechas_2024 = pd.date_range(start='2024-01-01', end='2024-12-31', freq='M')
419
+ fechas_df_2024 = pd.DataFrame({'fecha_mes': fechas_2024})
420
+
421
+ # Asegurarnos de que fecha_mes en fechas_df_2024 es datetime
422
+ fechas_df_2024['fecha_mes'] = pd.to_datetime(fechas_df_2024['fecha_mes'], errors='coerce')
423
+
424
+ # Combinar datos históricos con predicciones y ventas reales usando un merge
425
+ # Usamos how='outer' para asegurarnos de incluir todas las fechas de 2023 y 2024
426
+ datos_combinados = pd.merge(datos_historicos, datos_cliente_total, on='fecha_mes', how='outer').sort_values('fecha_mes')
427
+
428
+ # Rellenar los NaN: 0 en ventas_historicas donde faltan predicciones, y viceversa
429
+ datos_combinados['ventas_historicas'].fillna(0, inplace=True)
430
+ datos_combinados['ventas_predichas'].fillna(0, inplace=True)
431
+ datos_combinados['ventas_reales'].fillna(0, inplace=True)
432
+
433
+ # Crear la gráfica con Plotly
434
+ fig = go.Figure()
435
+
436
+ # Graficar ventas históricas
437
+ fig.add_trace(go.Scatter(
438
+ x=datos_combinados['fecha_mes'],
439
+ y=datos_combinados['ventas_historicas'],
440
+ mode='lines+markers',
441
+ name='Ventas Históricas',
442
+ line=dict(color='blue')
443
+ ))
444
+
445
+ # Graficar ventas predichas
446
+ fig.add_trace(go.Scatter(
447
+ x=datos_combinados['fecha_mes'],
448
+ y=datos_combinados['ventas_predichas'],
449
+ mode='lines+markers',
450
+ name='Ventas Predichas',
451
+ line=dict(color='orange')
452
+ ))
453
+
454
+ # Graficar ventas reales
455
+ fig.add_trace(go.Scatter(
456
+ x=datos_combinados['fecha_mes'],
457
+ y=datos_combinados['ventas_reales'],
458
+ mode='lines+markers',
459
+ name='Ventas Reales',
460
+ line=dict(color='green')
461
+ ))
462
+
463
+ # Personalizar el layout para enfocarse en 2023 y 2024
464
+ fig.update_layout(
465
+ title=f"Ventas Históricas, Predichas y Reales para Cliente {customer_code}",
466
+ xaxis_title="Fecha",
467
+ yaxis_title="Ventas (€)",
468
+ height=600,
469
+ xaxis_range=[fecha_inicio_2023, pd.to_datetime("2024-09-30")], # Ajustar el rango del eje x a 2023-2024
470
+ legend_title="Tipo de Ventas",
471
+ hovermode="x unified"
472
+ )
473
+
474
+ # Mostrar la gráfica en Streamlit
475
+ st.plotly_chart(fig)
476
+
477
+ # Calculate metrics for 2024 data
478
+ datos_2024 = datos_combinados[datos_combinados['fecha_mes'].dt.year == 2024]
479
+ actual = datos_2024['ventas_reales']
480
+ predicted = datos_2024['ventas_predichas']
481
+
482
+ def calculate_mape(y_true, y_pred):
483
+ mask = y_true != 0
484
+ return np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100
485
+
486
+ mae = mean_absolute_error(actual, predicted)
487
+ mse = mean_squared_error(actual, predicted)
488
+ rmse = np.sqrt(mse)
489
+ mape = calculate_mape(actual, predicted)
490
+ smape = np.mean(2 * np.abs(actual - predicted) / (np.abs(actual) + np.abs(predicted))) * 100
491
+
492
+ # Display metrics
493
+ st.subheader("Métricas de Predicción (2024)")
494
+ col1, col2, col3, col4 = st.columns(4)
495
+ col1.metric("MAE", f"{mae:.2f} €",help="Promedio de la diferencia absoluta entre las predicciones y los valores reales.")
496
+ col2.metric("MAPE", f"{mape:.2f}%",help="Porcentaje promedio de error en las predicciones.")
497
+ col3.metric("RMSE", f"{rmse:.2f} €",help="Medida de la desviación estándar de los residuos de predicción.")
498
+ col4.metric("SMAPE", f"{smape:.2f}%",help="Alternativa al MAPE que maneja mejor los valores cercanos a cero.")
499
+
500
 
501
  # Split space into two columns
502
  col1, col2 = st.columns(2)
 
867
  # else:
868
  # st.warning("⚠️ No recommendations found for the provided basket.")
869
  # else:
870
+ # st.warning("⚠️ Please select at least one article and set its quantity.")