| """ |
| 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 |
|
|
| |
| |
| _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 |
|
|
| |
| 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 |
|
|