daniel-saed commited on
Commit
ed72f48
·
verified ·
1 Parent(s): d683dff

Upload 2 files

Browse files
Files changed (1) hide show
  1. src/api/load.py +376 -76
src/api/load.py CHANGED
@@ -528,7 +528,7 @@ def get_ppp_difference(df, local, away, season, round_num, league=None):
528
 
529
  '''
530
 
531
- def predecir_corners(local, visitante, jornada, temporada="2526", league_code="ESP",df_database=pd.DataFrame(),xgb_model="",scaler="",lst_years=[]):
532
  """
533
  Predice corners totales con análisis completo para apuestas
534
 
@@ -598,6 +598,13 @@ def predecir_corners(local, visitante, jornada, temporada="2526", league_code="E
598
  dic_features['ppp_local'] = (local_ppp,)
599
  dic_features['ppp_away'] = (away_ppp,)
600
  dic_features['ppp_difference'] = (ppp_diff,)
 
 
 
 
 
 
 
601
 
602
  dic_features['lst_team1_home_form'] = create_line(team1_home, True, True, use_advanced=True)
603
  dic_features['lst_team1_home_general'] = create_line(team1_home, False, True, use_advanced=True)
@@ -651,7 +658,7 @@ def predecir_corners(local, visitante, jornada, temporada="2526", league_code="E
651
  for key in dic_features:
652
  lst_features_values.extend(list(dic_features[key]))
653
 
654
- if key in ['ppp_local', 'ppp_away', 'ppp_difference']:
655
  lst_features_names.append(key)
656
  elif key.startswith('league_'):
657
  lst_features_names.append(key)
@@ -682,6 +689,17 @@ def predecir_corners(local, visitante, jornada, temporada="2526", league_code="E
682
  # ===========================
683
 
684
  prediccion = xgb_model.predict(X_input_scaled)[0]
 
 
 
 
 
 
 
 
 
 
 
685
 
686
  # ===========================
687
  # ✅ ANÁLISIS PROBABILÍSTICO CON POISSON
@@ -692,42 +710,190 @@ def predecir_corners(local, visitante, jornada, temporada="2526", league_code="E
692
  # ===========================
693
  # ESTADÍSTICAS DETALLADAS
694
  # ===========================
 
 
 
 
 
 
 
695
 
696
- local_ck_home = team1_home['Pass Types_CK'].mean() if len(team1_home) > 0 else 0
697
- local_xg_home = team1_home['Expected_xG'].mean() if len(team1_home) > 0 else 0
698
- local_poss_home = team1_home['Poss'].mean() if len(team1_home) > 0 else 0
699
-
700
- away_ck_away = team2_away['Pass Types_CK'].mean() if len(team2_away) > 0 else 0
701
- away_xg_away = team2_away['Expected_xG'].mean() if len(team2_away) > 0 else 0
702
- away_poss_away = team2_away['Poss'].mean() if len(team2_away) > 0 else 0
703
-
704
- local_ck_received = team1_opp_home['Pass Types_CK'].mean() if len(team1_opp_home) > 0 else 0
705
- away_ck_received = team2_opp_away['Pass Types_CK'].mean() if len(team2_opp_away) > 0 else 0
706
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
707
  partido_ck_esperado = local_ck_home + away_ck_away
708
-
709
- h2h_ck_local = team1_h2h['Pass Types_CK'].mean() if len(team1_h2h) > 0 else 0
710
- h2h_ck_away = team2_h2h['Pass Types_CK'].mean() if len(team2_h2h) > 0 else 0
711
- h2h_total = h2h_ck_local + h2h_ck_away
712
-
713
- # ===========================
714
- # MOSTRAR RESULTADOS CON PROBABILIDADES
715
- # ===========================
716
-
717
- print(f"\n🎲 PREDICCIÓN MODELO: {prediccion:.2f} corners totales")
718
- print(f" PPP: {local} ({local_ppp:.2f}) vs {visitante} ({away_ppp:.2f}) | Diff: {ppp_diff:+.2f}")
719
-
720
- print(f"\n📊 ESTADÍSTICAS HISTÓRICAS:")
721
- print(f" {local} (Casa): {local_ck_home:.1f} CK/partido | xG: {local_xg_home:.2f} | Poss: {local_poss_home:.1f}%")
722
- print(f" {visitante} (Fuera): {away_ck_away:.1f} CK/partido | xG: {away_xg_away:.2f} | Poss: {away_poss_away:.1f}%")
723
- print(f" Corners recibidos: {local} ({local_ck_received:.1f}) | {visitante} ({away_ck_received:.1f})")
724
- print(f" Total esperado (suma): {partido_ck_esperado:.1f} corners")
725
-
726
- if len(team1_h2h) > 0 or len(team2_h2h) > 0:
727
- print(f"\n🔄 HEAD TO HEAD (últimos {max(len(team1_h2h), len(team2_h2h))} partidos):")
728
- print(f" {local}: {h2h_ck_local:.1f} CK/partido")
729
- print(f" {visitante}: {h2h_ck_away:.1f} CK/partido")
730
- print(f" Promedio total: {h2h_total:.1f} corners")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
731
 
732
  # ===========================
733
  # ✅ MOSTRAR PROBABILIDADES EXACTAS
@@ -809,11 +975,6 @@ def predecir_corners(local, visitante, jornada, temporada="2526", league_code="E
809
  df_varianza_temp = analizar_fiabilidad_equipos(df_database, temporada=temporada, min_partidos=3)
810
  riesgo = obtener_fiabilidad_partido(local, visitante, df_varianza_temp)
811
 
812
- print(f"\n⚠️ ANÁLISIS DE RIESGO:")
813
- print(f" Local ({local}): {riesgo['nivel_local']} (CV: {riesgo['cv_local']:.1f}%)")
814
- print(f" Away ({visitante}): {riesgo['nivel_away']} (CV: {riesgo['cv_away']:.1f}%)")
815
- print(f" 🎲 FIABILIDAD PARTIDO: {riesgo['fiabilidad']} (Score: {riesgo['score_promedio']:.1f})")
816
- print(f" 💡 {riesgo['mensaje']}")
817
 
818
  # ===========================
819
  # RETORNAR DICCIONARIO COMPLETO
@@ -821,19 +982,135 @@ def predecir_corners(local, visitante, jornada, temporada="2526", league_code="E
821
 
822
  return {
823
  "prediccion": round(prediccion, 2),
 
 
 
 
 
 
 
 
 
 
 
824
  "local": local,
825
  "visitante": visitante,
826
  "ppp_local": local_ppp,
827
  "ppp_away": away_ppp,
828
  "ppp_diff": ppp_diff,
 
829
  "riesgo": riesgo,
830
- "stats": {
831
- "local_ck": local_ck_home,
832
- "away_ck": away_ck_away,
833
- "local_ck_received": local_ck_received,
834
- "away_ck_received": away_ck_received,
835
- "h2h_total": h2h_total,
836
- "partido_esperado": partido_ck_esperado
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  },
838
  "probabilidades_exactas": analisis['exactas'],
839
  "probabilidades_over": analisis['over'],
@@ -1086,38 +1363,47 @@ class USE_MODEL():
1086
 
1087
  # URLs de descarga directa (raw.githubusercontent.com)
1088
  base_url = "https://raw.githubusercontent.com/danielsaed/futbol_corners_forecast/refs/heads/main/models"
1089
- model_url = f"{base_url}/xgboost_corners_v4_retrain.pkl"
1090
- scaler_url = f"{base_url}/scaler_corners_v4_retrain.pkl"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1091
 
1092
  try:
1093
- # Descargar modelo
1094
- print(f"📥 Descargando modelo desde: {model_url}")
1095
- response_model = requests.get(model_url, timeout=30)
1096
- response_model.raise_for_status()
1097
-
1098
- # Descargar scaler
1099
- print(f"📥 Descargando scaler desde: {scaler_url}")
1100
- response_scaler = requests.get(scaler_url, timeout=30)
1101
- response_scaler.raise_for_status()
1102
-
1103
- # Guardar temporalmente y cargar
1104
- with tempfile.NamedTemporaryFile(delete=False, suffix='.pkl') as tmp_model:
1105
- tmp_model.write(response_model.content)
1106
- tmp_model_path = tmp_model.name
1107
-
1108
- with tempfile.NamedTemporaryFile(delete=False, suffix='.pkl') as tmp_scaler:
1109
- tmp_scaler.write(response_scaler.content)
1110
- tmp_scaler_path = tmp_scaler.name
 
 
 
1111
 
1112
- # Cargar modelos desde archivos temporales
1113
- self.xgb_model = joblib.load(tmp_model_path)
1114
- self.scaler = joblib.load(tmp_scaler_path)
1115
-
1116
- # Limpiar archivos temporales
1117
- os.unlink(tmp_model_path)
1118
- os.unlink(tmp_scaler_path)
1119
-
1120
- print("✅ Modelos cargados correctamente desde GitHub")
1121
 
1122
  except requests.exceptions.RequestException as e:
1123
  raise Exception(f"❌ Error descargando modelos: {str(e)}")
@@ -1152,6 +1438,9 @@ class USE_MODEL():
1152
  # Limpieza
1153
  self.df_dataset["season"] = self.df_dataset["season"].astype(str)
1154
  self.df_dataset["Performance_Save%"].fillna(0, inplace=True)
 
 
 
1155
 
1156
  print(f"✅ Total registros: {len(self.df_dataset)}")
1157
 
@@ -1194,6 +1483,17 @@ class USE_MODEL():
1194
  league_code=league_code,
1195
  df_database = self.df_dataset,
1196
  xgb_model = self.xgb_model,
 
 
 
 
 
 
 
 
 
 
 
1197
  scaler=self.scaler,
1198
  lst_years=self.lst_years
1199
  )
 
528
 
529
  '''
530
 
531
+ def predecir_corners(local, visitante, jornada, temporada="2526", league_code="ESP",df_database=pd.DataFrame(),xgb_model="",xgb_model_local="",xgb_model_away="",xgb_model_xg="",xgb_model_xg_local="",xgb_model_xg_away="",xgb_model_gf="",xgb_model_gf_local="",xgb_model_gf_away="",xgb_model_st="",xgb_model_st_local="",xgb_model_st_away="",scaler="",lst_years=[]):
532
  """
533
  Predice corners totales con análisis completo para apuestas
534
 
 
598
  dic_features['ppp_local'] = (local_ppp,)
599
  dic_features['ppp_away'] = (away_ppp,)
600
  dic_features['ppp_difference'] = (ppp_diff,)
601
+ if jornada < 15:
602
+ dic_features['round'] = (1,)
603
+ elif jornada < 15 and jornada > 25:
604
+ dic_features['round'] = (2,)
605
+ else:
606
+ dic_features['round'] = (3,)
607
+
608
 
609
  dic_features['lst_team1_home_form'] = create_line(team1_home, True, True, use_advanced=True)
610
  dic_features['lst_team1_home_general'] = create_line(team1_home, False, True, use_advanced=True)
 
658
  for key in dic_features:
659
  lst_features_values.extend(list(dic_features[key]))
660
 
661
+ if key in ['ppp_local', 'ppp_away', 'ppp_difference','round']:
662
  lst_features_names.append(key)
663
  elif key.startswith('league_'):
664
  lst_features_names.append(key)
 
689
  # ===========================
690
 
691
  prediccion = xgb_model.predict(X_input_scaled)[0]
692
+ prediccion_local = xgb_model_local.predict(X_input_scaled)[0]
693
+ prediccion_away = xgb_model_away.predict(X_input_scaled)[0]
694
+ prediccion_xg = xgb_model_xg.predict(X_input_scaled)[0]
695
+ prediccion_xg_local = xgb_model_xg_local.predict(X_input_scaled)[0]
696
+ prediccion_xg_away = xgb_model_xg_away.predict(X_input_scaled)[0]
697
+ prediccion_gf = xgb_model_gf.predict(X_input_scaled)[0]
698
+ prediccion_gf_local = xgb_model_gf_local.predict(X_input_scaled)[0]
699
+ prediccion_gf_away = xgb_model_gf_away.predict(X_input_scaled)[0]
700
+ prediccion_st = xgb_model_st.predict(X_input_scaled)[0]
701
+ prediccion_st_local = xgb_model_st_local.predict(X_input_scaled)[0]
702
+ prediccion_st_away = xgb_model_st_away.predict(X_input_scaled)[0]
703
 
704
  # ===========================
705
  # ✅ ANÁLISIS PROBABILÍSTICO CON POISSON
 
710
  # ===========================
711
  # ESTADÍSTICAS DETALLADAS
712
  # ===========================
713
+ def get_stat(df, col, form=False):
714
+ if len(df) == 0: return 0
715
+ data = df.tail(6) if form else df
716
+ if col not in data.columns: return 0
717
+ return data[col].mean()
718
+
719
+ # --- 1. EQUIPO LOCAL (TEAM 1) ---
720
 
721
+ # 1.1 Cuando juega en CASA (Home)
722
+ local_ck_home = get_stat(team1_home, 'Pass Types_CK')
723
+ local_xg_home = get_stat(team1_home, 'Expected_xG')
724
+ local_gf_home = get_stat(team1_home, 'GF')
725
+ local_st_home = get_stat(team1_home, 'Standard_SoT') # Nuevo
726
+
727
+ print(team1_home)
728
+ print(team2_away)
 
 
729
 
730
+ local_ck_home_form = get_stat(team1_home, 'Pass Types_CK', form=True)
731
+ local_xg_home_form = get_stat(team1_home, 'Expected_xG', form=True)
732
+ local_gf_home_form = get_stat(team1_home, 'GF', form=True)
733
+ local_st_home_form = get_stat(team1_home, 'Standard_SoT', form=True) # Nuevo
734
+
735
+ local_ck_received_home = get_stat(team1_opp_home, 'Pass Types_CK')
736
+ local_gf_received_home = get_stat(team1_opp_home, 'GF')
737
+ local_xg_received_home = get_stat(team1_opp_home, 'Expected_xG')
738
+ local_st_received_home = get_stat(team1_opp_home, 'Standard_SoT') # Nuevo
739
+
740
+ local_ck_received_home_form = get_stat(team1_opp_home, 'Pass Types_CK', form=True)
741
+ local_gf_received_home_form = get_stat(team1_opp_home, 'GF', form=True)
742
+ local_xg_received_home_form = get_stat(team1_opp_home, 'Expected_xG', form=True)
743
+ local_st_received_home_form = get_stat(team1_opp_home, 'Standard_SoT', form=True) # Nuevo
744
+
745
+ # 1.2 Cuando juega FUERA (Away) - NUEVO
746
+ local_ck_away = get_stat(team1_away, 'Pass Types_CK')
747
+ local_xg_away = get_stat(team1_away, 'Expected_xG')
748
+ local_gf_away = get_stat(team1_away, 'GF')
749
+ local_st_away = get_stat(team1_away, 'Standard_SoT') # Nuevo
750
+
751
+ local_ck_away_form = get_stat(team1_away, 'Pass Types_CK', form=True)
752
+ local_xg_away_form = get_stat(team1_away, 'Expected_xG', form=True)
753
+ local_gf_away_form = get_stat(team1_away, 'GF', form=True)
754
+ local_st_away_form = get_stat(team1_away, 'Standard_SoT', form=True) # Nuevo
755
+
756
+ local_ck_received_away = get_stat(team1_opp_away, 'Pass Types_CK')
757
+ local_gf_received_away = get_stat(team1_opp_away, 'GF')
758
+ local_xg_received_away = get_stat(team1_opp_away, 'Expected_xG')
759
+ local_st_received_away = get_stat(team1_opp_away, 'Standard_SoT') # Nuevo
760
+
761
+ local_ck_received_away_form = get_stat(team1_opp_away, 'Pass Types_CK', form=True)
762
+ local_gf_received_away_form = get_stat(team1_opp_away, 'GF', form=True)
763
+ local_xg_received_away_form = get_stat(team1_opp_away, 'Expected_xG', form=True)
764
+ local_st_received_away_form = get_stat(team1_opp_away, 'Standard_SoT', form=True) # Nuevo
765
+
766
+
767
+ # --- 2. EQUIPO VISITANTE (TEAM 2) ---
768
+
769
+ # 2.1 Cuando juega FUERA (Away)
770
+ away_ck_away = get_stat(team2_away, 'Pass Types_CK')
771
+ away_xg_away = get_stat(team2_away, 'Expected_xG')
772
+ away_gf_away = get_stat(team2_away, 'GF')
773
+ away_st_away = get_stat(team2_away, 'Standard_SoT') # Nuevo
774
+
775
+ away_ck_away_form = get_stat(team2_away, 'Pass Types_CK', form=True)
776
+ away_xg_away_form = get_stat(team2_away, 'Expected_xG', form=True)
777
+ away_gf_away_form = get_stat(team2_away, 'GF', form=True)
778
+ away_st_away_form = get_stat(team2_away, 'Standard_SoT', form=True) # Nuevo
779
+
780
+ away_ck_received_away = get_stat(team2_opp_away, 'Pass Types_CK')
781
+ away_gf_received_away = get_stat(team2_opp_away, 'GF')
782
+ away_xg_received_away = get_stat(team2_opp_away, 'Expected_xG')
783
+ away_st_received_away = get_stat(team2_opp_away, 'Standard_SoT') # Nuevo
784
+
785
+ away_ck_received_away_form = get_stat(team2_opp_away, 'Pass Types_CK', form=True)
786
+ away_gf_received_away_form = get_stat(team2_opp_away, 'GF', form=True)
787
+ away_xg_received_away_form = get_stat(team2_opp_away, 'Expected_xG', form=True)
788
+ away_st_received_away_form = get_stat(team2_opp_away, 'Standard_SoT', form=True) # Nuevo
789
+
790
+ # 2.2 Cuando juega en CASA (Home) - NUEVO
791
+ away_ck_home = get_stat(team2_home, 'Pass Types_CK')
792
+ away_xg_home = get_stat(team2_home, 'Expected_xG')
793
+ away_gf_home = get_stat(team2_home, 'GF')
794
+ away_st_home = get_stat(team2_home, 'Standard_SoT') # Nuevo
795
+
796
+ away_ck_home_form = get_stat(team2_home, 'Pass Types_CK', form=True)
797
+ away_xg_home_form = get_stat(team2_home, 'Expected_xG', form=True)
798
+ away_gf_home_form = get_stat(team2_home, 'GF', form=True)
799
+ away_st_home_form = get_stat(team2_home, 'Standard_SoT', form=True) # Nuevo
800
+
801
+ away_ck_received_home = get_stat(team2_opp_home, 'Pass Types_CK')
802
+ away_gf_received_home = get_stat(team2_opp_home, 'GF')
803
+ away_xg_received_home = get_stat(team2_opp_home, 'Expected_xG')
804
+ away_st_received_home = get_stat(team2_opp_home, 'Standard_SoT') # Nuevo
805
+
806
+ away_ck_received_home_form = get_stat(team2_opp_home, 'Pass Types_CK', form=True)
807
+ away_gf_received_home_form = get_stat(team2_opp_home, 'GF', form=True)
808
+ away_xg_received_home_form = get_stat(team2_opp_home, 'Expected_xG', form=True)
809
+ away_st_received_home_form = get_stat(team2_opp_home, 'Standard_SoT', form=True) # Nuevo
810
+
811
+
812
+ # --- TOTALES ESPERADOS (Contexto del partido actual: Local en Casa vs Visitante Fuera) ---
813
  partido_ck_esperado = local_ck_home + away_ck_away
814
+ partido_gf_esperado = local_gf_home + away_gf_away
815
+ partido_xg_esperado = local_xg_home + away_xg_away
816
+ partido_st_esperado = local_st_home + away_st_away # Nuevo
817
+
818
+ partido_ck_esperado_form = local_ck_home_form + away_ck_away_form
819
+ partido_gf_esperado_form = local_gf_home_form + away_gf_away_form
820
+ partido_xg_esperado_form = local_xg_home_form + away_xg_away_form
821
+ partido_st_esperado_form = local_st_home_form + away_st_away_form # Nuevo
822
+
823
+ # --- H2H ---
824
+ h2h_ck_local = get_stat(team1_h2h, 'Pass Types_CK')
825
+ h2h_ck_away = get_stat(team2_h2h, 'Pass Types_CK')
826
+ h2h_ck_total = h2h_ck_local + h2h_ck_away
827
+
828
+ h2h_gf_local = get_stat(team1_h2h, 'GF')
829
+ h2h_gf_away = get_stat(team2_h2h, 'GF')
830
+ h2h_gf_total = h2h_gf_local + h2h_gf_away
831
+
832
+ h2h_xg_local = get_stat(team1_h2h, 'Expected_xG')
833
+ h2h_xg_away = get_stat(team2_h2h, 'Expected_xG')
834
+ h2h_xg_total = h2h_xg_local + h2h_xg_away
835
+
836
+ h2h_st_local = get_stat(team1_h2h, 'Standard_SoT') # Nuevo
837
+ h2h_st_away = get_stat(team2_h2h, 'Standard_SoT') # Nuevo
838
+ h2h_st_total = h2h_st_local + h2h_st_away # Nuevo
839
+
840
+ # --- H2H DATAFRAME ---
841
+ h2h_matches = []
842
+
843
+ # Asegurar que trabajamos con copias limpias
844
+ t1_h2h = team1_h2h.copy()
845
+ t2_h2h = team2_h2h.copy()
846
+
847
+ # Iterar sobre los partidos del equipo local (team1)
848
+ for idx, row1 in t1_h2h.iterrows():
849
+ # Buscar el partido correspondiente en el df del visitante (team2)
850
+ # Coincidencia por temporada y jornada
851
+ row2_match = t2_h2h[
852
+ (t2_h2h['season'] == row1['season']) &
853
+ (t2_h2h['round'] == row1['round'])
854
+ ]
855
+
856
+ if not row2_match.empty:
857
+ row2 = row2_match.iloc[0]
858
+
859
+ # Determinar quién jugó en casa en ese partido histórico
860
+ if row1['venue'] == 'Home':
861
+ match_home_team = local
862
+ match_away_team = visitante
863
+ else:
864
+ match_home_team = visitante
865
+ match_away_team = local
866
+
867
+ match_data = {
868
+ 'season': row1['season'],
869
+ 'round': int(row1['round']) if pd.notna(row1['round']) else 0,
870
+ 'match_home_team': match_home_team,
871
+ 'match_away_team': match_away_team,
872
+
873
+ # Datos del equipo que es LOCAL en la predicción actual (team1)
874
+ 'local_team_stats': {
875
+ 'team': local,
876
+ 'venue': row1['venue'],
877
+ 'goals': int(row1['GF']),
878
+ 'corners': int(row1['Pass Types_CK']),
879
+ 'xg': float(row1['Expected_xG']),
880
+ 'sot': int(row1['Standard_SoT']) if 'Standard_SoT' in row1 else 0
881
+ },
882
+
883
+ # Datos del equipo que es VISITANTE en la predicción actual (team2)
884
+ 'away_team_stats': {
885
+ 'team': visitante,
886
+ 'venue': row2['venue'],
887
+ 'goals': int(row2['GF']),
888
+ 'corners': int(row2['Pass Types_CK']),
889
+ 'xg': float(row2['Expected_xG']),
890
+ 'sot': int(row2['Standard_SoT']) if 'Standard_SoT' in row2 else 0
891
+ }
892
+ }
893
+ h2h_matches.append(match_data)
894
+
895
+ print(h2h_matches)
896
+
897
 
898
  # ===========================
899
  # ✅ MOSTRAR PROBABILIDADES EXACTAS
 
975
  df_varianza_temp = analizar_fiabilidad_equipos(df_database, temporada=temporada, min_partidos=3)
976
  riesgo = obtener_fiabilidad_partido(local, visitante, df_varianza_temp)
977
 
 
 
 
 
 
978
 
979
  # ===========================
980
  # RETORNAR DICCIONARIO COMPLETO
 
982
 
983
  return {
984
  "prediccion": round(prediccion, 2),
985
+ "prediccion_local": round(prediccion_local, 2),
986
+ "prediccion_away": round(prediccion_away, 2),
987
+ "prediccion_xg": round(prediccion_xg, 2),
988
+ "prediccion_xg_local": round(prediccion_xg_local, 2),
989
+ "prediccion_xg_away": round(prediccion_xg_away, 2),
990
+ "prediccion_gf": round(prediccion_gf, 2),
991
+ "prediccion_gf_local": round(prediccion_gf_local, 2),
992
+ "prediccion_gf_away": round(prediccion_gf_away, 2),
993
+ "prediccion_st": round(prediccion_st, 2),
994
+ "prediccion_st_local": round(prediccion_st_local, 2),
995
+ "prediccion_st_away": round(prediccion_st_away, 2),
996
  "local": local,
997
  "visitante": visitante,
998
  "ppp_local": local_ppp,
999
  "ppp_away": away_ppp,
1000
  "ppp_diff": ppp_diff,
1001
+ "h2h_matches": h2h_matches, # Nuevo
1002
  "riesgo": riesgo,
1003
+ "stats_ck": {
1004
+ # Local Team Stats
1005
+ "local_ck_home": local_ck_home,
1006
+ "local_ck_away": local_ck_away, # Nuevo
1007
+ "local_ck_received_home": local_ck_received_home,
1008
+ "local_ck_received_away": local_ck_received_away, # Nuevo
1009
+
1010
+ "local_ck_home_form": local_ck_home_form,
1011
+ "local_ck_away_form": local_ck_away_form, # Nuevo
1012
+ "local_ck_received_home_form": local_ck_received_home_form,
1013
+ "local_ck_received_away_form": local_ck_received_away_form, # Nuevo
1014
+
1015
+ # Away Team Stats
1016
+ "away_ck_home": away_ck_home, # Nuevo
1017
+ "away_ck_away": away_ck_away,
1018
+ "away_ck_received_home": away_ck_received_home, # Nuevo
1019
+ "away_ck_received_away": away_ck_received_away,
1020
+
1021
+ "away_ck_home_form": away_ck_home_form, # Nuevo
1022
+ "away_ck_away_form": away_ck_away_form,
1023
+ "away_ck_received_home_form": away_ck_received_home_form, # Nuevo
1024
+ "away_ck_received_away_form": away_ck_received_away_form,
1025
+
1026
+ # Totals
1027
+ "h2h_ck_total": h2h_ck_total,
1028
+ "partido_ck_esperado": partido_ck_esperado,
1029
+ "partido_ck_esperado_form": partido_ck_esperado_form
1030
+ },
1031
+ "stats_gf": {
1032
+ # Local Team Stats
1033
+ "local_gf_home": local_gf_home,
1034
+ "local_gf_away": local_gf_away, # Nuevo
1035
+ "local_gf_received_home": local_gf_received_home,
1036
+ "local_gf_received_away": local_gf_received_away, # Nuevo
1037
+
1038
+ "local_gf_home_form": local_gf_home_form,
1039
+ "local_gf_away_form": local_gf_away_form, # Nuevo
1040
+ "local_gf_received_home_form": local_gf_received_home_form,
1041
+ "local_gf_received_away_form": local_gf_received_away_form, # Nuevo
1042
+
1043
+ # Away Team Stats
1044
+ "away_gf_home": away_gf_home, # Nuevo
1045
+ "away_gf_away": away_gf_away,
1046
+ "away_gf_received_home": away_gf_received_home, # Nuevo
1047
+ "away_gf_received_away": away_gf_received_away,
1048
+
1049
+ "away_gf_home_form": away_gf_home_form, # Nuevo
1050
+ "away_gf_away_form": away_gf_away_form,
1051
+ "away_gf_received_home_form": away_gf_received_home_form, # Nuevo
1052
+ "away_gf_received_away_form": away_gf_received_away_form,
1053
+
1054
+ # Totals
1055
+ "h2h_gf_total": h2h_gf_total,
1056
+ "partido_gf_esperado": partido_gf_esperado,
1057
+ "partido_gf_esperado_form": partido_gf_esperado_form,
1058
+ },
1059
+ "stats_xg": {
1060
+ # Local Team Stats
1061
+ "local_xg_home": local_xg_home,
1062
+ "local_xg_away": local_xg_away, # Nuevo
1063
+ "local_xg_received_home": local_xg_received_home,
1064
+ "local_xg_received_away": local_xg_received_away, # Nuevo
1065
+
1066
+ "local_xg_home_form": local_xg_home_form,
1067
+ "local_xg_away_form": local_xg_away_form, # Nuevo
1068
+ "local_xg_received_home_form": local_xg_received_home_form,
1069
+ "local_xg_received_away_form": local_xg_received_away_form, # Nuevo
1070
+
1071
+ # Away Team Stats
1072
+ "away_xg_home": away_xg_home, # Nuevo
1073
+ "away_xg_away": away_xg_away,
1074
+ "away_xg_received_home": away_xg_received_home, # Nuevo
1075
+ "away_xg_received_away": away_xg_received_away,
1076
+
1077
+ "away_xg_home_form": away_xg_home_form, # Nuevo
1078
+ "away_xg_away_form": away_xg_away_form,
1079
+ "away_xg_received_home_form": away_xg_received_home_form, # Nuevo
1080
+ "away_xg_received_away_form": away_xg_received_away_form,
1081
+
1082
+ # Totals
1083
+ "h2h_xg_total": h2h_xg_total,
1084
+ "partido_xg_esperado": partido_xg_esperado,
1085
+ "partido_xg_esperado_form": partido_xg_esperado_form
1086
+ },
1087
+ "stats_st": { # Nuevo bloque
1088
+ # Local Team Stats
1089
+ "local_st_home": local_st_home,
1090
+ "local_st_away": local_st_away,
1091
+ "local_st_received_home": local_st_received_home,
1092
+ "local_st_received_away": local_st_received_away,
1093
+
1094
+ "local_st_home_form": local_st_home_form,
1095
+ "local_st_away_form": local_st_away_form,
1096
+ "local_st_received_home_form": local_st_received_home_form,
1097
+ "local_st_received_away_form": local_st_received_away_form,
1098
+
1099
+ # Away Team Stats
1100
+ "away_st_home": away_st_home,
1101
+ "away_st_away": away_st_away,
1102
+ "away_st_received_home": away_st_received_home,
1103
+ "away_st_received_away": away_st_received_away,
1104
+
1105
+ "away_st_home_form": away_st_home_form,
1106
+ "away_st_away_form": away_st_away_form,
1107
+ "away_st_received_home_form": away_st_received_home_form,
1108
+ "away_st_received_away_form": away_st_received_away_form,
1109
+
1110
+ # Totals
1111
+ "h2h_st_total": h2h_st_total,
1112
+ "partido_st_esperado": partido_st_esperado,
1113
+ "partido_st_esperado_form": partido_st_esperado_form,
1114
  },
1115
  "probabilidades_exactas": analisis['exactas'],
1116
  "probabilidades_over": analisis['over'],
 
1363
 
1364
  # URLs de descarga directa (raw.githubusercontent.com)
1365
  base_url = "https://raw.githubusercontent.com/danielsaed/futbol_corners_forecast/refs/heads/main/models"
1366
+ #model_url = f"{base_url}/xgboost_corners_v4_retrain.pkl"
1367
+ models_map = {
1368
+ "xgb_model": "xgboost_corners_v4_retrain_PRODUCTION.pkl",
1369
+ "xgb_model_local": "xgboost_corners_v4_retrain_local_PRODUCTION.pkl",
1370
+ "xgb_model_away": "xgboost_corners_v4_retrain_away_PRODUCTION.pkl",
1371
+ "xgb_model_xg": "xgboost_corners_v4_retrain_eg_total_PRODUCTION.pkl",
1372
+ "xgb_model_xg_local": "xgboost_corners_v4_retrain_eg_local_PRODUCTION.pkl",
1373
+ "xgb_model_xg_away": "xgboost_corners_v4_retrain_eg_away_PRODUCTION.pkl",
1374
+ "xgb_model_gf": "xgboost_corners_v4_retrain_gol_total_PRODUCTION.pkl",
1375
+ "xgb_model_gf_local": "xgboost_corners_v4_retrain_gol_local_PRODUCTION.pkl",
1376
+ "xgb_model_gf_away": "xgboost_corners_v4_retrain_gol_away_PRODUCTION.pkl",
1377
+ "xgb_model_st": "xgboost_corners_v4_retrain_st_total_PRODUCTION.pkl",
1378
+ "xgb_model_st_local": "xgboost_corners_v4_retrain_st_local_PRODUCTION.pkl",
1379
+ "xgb_model_st_away": "xgboost_corners_v4_retrain_st_away_PRODUCTION.pkl",
1380
+ "scaler": "scaler_corners_v4_retrain_PRODUCTION.pkl"
1381
+ }
1382
 
1383
  try:
1384
+ for attr_name, filename in models_map.items():
1385
+ url = f"{base_url}/{filename}"
1386
+ print(f"📥 Descargando {attr_name} desde: {url}")
1387
+
1388
+ # 1. Descargar
1389
+ response = requests.get(url, timeout=30)
1390
+ response.raise_for_status()
1391
+
1392
+ # 2. Guardar temporalmente
1393
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.pkl') as tmp_file:
1394
+ tmp_file.write(response.content)
1395
+ tmp_path = tmp_file.name
1396
+
1397
+ # 3. Cargar con joblib y asignar al atributo de la clase
1398
+ try:
1399
+ loaded_obj = joblib.load(tmp_path)
1400
+ setattr(self, attr_name, loaded_obj)
1401
+ finally:
1402
+ # 4. Limpiar archivo temporal
1403
+ if os.path.exists(tmp_path):
1404
+ os.unlink(tmp_path)
1405
 
1406
+ print("✅ Todos los modelos cargados correctamente desde GitHub")
 
 
 
 
 
 
 
 
1407
 
1408
  except requests.exceptions.RequestException as e:
1409
  raise Exception(f"❌ Error descargando modelos: {str(e)}")
 
1438
  # Limpieza
1439
  self.df_dataset["season"] = self.df_dataset["season"].astype(str)
1440
  self.df_dataset["Performance_Save%"].fillna(0, inplace=True)
1441
+ self.df_dataset['date'] = pd.to_datetime(self.df_dataset['date'])
1442
+
1443
+ self.df_dataset = self.df_dataset.sort_values(by='date',ascending=True)
1444
 
1445
  print(f"✅ Total registros: {len(self.df_dataset)}")
1446
 
 
1483
  league_code=league_code,
1484
  df_database = self.df_dataset,
1485
  xgb_model = self.xgb_model,
1486
+ xgb_model_local= self.xgb_model_local,
1487
+ xgb_model_away = self.xgb_model_away,
1488
+ xgb_model_xg= self.xgb_model_xg,
1489
+ xgb_model_xg_local= self.xgb_model_xg_local,
1490
+ xgb_model_xg_away= self.xgb_model_xg_away,
1491
+ xgb_model_gf = self.xgb_model_gf,
1492
+ xgb_model_gf_local = self.xgb_model_gf_local,
1493
+ xgb_model_gf_away = self.xgb_model_gf_away,
1494
+ xgb_model_st = self.xgb_model_st,
1495
+ xgb_model_st_local = self.xgb_model_st_local,
1496
+ xgb_model_st_away = self.xgb_model_st_away,
1497
  scaler=self.scaler,
1498
  lst_years=self.lst_years
1499
  )