Skip to content

Commit 88db396

Browse files
Refactor: Move scripts to united_passing package structure
1 parent 9a86e87 commit 88db396

11 files changed

Lines changed: 179 additions & 86 deletions

File tree

analyze_mediocampo.py

Lines changed: 0 additions & 22 deletions
This file was deleted.

clean_data.py

Lines changed: 0 additions & 22 deletions
This file was deleted.

data_load.py

Lines changed: 0 additions & 16 deletions
This file was deleted.

plot_pases.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

pyproject.toml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[build-system]
2+
requires = ["setuptools>=61.0"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "united_passing"
7+
version = "0.1.0"
8+
authors = [
9+
{ name="Alvaro Salinas Ortiz", email="alvarosalinasortiz@gmail.com" },
10+
]
11+
description = "Análisis de eficiencia de pases del Manchester United"
12+
readme = "README.md"
13+
requires-python = ">=3.8"
14+
classifiers = [
15+
"Programming Language :: Python :: 3",
16+
"License :: OSI Approved :: MIT License",
17+
"Operating System :: OS Independent",
18+
]
19+
dependencies = [
20+
"pandas",
21+
"matplotlib",
22+
"seaborn",
23+
]
24+
25+
[project.urls]
26+
"Homepage" = "https://github.com/alvarosalinaso/united-passing-efficiency-24-25"
27+
28+
[tool.pytest.ini_options]
29+
minversion = "6.0"
30+
addopts = "-ra -q"
31+
testpaths = [
32+
"tests",
33+
]

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ pandas
22
pytest
33
matplotlib
44
seaborn
5+
6+
pytest
7+
matplotlib
8+
seaborn

src/united_passing/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"""Paquete de análisis de pases del Manchester United."""
2+
__version__ = '0.1.0'

src/united_passing/analysis.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Módulo de análisis de datos."""
2+
import pandas as pd
3+
4+
def top_by_prog_ratio(df: pd.DataFrame, top_n: int = 10) -> pd.DataFrame:
5+
"""
6+
Obtiene el top N de jugadores ordenados por Prog_Ratio.
7+
Calcula Prog_Ratio si no existe (Prog / Cmp).
8+
"""
9+
df = df.copy()
10+
11+
# Calcular Prog_Ratio si no está presente y tenemos las columnas necesarias
12+
if 'Prog_Ratio' not in df.columns:
13+
if 'Prog' in df.columns and 'Cmp' in df.columns:
14+
# Evitar división por cero
15+
df['Prog_Ratio'] = df.apply(
16+
lambda x: x['Prog'] / x['Cmp'] if x['Cmp'] > 0 else 0, axis=1
17+
)
18+
else:
19+
# Si no podemos calcularlo y no está, devolvemos vacío o error
20+
# Por compatibilidad con el script anterior, asumiremos que el input puede ser el 'reporte' que ya lo tenga
21+
pass
22+
23+
if 'Prog_Ratio' in df.columns:
24+
df_sorted = df.sort_values('Prog_Ratio', ascending=False)
25+
return df_sorted.head(top_n)
26+
else:
27+
# Fallback si no hay métrica
28+
return df.head(top_n)
29+
30+
def filter_midfielders(df: pd.DataFrame) -> pd.DataFrame:
31+
"""
32+
Filtra jugadores que son mediocampistas (MF).
33+
Asume columna 'Pos' con valores como 'MF', 'MFDF', etc.
34+
"""
35+
if 'Pos' in df.columns:
36+
return df[df['Pos'].str.contains('MF', na=False)]
37+
return df

src/united_passing/data.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Módulo para carga y limpieza de datos."""
2+
import pandas as pd
3+
from pathlib import Path
4+
5+
def load_data(passes_path: str = 'passing.csv', report_path: str = 'reporte_mediocampo.csv') -> tuple:
6+
"""Carga los datos de pases y el reporte de mediocampo desde CSV."""
7+
# Usar Path para mayor robustez, asumiendo ejecución desde la raíz o configurando paths relativos
8+
base_path = Path.cwd()
9+
p_path = base_path / passes_path
10+
r_path = base_path / report_path
11+
12+
if not p_path.exists():
13+
raise FileNotFoundError(f"No se encontró el archivo: {p_path}")
14+
15+
df = pd.read_csv(p_path)
16+
17+
if r_path.exists():
18+
report = pd.read_csv(r_path)
19+
else:
20+
report = pd.DataFrame() # Return empty if report doesn't exist yet
21+
22+
return df, report
23+
24+
def clean_passes(df: pd.DataFrame) -> pd.DataFrame:
25+
"""Limpia el DataFrame de pases."""
26+
df = df.copy()
27+
# Remover columnas completamente vacías
28+
df.dropna(axis=1, how='all', inplace=True)
29+
# Reemplazar cadenas vacías por NaN (usando forward fill para compatibilidad o pd.NA)
30+
df = df.replace(r'^\s*$', pd.NA, regex=True)
31+
32+
# Intentar convertir columnas numéricas conocidas
33+
numeric_cols = ['90s', 'Cmp', 'Att', 'TotDist', 'PrgDist', 'Ast', 'xA', 'KP', '1/3', 'PPA', 'CrsPA', 'Prog']
34+
for col in numeric_cols:
35+
if col in df.columns:
36+
df[col] = pd.to_numeric(df[col], errors='coerce')
37+
38+
return df

src/united_passing/plot.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Módulo para visualización de datos."""
2+
import matplotlib.pyplot as plt
3+
import pandas as pd
4+
try:
5+
import seaborn as sns
6+
HAS_SEABORN = True
7+
except ImportError:
8+
HAS_SEABORN = False
9+
10+
def plot_top_passers(df: pd.DataFrame, metric: str = 'Cmp', top_n: int = 10, out_path: str = 'grafico_pases_united.png'):
11+
"""Genera un gráfico de barras de los top N jugadores según una métrica."""
12+
if metric not in df.columns:
13+
raise KeyError(f'La columna {metric} no existe en los datos.')
14+
15+
# Ordenar y seleccionar top
16+
df_top = df.sort_values(metric, ascending=False).head(top_n)
17+
18+
plt.figure(figsize=(10, 6))
19+
20+
if HAS_SEABORN:
21+
sns.set_theme(style="whitegrid")
22+
sns.barplot(x=metric, y='Player', data=df_top, palette='viridis')
23+
else:
24+
# Fallback a matplotlib puro
25+
plt.barh(df_top['Player'][::-1], df_top[metric][::-1])
26+
27+
plt.xlabel(metric)
28+
plt.title(f'Top {top_n} jugadores por {metric}')
29+
plt.tight_layout()
30+
plt.savefig(out_path)
31+
plt.close()
32+
return out_path

0 commit comments

Comments
 (0)