scouting / scoring.py
matias-cataife
Initial scouting Docker space
62f41ca
Raw
History Blame Contribute Delete
3.7 kB
"""
Scoring wrapper: takes a filtered DF and tunable weights, returns scored rows.
Reuses the existing functions in scouting_score.py.
"""
import sys
from functools import lru_cache
from pathlib import Path
import pandas as pd
# Allow importing scouting_score.py from the parent directory in dev. In the Docker
# image, scouting_score.py is copied into /app so this is a no-op.
_PARENT = Path(__file__).resolve().parent.parent
if str(_PARENT) not in sys.path:
sys.path.insert(0, str(_PARENT))
import scouting_score as ss
APP_DIR = Path(__file__).parent
# weights xlsx path — searched in DATA_DIR first, fallback to project root
def _weights_path():
import os
data_dir = Path(os.environ.get('DATA_DIR', APP_DIR.parent / 'data' / 'clean'))
candidates = [
data_dir / 'PESOS_POR_VARIABLE.xlsx',
data_dir / 'PESOS POR VARIABLE (BLOQUES).xlsx',
APP_DIR.parent / 'PESOS POR VARIABLE (BLOQUES).xlsx',
]
for c in candidates:
if c.exists():
return c
raise FileNotFoundError(f'weights xlsx not found; tried: {candidates}')
@lru_cache(maxsize=1)
def get_block_weights():
return ss.load_weights(_weights_path())
def score_with_weights(df: pd.DataFrame,
perf_weight=0.6, prof_weight=0.4,
w_age=0.35, w_eu=0.25, w_league=0.20, w_mv=0.20):
if df.empty:
return df.assign(performance_score=pd.NA, profile_score=pd.NA,
scouting_score=pd.NA, percentile_rank_in_position=pd.NA)
weights = get_block_weights()
df = df.copy()
df['mapped_position'] = df['Posicion'].map(ss.POSITION_MAP)
df = df[df['mapped_position'].notna()].copy()
df = ss.compute_percentile_ranks(df, weights)
df = ss.compute_performance_scores(df, weights)
def age_factor(a):
try:
a = float(a)
except (TypeError, ValueError):
return None
if pd.isna(a):
return None
if a < 23:
return 1.0
if a <= 25:
return 0.5
return 0.0
df['age_factor'] = df['_age_num'].apply(age_factor)
df['eu_nationality_factor'] = df['_eu'].astype(float)
def mv_factor(mv):
if mv is None or pd.isna(mv):
return None
if mv < 3:
return 1.0
if mv <= 5:
return 0.5
return -1
df['market_value_factor'] = df['mv_millions'].apply(mv_factor)
df['league_tier_factor'] = df['Competencia'].map(ss.LEAGUE_TIERS).fillna(ss.DEFAULT_LEAGUE_TIER)
weights_p = {'age': w_age, 'eu_nationality': w_eu, 'league_tier': w_league, 'market_value': w_mv}
def profile_score(row):
comps, w_total = [], 0
if row['age_factor'] is not None:
comps.append(weights_p['age'] * row['age_factor']); w_total += weights_p['age']
comps.append(weights_p['eu_nationality'] * row['eu_nationality_factor']); w_total += weights_p['eu_nationality']
comps.append(weights_p['league_tier'] * row['league_tier_factor']); w_total += weights_p['league_tier']
if row['market_value_factor'] is not None and row['market_value_factor'] >= 0:
comps.append(weights_p['market_value'] * row['market_value_factor']); w_total += weights_p['market_value']
return sum(comps) / w_total if w_total else 0
df['profile_score'] = df.apply(profile_score, axis=1)
df['scouting_score'] = (perf_weight * df['performance_score'].fillna(0)
+ prof_weight * df['profile_score'].fillna(0))
df['percentile_rank_in_position'] = (
df.groupby(['mapped_position', 'Temporada'])['performance_score'].rank(pct=True)
)
return df