Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from typing import Any | |
| import pandas as pd | |
| from models.batter_zone_store import load_batter_zone_store_metrics | |
| PITCH_FAMILY_MAP = { | |
| "4-seam fastball": "fastball", | |
| "four-seam fastball": "fastball", | |
| "fastball": "fastball", | |
| "sinker": "fastball", | |
| "cutter": "fastball", | |
| "slider": "breaking", | |
| "sweeper": "breaking", | |
| "curveball": "breaking", | |
| "knuckle curve": "breaking", | |
| "slurve": "breaking", | |
| "changeup": "offspeed", | |
| "splitter": "offspeed", | |
| "forkball": "offspeed", | |
| "split-finger": "offspeed", | |
| "circle change": "offspeed", | |
| } | |
| def normalize_pitch_family(pitch_name: Any) -> str: | |
| text = str(pitch_name or "").strip().lower() | |
| if text in {"", "nan", "none"}: | |
| return "unknown" | |
| return PITCH_FAMILY_MAP.get(text, "unknown") | |
| def classify_zone_bucket(plate_x: Any, plate_z: Any) -> str: | |
| try: | |
| x = float(plate_x) | |
| z = float(plate_z) | |
| except Exception: | |
| return "unknown" | |
| zone_left = -0.83 | |
| zone_right = 0.83 | |
| zone_bottom = 1.50 | |
| zone_top = 3.50 | |
| if zone_left <= x <= zone_right and zone_bottom <= z <= zone_top: | |
| inner_left = -0.45 | |
| inner_right = 0.45 | |
| inner_bottom = 1.90 | |
| inner_top = 3.10 | |
| if inner_left <= x <= inner_right and inner_bottom <= z <= inner_top: | |
| return "heart" | |
| return "shadow" | |
| chase_left = -1.20 | |
| chase_right = 1.20 | |
| chase_bottom = 1.10 | |
| chase_top = 3.90 | |
| if chase_left <= x <= chase_right and chase_bottom <= z <= chase_top: | |
| return "chase" | |
| return "waste" | |
| def build_batter_zone_feature_row( | |
| statcast_df: pd.DataFrame, | |
| player_name: str, | |
| ) -> dict[str, Any]: | |
| store_metrics = load_batter_zone_store_metrics(player_name) | |
| row: dict[str, Any] = { | |
| "player_name": player_name, | |
| "zone_sample_size": store_metrics.get("stored_zone_sample_size", 0), | |
| } | |
| pitch_families = ["fastball", "breaking", "offspeed"] | |
| zones = ["heart", "shadow", "chase", "waste"] | |
| for family in pitch_families: | |
| for zone in zones: | |
| row[f"hr_prob_{family}_{zone}"] = store_metrics.get( | |
| f"stored_hr_prob_{family}_{zone}" | |
| ) | |
| row[f"hit_prob_{family}_{zone}"] = store_metrics.get( | |
| f"stored_hit_prob_{family}_{zone}" | |
| ) | |
| row[f"tb2p_prob_{family}_{zone}"] = store_metrics.get( | |
| f"stored_tb2p_prob_{family}_{zone}" | |
| ) | |
| row[f"whiff_prob_{family}_{zone}"] = store_metrics.get( | |
| f"stored_whiff_prob_{family}_{zone}" | |
| ) | |
| row[f"damage_prob_{family}_{zone}"] = store_metrics.get( | |
| f"stored_damage_prob_{family}_{zone}" | |
| ) | |
| row[f"sample_size_{family}_{zone}"] = store_metrics.get( | |
| f"stored_sample_size_{family}_{zone}" | |
| ) | |
| return row |