Primer commit

This commit is contained in:
Tatiana Villa 2026-04-30 12:48:31 +02:00
parent 21012274cb
commit c8e351da00
15 changed files with 473 additions and 0 deletions

View File

@ -0,0 +1,21 @@
posicion,equipo,PJ,PG,PE,PP,GF,GC,DG,Pts,Racha1,Racha2,Racha3,Racha4,Racha5
1,Barcelona,33,28,1,4,87,30,57,85,G,G,G,G,G
2,Real Madrid,33,23,5,5,68,31,37,74,G,P,E,G,E
3,Villarreal,33,20,5,8,59,38,21,65,G,P,G,E,G
4,Atletico Madrid,33,18,6,9,56,37,19,60,P,P,P,P,G
5,Real Betis,33,12,14,7,49,41,8,50,P,E,E,G,E
6,Getafe,33,13,5,15,28,34,-6,44,G,G,P,G,P
7,Celta Vigo,33,11,11,11,45,43,2,44,P,G,P,P,P
8,Real Sociedad,33,11,10,12,52,52,0,43,P,G,E,P,E
9,Osasuna,33,11,9,13,39,40,-1,42,G,E,E,P,G
10,Athletic Club,33,12,5,16,36,48,-12,41,G,P,P,G,P
11,Rayo Vallecano,33,9,12,12,33,41,-8,39,P,G,P,G,E
12,Valencia,33,10,9,14,37,48,-11,39,G,P,P,E,G
13,Espanyol,33,10,9,14,37,49,-12,39,P,E,P,P,E
14,Elche,33,9,11,13,44,50,-6,38,G,P,G,G,G
15,Girona,33,9,11,13,36,50,-14,38,P,G,E,P,P
16,Deportivo Alaves,33,9,9,15,38,49,-11,36,G,E,E,P,G
17,Mallorca,33,9,8,16,41,51,-10,35,P,G,G,E,P
18,Sevilla,33,9,7,17,40,55,-15,34,P,P,G,P,P
19,Levante,33,8,9,16,37,50,-13,33,G,P,G,G,E
20,Real Oviedo,33,6,10,17,26,51,-25,28,P,G,G,E,P
1 posicion equipo PJ PG PE PP GF GC DG Pts Racha1 Racha2 Racha3 Racha4 Racha5
2 1 Barcelona 33 28 1 4 87 30 57 85 G G G G G
3 2 Real Madrid 33 23 5 5 68 31 37 74 G P E G E
4 3 Villarreal 33 20 5 8 59 38 21 65 G P G E G
5 4 Atletico Madrid 33 18 6 9 56 37 19 60 P P P P G
6 5 Real Betis 33 12 14 7 49 41 8 50 P E E G E
7 6 Getafe 33 13 5 15 28 34 -6 44 G G P G P
8 7 Celta Vigo 33 11 11 11 45 43 2 44 P G P P P
9 8 Real Sociedad 33 11 10 12 52 52 0 43 P G E P E
10 9 Osasuna 33 11 9 13 39 40 -1 42 G E E P G
11 10 Athletic Club 33 12 5 16 36 48 -12 41 G P P G P
12 11 Rayo Vallecano 33 9 12 12 33 41 -8 39 P G P G E
13 12 Valencia 33 10 9 14 37 48 -11 39 G P P E G
14 13 Espanyol 33 10 9 14 37 49 -12 39 P E P P E
15 14 Elche 33 9 11 13 44 50 -6 38 G P G G G
16 15 Girona 33 9 11 13 36 50 -14 38 P G E P P
17 16 Deportivo Alaves 33 9 9 15 38 49 -11 36 G E E P G
18 17 Mallorca 33 9 8 16 41 51 -10 35 P G G E P
19 18 Sevilla 33 9 7 17 40 55 -15 34 P P G P P
20 19 Levante 33 8 9 16 37 50 -13 33 G P G G E
21 20 Real Oviedo 33 6 10 17 26 51 -25 28 P G G E P

1
data/espana Submodule

@ -0,0 +1 @@
Subproject commit 98851d5d630896c679facf4fa693e48462566575

View File

@ -0,0 +1,21 @@
equipo,goles_favor,goles_contra,disparos_puerta,faltas,amarillas,rojas
Real Oviedo,26,51,109,426,67,8
Getafe CF,28,34,91,508,92,7
Rayo Vallecano,33,41,156,434,85,9
Girona FC,36,50,126,349,65,7
Athletic Club,36,48,149,450,66,7
Levante UD,37,50,116,420,75,4
Valencia CF,37,48,105,412,64,2
RCD Espanyol de Barcelona,37,49,134,455,74,5
Deportivo Alavés,38,49,130,501,71,5
CA Osasuna,39,40,126,445,80,6
Sevilla FC,40,55,113,481,92,5
RCD Mallorca,41,51,133,382,69,4
Elche CF,44,50,130,426,63,6
Celta,45,43,137,388,64,1
Real Betis,49,41,155,351,67,1
Real Sociedad,52,52,160,469,71,4
Atlético de Madrid,56,37,172,371,67,4
Villarreal CF,59,38,148,403,71,3
Real Madrid,68,31,231,330,58,7
FC Barcelona,87,30,233,309,50,2
1 equipo goles_favor goles_contra disparos_puerta faltas amarillas rojas
2 Real Oviedo 26 51 109 426 67 8
3 Getafe CF 28 34 91 508 92 7
4 Rayo Vallecano 33 41 156 434 85 9
5 Girona FC 36 50 126 349 65 7
6 Athletic Club 36 48 149 450 66 7
7 Levante UD 37 50 116 420 75 4
8 Valencia CF 37 48 105 412 64 2
9 RCD Espanyol de Barcelona 37 49 134 455 74 5
10 Deportivo Alavés 38 49 130 501 71 5
11 CA Osasuna 39 40 126 445 80 6
12 Sevilla FC 40 55 113 481 92 5
13 RCD Mallorca 41 51 133 382 69 4
14 Elche CF 44 50 130 426 63 6
15 Celta 45 43 137 388 64 1
16 Real Betis 49 41 155 351 67 1
17 Real Sociedad 52 52 160 469 71 4
18 Atlético de Madrid 56 37 172 371 67 4
19 Villarreal CF 59 38 148 403 71 3
20 Real Madrid 68 31 231 330 58 7
21 FC Barcelona 87 30 233 309 50 2

View File

@ -0,0 +1,19 @@
Predicciones para la jornada (con features de clasificación y estadísticas):
local visitante ... dif_rojas prediccion
0 Villarreal Levante ... 0.00 1
1 Valencia At. Madrid ... 0.00 2
2 Alavés Athletic Club ... -2.15 1
3 Osasuna Barcelona ... 0.00 2
4 Celta Elche ... -3.85 1
5 Getafe Rayo Vallecano ... -4.15 1
6 Betis R. Oviedo ... 0.00 X
7 Espanyol Real Madrid ... -2.15 2
8 Cultural Leonesa Cádiz ... 0.00 X
9 Castellón Córdoba ... 0.00 X
10 Eibar Málaga ... 0.00 X
11 Racing Santander Huesca ... 0.00 X
12 Sporting Ceuta ... 0.00 X
13 Las Palmas Valladolid ... 0.00 X
14 Sevilla Real Sociedad ... 0.85 2
[15 rows x 12 columns]

11
main.py Normal file
View File

@ -0,0 +1,11 @@
# main.py
"""
Script principal para predecir quinielas de fútbol español.
"""
def main():
print("Bienvenido al predictor de quinielas de fútbol español.")
# Aquí se cargarán los datos, el modelo y se harán predicciones
if __name__ == "__main__":
main()

BIN
models/modelo_rf.pkl Normal file

Binary file not shown.

15
partidos_jornada.txt Normal file
View File

@ -0,0 +1,15 @@
Villarreal - Levante
Valencia - At. Madrid
Alavés - Athletic Club
Osasuna - Barcelona
Celta - Elche
Getafe - Rayo Vallecano
Betis - R. Oviedo
Espanyol - Real Madrid
Cultural Leonesa - Cádiz
Castellón - Córdoba
Eibar - Málaga
Racing Santander - Huesca
Sporting - Ceuta
Las Palmas - Valladolid
Sevilla - Real Sociedad

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
pandas
requests
scikit-learn

View File

@ -0,0 +1,21 @@
# descargar_datos.py
"""
Script para descargar datos históricos de quinielas de fútbol español.
"""
import os
import requests
def descargar_datos(url, destino):
response = requests.get(url)
if response.status_code == 200:
with open(destino, 'wb') as f:
f.write(response.content)
print(f"Datos descargados en {destino}")
else:
print(f"Error al descargar datos: {response.status_code}")
if __name__ == "__main__":
# Ejemplo de URL de datos históricos (reemplazar por una fuente válida)
url = "https://www.example.com/quinielas_historico.csv"
destino = os.path.join(os.path.dirname(__file__), '../data/quinielas_historico.csv')
descargar_datos(url, destino)

View File

@ -0,0 +1,33 @@
# entrenar_modelo.py
"""
Entrena y evalúa un modelo simple para predecir quinielas (1/X/2).
"""
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, accuracy_score
import os
# Cargar datos
DATA_PATH = os.path.join(os.path.dirname(__file__), '../data/espana/partidos_todos.csv')
df = pd.read_csv(DATA_PATH)
# Features simples: diferencia de goles históricos entre local y visitante
# (Para un modelo más avanzado, se pueden agregar más features)
df['dif_goles'] = df['goles_local'] - df['goles_visitante']
# Features y etiquetas
X = df[['dif_goles']]
y = df['resultado']
# Separar en train/test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Modelo
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)
# Predicción y evaluación
y_pred = clf.predict(X_test)
print("Accuracy:", accuracy_score(y_test, y_pred))
print("\nClassification report:\n", classification_report(y_test, y_pred))

View File

@ -0,0 +1,56 @@
# predecir_jornada.py
"""
Predice los resultados de una jornada futura usando el modelo entrenado.
"""
import pandas as pd
import os
import joblib
from sklearn.ensemble import RandomForestClassifier
MODEL_PATH = os.path.join(os.path.dirname(__file__), '../models/modelo_rf.pkl')
DATA_PATH = os.path.join(os.path.dirname(__file__), '../data/espana/partidos_todos.csv')
PARTIDOS_PATH = os.path.join(os.path.dirname(__file__), '../partidos_jornada.txt')
# Cargar datos históricos
df_hist = pd.read_csv(DATA_PATH)
# Calcular medias históricas de goles local y visitante
media_goles_local = df_hist.groupby('local')['goles_local'].mean().to_dict()
media_goles_visitante = df_hist.groupby('visitante')['goles_visitante'].mean().to_dict()
# Cargar modelo entrenado o entrenar si no existe
if os.path.exists(MODEL_PATH):
clf = joblib.load(MODEL_PATH)
else:
df_hist['dif_goles'] = df_hist['goles_local'] - df_hist['goles_visitante']
X = df_hist[['dif_goles']]
y = df_hist['resultado']
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X, y)
joblib.dump(clf, MODEL_PATH)
# Leer partidos desde archivo
partidos = []
if os.path.exists(PARTIDOS_PATH):
with open(PARTIDOS_PATH, 'r', encoding='utf-8') as f:
for linea in f:
linea = linea.strip()
if not linea or '-' not in linea:
continue
local, visitante = [x.strip() for x in linea.split('-', 1)]
goles_local = media_goles_local.get(local, df_hist['goles_local'].mean())
goles_visitante = media_goles_visitante.get(visitante, df_hist['goles_visitante'].mean())
dif_goles = goles_local - goles_visitante
partidos.append({'local': local, 'visitante': visitante, 'dif_goles': dif_goles})
else:
print(f"No se encontró el archivo {PARTIDOS_PATH}")
if partidos:
df_pred = pd.DataFrame(partidos)
predicciones = clf.predict(df_pred[['dif_goles']])
df_pred['prediccion'] = predicciones
print("\nPredicciones para la jornada:")
print(df_pred[['local', 'visitante', 'prediccion']])
else:
print("No se introdujeron partidos.")

View File

@ -0,0 +1,83 @@
# predecir_jornada_mejorado.py
"""
Predice resultados de la quiniela usando features de la clasificación actual.
"""
import pandas as pd
import os
import joblib
from sklearn.ensemble import RandomForestClassifier
MODEL_PATH = os.path.join(os.path.dirname(__file__), '../models/modelo_rf.pkl')
DATA_PATH = os.path.join(os.path.dirname(__file__), '../data/espana/partidos_todos.csv')
PARTIDOS_PATH = os.path.join(os.path.dirname(__file__), '../partidos_jornada.txt')
CLASIF_PATH = os.path.join(os.path.dirname(__file__), '../data/clasificacion2026.csv')
# Cargar datos históricos y clasificación
clasif = pd.read_csv(CLASIF_PATH)
clasif.set_index('equipo', inplace=True)
df_hist = pd.read_csv(DATA_PATH)
# Cargar modelo entrenado o entrenar si no existe
if os.path.exists(MODEL_PATH):
clf = joblib.load(MODEL_PATH)
else:
df_hist['dif_goles'] = df_hist['goles_local'] - df_hist['goles_visitante']
X = df_hist[['dif_goles']]
y = df_hist['resultado']
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X, y)
joblib.dump(clf, MODEL_PATH)
# Leer partidos desde archivo
def leer_partidos(path):
partidos = []
with open(path, 'r', encoding='utf-8') as f:
for linea in f:
linea = linea.strip()
if not linea or '-' not in linea:
continue
local, visitante = [x.strip() for x in linea.split('-', 1)]
partidos.append({'local': local, 'visitante': visitante})
return partidos
partidos = leer_partidos(PARTIDOS_PATH)
# Generar features usando la clasificación
features = []
for p in partidos:
local = p['local']
visitante = p['visitante']
# Si el equipo no está en la clasificación, usar valores medios
if local in clasif.index:
pos_local = clasif.loc[local, 'posicion']
pts_local = clasif.loc[local, 'Pts']
dg_local = clasif.loc[local, 'DG']
else:
pos_local = clasif['posicion'].mean()
pts_local = clasif['Pts'].mean()
dg_local = clasif['DG'].mean()
if visitante in clasif.index:
pos_visit = clasif.loc[visitante, 'posicion']
pts_visit = clasif.loc[visitante, 'Pts']
dg_visit = clasif.loc[visitante, 'DG']
else:
pos_visit = clasif['posicion'].mean()
pts_visit = clasif['Pts'].mean()
dg_visit = clasif['DG'].mean()
features.append({
'local': local,
'visitante': visitante,
'dif_puntos': pts_local - pts_visit,
'dif_posicion': pos_visit - pos_local, # positivo si local va mejor
'dif_dg': dg_local - dg_visit
})
# Predecir usando el modelo (por ahora solo con dif_goles, pero mostramos features para extender)
df_pred = pd.DataFrame(features)
# Usar solo dif_puntos como feature para el modelo actual (puedes reentrenar el modelo con más features luego)
df_pred['dif_goles'] = df_pred['dif_puntos'] # placeholder para compatibilidad
predicciones = clf.predict(df_pred[['dif_goles']])
df_pred['prediccion'] = predicciones
print("Predicciones para la jornada (con features de clasificación):")
print(df_pred[['local', 'visitante', 'dif_puntos', 'dif_posicion', 'dif_dg', 'prediccion']])

View File

@ -0,0 +1,94 @@
# predecir_jornada_stats.py
"""
Predice resultados de la quiniela usando features de la clasificación y estadísticas por equipo.
"""
import pandas as pd
import os
import joblib
from sklearn.ensemble import RandomForestClassifier
MODEL_PATH = os.path.join(os.path.dirname(__file__), '../models/modelo_rf.pkl')
DATA_PATH = os.path.join(os.path.dirname(__file__), '../data/espana/partidos_todos.csv')
PARTIDOS_PATH = os.path.join(os.path.dirname(__file__), '../partidos_jornada.txt')
CLASIF_PATH = os.path.join(os.path.dirname(__file__), '../data/clasificacion2026.csv')
STATS_PATH = os.path.join(os.path.dirname(__file__), '../data/estadisticas_por_equipo.csv')
# Cargar datos
clasif = pd.read_csv(CLASIF_PATH)
clasif.set_index('equipo', inplace=True)
stats = pd.read_csv(STATS_PATH)
stats.set_index('equipo', inplace=True)
df_hist = pd.read_csv(DATA_PATH)
# Cargar modelo entrenado o entrenar si no existe
if os.path.exists(MODEL_PATH):
clf = joblib.load(MODEL_PATH)
else:
df_hist['dif_goles'] = df_hist['goles_local'] - df_hist['goles_visitante']
X = df_hist[['dif_goles']]
y = df_hist['resultado']
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X, y)
joblib.dump(clf, MODEL_PATH)
# Leer partidos desde archivo
def leer_partidos(path):
partidos = []
with open(path, 'r', encoding='utf-8') as f:
for linea in f:
linea = linea.strip()
if not linea or '-' not in linea:
continue
local, visitante = [x.strip() for x in linea.split('-', 1)]
partidos.append({'local': local, 'visitante': visitante})
return partidos
partidos = leer_partidos(PARTIDOS_PATH)
# Generar features usando la clasificación y estadísticas
features = []
for p in partidos:
local = p['local']
visitante = p['visitante']
# Clasificación
pos_local = clasif.loc[local, 'posicion'] if local in clasif.index else clasif['posicion'].mean()
pos_visit = clasif.loc[visitante, 'posicion'] if visitante in clasif.index else clasif['posicion'].mean()
pts_local = clasif.loc[local, 'Pts'] if local in clasif.index else clasif['Pts'].mean()
pts_visit = clasif.loc[visitante, 'Pts'] if visitante in clasif.index else clasif['Pts'].mean()
dg_local = clasif.loc[local, 'DG'] if local in clasif.index else clasif['DG'].mean()
dg_visit = clasif.loc[visitante, 'DG'] if visitante in clasif.index else clasif['DG'].mean()
# Estadísticas
gf_local = stats.loc[local, 'goles_favor'] if local in stats.index else stats['goles_favor'].mean()
gf_visit = stats.loc[visitante, 'goles_favor'] if visitante in stats.index else stats['goles_favor'].mean()
gc_local = stats.loc[local, 'goles_contra'] if local in stats.index else stats['goles_contra'].mean()
gc_visit = stats.loc[visitante, 'goles_contra'] if visitante in stats.index else stats['goles_contra'].mean()
disparos_local = stats.loc[local, 'disparos_puerta'] if local in stats.index else stats['disparos_puerta'].mean()
disparos_visit = stats.loc[visitante, 'disparos_puerta'] if visitante in stats.index else stats['disparos_puerta'].mean()
faltas_local = stats.loc[local, 'faltas'] if local in stats.index else stats['faltas'].mean()
faltas_visit = stats.loc[visitante, 'faltas'] if visitante in stats.index else stats['faltas'].mean()
amarillas_local = stats.loc[local, 'amarillas'] if local in stats.index else stats['amarillas'].mean()
amarillas_visit = stats.loc[visitante, 'amarillas'] if visitante in stats.index else stats['amarillas'].mean()
rojas_local = stats.loc[local, 'rojas'] if local in stats.index else stats['rojas'].mean()
rojas_visit = stats.loc[visitante, 'rojas'] if visitante in stats.index else stats['rojas'].mean()
features.append({
'local': local,
'visitante': visitante,
'dif_puntos': pts_local - pts_visit,
'dif_posicion': pos_visit - pos_local,
'dif_dg': dg_local - dg_visit,
'dif_gf': gf_local - gf_visit,
'dif_gc': gc_local - gc_visit,
'dif_disparos': disparos_local - disparos_visit,
'dif_faltas': faltas_local - faltas_visit,
'dif_amarillas': amarillas_local - amarillas_visit,
'dif_rojas': rojas_local - rojas_visit
})
# Por compatibilidad con el modelo actual, usamos solo dif_puntos (puedes reentrenar el modelo con más features luego)
df_pred = pd.DataFrame(features)
df_pred['dif_goles'] = df_pred['dif_puntos']
predicciones = clf.predict(df_pred[['dif_goles']])
df_pred['prediccion'] = predicciones
print("Predicciones para la jornada (con features de clasificación y estadísticas):")
print(df_pred[['local', 'visitante', 'dif_puntos', 'dif_posicion', 'dif_dg', 'dif_gf', 'dif_gc', 'dif_disparos', 'dif_faltas', 'dif_amarillas', 'dif_rojas', 'prediccion']])

View File

@ -0,0 +1,65 @@
# procesar_txt_a_csv.py
"""
Convierte archivos de texto de resultados de fútbol a formato CSV.
"""
import os
import re
import csv
from glob import glob
# Carpeta base de datos
BASE_DIR = os.path.join(os.path.dirname(__file__), '../data/espana')
# Expresión regular para partidos
PARTIDO_REGEX = re.compile(r"\s*(\d{1,2}\.\d{2})?\s*([\w .ÁÉÍÓÚÑáéíóúñB]+)\s+v\s+([\w .ÁÉÍÓÚÑáéíóúñB]+)\s+(\d+)-(\d+)")
# Expresión regular para fecha de jornada
FECHA_REGEX = re.compile(r"\s*(\d{1,2}\.\d{2})?\s*([A-Za-z]{3,9})\s*(\w+)?")
def procesar_archivo_txt(ruta_txt, temporada, division, salida_csv):
with open(ruta_txt, 'r', encoding='utf-8') as f:
lines = f.readlines()
jornada = None
fecha = None
partidos = []
for line in lines:
line = line.strip()
if line.startswith('» Matchday'):
jornada = line.split()[-1]
elif re.match(r"\d{2}\.\d{2}", line) or line.startswith(('Sat', 'Sun', 'Mon', 'Fri', 'Thu', 'Wed', 'Tue')):
fecha = line
elif 'v' in line and '-' in line:
m = PARTIDO_REGEX.search(line)
if m:
equipo_local = m.group(2).strip()
equipo_visitante = m.group(3).strip()
goles_local = int(m.group(4))
goles_visitante = int(m.group(5))
if goles_local > goles_visitante:
resultado = '1'
elif goles_local < goles_visitante:
resultado = '2'
else:
resultado = 'X'
partidos.append([
temporada, division, jornada, fecha, equipo_local, equipo_visitante, goles_local, goles_visitante, resultado
])
with open(salida_csv, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['temporada', 'division', 'jornada', 'fecha', 'local', 'visitante', 'goles_local', 'goles_visitante', 'resultado'])
writer.writerows(partidos)
print(f"Procesado {ruta_txt} -> {salida_csv}")
if __name__ == "__main__":
# Procesar todos los archivos txt de todas las temporadas y divisiones
for temporada in os.listdir(BASE_DIR):
temp_path = os.path.join(BASE_DIR, temporada)
if os.path.isdir(temp_path):
for archivo in os.listdir(temp_path):
if archivo.endswith('.txt'):
division = archivo.split('-')[1].replace('.txt', '') if '-' in archivo else '1'
ruta_txt = os.path.join(temp_path, archivo)
salida_csv = os.path.join(temp_path, archivo.replace('.txt', '.csv'))
procesar_archivo_txt(ruta_txt, temporada, division, salida_csv)

View File

@ -0,0 +1,30 @@
# unir_y_analizar_csv.py
"""
Une todos los CSV de partidos en uno solo y analiza la cantidad de datos.
"""
import os
import pandas as pd
from glob import glob
BASE_DIR = os.path.join(os.path.dirname(__file__), '../data/espana')
SALIDA = os.path.join(BASE_DIR, 'partidos_todos.csv')
# Buscar todos los CSV de partidos
csvs = glob(os.path.join(BASE_DIR, '*', '*.csv'))
# Unir todos los CSV
dfs = []
for csv_file in csvs:
df = pd.read_csv(csv_file)
dfs.append(df)
df_total = pd.concat(dfs, ignore_index=True)
df_total.to_csv(SALIDA, index=False)
# Análisis básico
total_partidos = len(df_total)
print(f"Total de partidos: {total_partidos}")
print("Primeras filas:")
print(df_total.head())
print("Distribución de resultados:")
print(df_total['resultado'].value_counts())