Download src/analytics/diagnostics.py from kr4phy/SchoolSustainabilityPrediction: direct link, hf CLI and curl.
- Browser
- Download file 8.88 kB
-
https://huggingface.co/spaces/kr4phy/SchoolSustainabilityPrediction/resolve/main/src/analytics/diagnostics.py
- Command line
-
hf download hf://spaces/kr4phy/SchoolSustainabilityPrediction/src/analytics/diagnostics.py
-
curl -L -o diagnostics.py https://huggingface.co/spaces/kr4phy/SchoolSustainabilityPrediction/resolve/main/src/analytics/diagnostics.py
8.88 kB
| """ | |
| ์ข ํฉ ์ง๋จ ์์ง ๋ชจ๋. | |
| ์์ธก(GranitePredictor), XAI(ShapExplainer), ์ ์(SustainabilityScorer) ๋ฅผ | |
| ํตํฉํ์ฌ ๋จ์ผ API ํธ์ถ๋ก ํ๊ต ์ ์ฒด ์ง๋จ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค. | |
| ๋ํ ์ ์ฑ ์๋ฎฌ๋ ์ด์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค: | |
| - ๊ต์ฌ 1์ธ๋น ํ์ ์ ๊ฐ์ ์๋๋ฆฌ์ค | |
| - ๋ณตํฉ์์ค ๋์ ์๋๋ฆฌ์ค | |
| - ๊ธฐ๊ฐ์ ๊ต์ ์ ๊ท์ง ์ ํ ์๋๋ฆฌ์ค | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from dataclasses import dataclass, field | |
| from typing import Any | |
| import pandas as pd | |
| from src.analytics.predictor import ForecastResult, GranitePredictor | |
| from src.analytics.scorer import SustainabilityScore, SustainabilityScorer | |
| from src.analytics.xai import ShapExplainer, ShapResult | |
| logger = logging.getLogger(__name__) | |
| # ์๋๋ฆฌ์ค ์ ์ (key: ์๋๋ฆฌ์คID, value: ๋ณ๊ฒฝ ํญ๋ชฉ) | |
| SIMULATION_SCENARIOS: dict[str, dict[str, Any]] = { | |
| "improve_teacher_ratio": { | |
| "label": "๊ต์ฌ 1์ธ๋น ํ์ ์ ๊ฐ์ ", | |
| "description": "๊ธฐ๊ฐ์ ๊ต์์ ์ ๊ท์ง์ผ๋ก ์ ํํ๊ณ ๊ต์์ 1๋ช ์ถ๊ฐ ๋ฐฐ์นํฉ๋๋ค.", | |
| "delta": {"teacher_count": 1, "temp_teacher_count": -1}, | |
| }, | |
| "introduce_complex_facility": { | |
| "label": "๋ณตํฉ์์ค ๋์ ", | |
| "description": "๋์๊ดยท์ฒด์ก๊ด ๋ฑ ๋ณตํฉ์์ค์ ๋์ ํ์ฌ ์ง์ญ ์ ์ ํจ๊ณผ๋ฅผ ๋ฐ์ํฉ๋๋ค.", | |
| "delta": {"established_year_delta": 20, "transfer_net_avg": 3}, | |
| }, | |
| "community_revitalization": { | |
| "label": "์ง์ญ ์ฐ๊ณ ๊ฐํ", | |
| "description": "์ง์ญ ์ฌ์ ๊ฑฐ์ ์ผ๋ก ์ ํํ์ฌ ์๋ฉธ์ํ์ง์๋ฅผ ๊ฐ์ ํฉ๋๋ค.", | |
| "delta": {"population_risk_index": 0.3}, | |
| }, | |
| } | |
| class DiagnosticsResult: | |
| """์ข ํฉ ์ง๋จ ๊ฒฐ๊ณผ ์ปจํ ์ด๋.""" | |
| schul_code: str | |
| school_name: str | |
| status_label: str | |
| status_code: int | |
| sustainability_score: float | |
| forecast: ForecastResult | |
| shap_result: ShapResult | |
| score_detail: SustainabilityScore | |
| simulations: dict[str, dict[str, Any]] = field(default_factory=dict) | |
| metadata: dict[str, Any] = field(default_factory=dict) | |
| def to_dict(self) -> dict[str, Any]: | |
| return { | |
| "schul_code": self.schul_code, | |
| "school_name": self.school_name, | |
| "status_label": self.status_label, | |
| "status_code": self.status_code, | |
| "sustainability_score": round(self.sustainability_score, 2), | |
| "forecast": self.forecast.to_dict(), | |
| "shap_result": self.shap_result.to_dict(), | |
| "score_detail": self.score_detail.to_dict(), | |
| "simulations": self.simulations, | |
| "metadata": self.metadata, | |
| } | |
| class DiagnosticsEngine: | |
| """ | |
| ํ๊ต ์ข ํฉ ์ง๋จ ์์ง. | |
| ์ฌ์ฉ ์:: | |
| engine = DiagnosticsEngine() | |
| result = engine.diagnose( | |
| schul_code="7431234", | |
| school_data={...}, | |
| timeseries=pd.Series({2018: 120, ...}), | |
| ) | |
| """ | |
| def __init__( | |
| self, | |
| predictor: GranitePredictor | None = None, | |
| shap_explainer: ShapExplainer | None = None, | |
| scorer: SustainabilityScorer | None = None, | |
| ) -> None: | |
| self._predictor = predictor or GranitePredictor() | |
| self._shap = shap_explainer or ShapExplainer() | |
| self._scorer = scorer or SustainabilityScorer() | |
| def _run_simulation( | |
| self, | |
| scenario_id: str, | |
| base_data: dict[str, Any], | |
| ) -> dict[str, Any]: | |
| """ | |
| ๋จ์ผ ์ ์ฑ ์๋๋ฆฌ์ค๋ฅผ ์ ์ฉํ ์ง์ ๊ฐ๋ฅ์ฑ ์ ์๋ฅผ ๋ฐํํฉ๋๋ค. | |
| Parameters | |
| ---------- | |
| scenario_id: | |
| SIMULATION_SCENARIOS ์ ํค. | |
| base_data: | |
| ์๋ณธ ํ๊ต ๋ฐ์ดํฐ ๋์ ๋๋ฆฌ. | |
| Returns | |
| ------- | |
| dict | |
| ์๋๋ฆฌ์ค ๋ ์ด๋ธ, ์ค๋ช , ๋ณ๊ฒฝ ์ /ํ ์ ์ ๋น๊ต. | |
| """ | |
| scenario = SIMULATION_SCENARIOS[scenario_id] | |
| delta = scenario["delta"] | |
| modified = dict(base_data) | |
| for key, change in delta.items(): | |
| if key == "established_year_delta": | |
| # ์์ค ๋ ธํ๋๋ฅผ ๊ฐ์ ํ๋ ๊ฒ์ ์๋ฎฌ๋ ์ด์ | |
| orig = int(modified.get("established_year", 0) or 0) | |
| if orig > 0: | |
| modified["established_year"] = orig + int(change) | |
| elif key in modified: | |
| modified[key] = (float(modified[key] or 0)) + float(change) | |
| else: | |
| modified[key] = float(change) | |
| base_score = self._scorer.compute(base_data.get("sd_schul_code", ""), base_data) | |
| sim_score = self._scorer.compute(base_data.get("sd_schul_code", ""), modified) | |
| return { | |
| "label": scenario["label"], | |
| "description": scenario["description"], | |
| "base_total_score": round(base_score.total_score, 2), | |
| "sim_total_score": round(sim_score.total_score, 2), | |
| "delta_score": round(sim_score.total_score - base_score.total_score, 2), | |
| "sim_curriculum_score": round(sim_score.curriculum_score, 2), | |
| "sim_personnel_score": round(sim_score.personnel_score, 2), | |
| "sim_facility_score": round(sim_score.facility_score, 2), | |
| "sim_community_score": round(sim_score.community_score, 2), | |
| } | |
| def diagnose( | |
| self, | |
| schul_code: str, | |
| school_data: dict[str, Any], | |
| timeseries: pd.Series, | |
| horizon_years: int = 5, | |
| run_simulations: bool = True, | |
| ) -> DiagnosticsResult: | |
| """ | |
| ํ๊ต ์ข ํฉ ์ง๋จ์ ์ํํฉ๋๋ค. | |
| Parameters | |
| ---------- | |
| schul_code: | |
| ๋์ ํ๊ต SD_SCHUL_CODE. | |
| school_data: | |
| ํ๊ต ์งํ ๋์ ๋๋ฆฌ (SustainabilityScorer, ShapExplainer ๊ณตํต ์ ๋ ฅ). | |
| timeseries: | |
| ์ฐ๋(int) ์ธ๋ฑ์ค์ ํ์ ์ ์๊ณ์ด Series. | |
| horizon_years: | |
| ์์ธก ๊ธฐ๊ฐ (๋ ). | |
| run_simulations: | |
| ์ ์ฑ ์๋ฎฌ๋ ์ด์ ์คํ ์ฌ๋ถ. | |
| Returns | |
| ------- | |
| DiagnosticsResult | |
| """ | |
| school_name = str(school_data.get("school_name", schul_code)) | |
| logger.info("์ง๋จ ์์: schul_code=%s school_name=%s", schul_code, school_name) | |
| # 1. ์๊ณ์ด ์์ธก | |
| try: | |
| forecast = self._predictor.predict( | |
| schul_code=schul_code, | |
| timeseries=timeseries, | |
| horizon_years=horizon_years, | |
| target_col="student_count", | |
| ) | |
| except Exception as exc: | |
| logger.error("์์ธก ์คํจ: schul_code=%s error=%s", schul_code, exc) | |
| raise | |
| # ํ์คํ ๋ฆฌ๋ฅผ school_data ์ ์ฃผ์ (SHAP ํผ์ฒ ๊ณ์ฐ์ฉ) | |
| school_data_enriched = dict(school_data) | |
| school_data_enriched["student_count_history"] = forecast.context_values | |
| school_data_enriched["sd_schul_code"] = schul_code | |
| # 2. SHAP ๋ถ๋ฅ ๋ฐ ๊ธฐ์ฌ๋ ๋ถ์ | |
| try: | |
| shap_result = self._shap.explain(schul_code=schul_code, school_data=school_data_enriched) | |
| except Exception as exc: | |
| logger.error("SHAP ๋ถ์ ์คํจ: schul_code=%s error=%s", schul_code, exc) | |
| raise | |
| # 3. ์ง์ ๊ฐ๋ฅ์ฑ ์ ์ ์ฐ์ถ | |
| try: | |
| score_detail = self._scorer.compute(schul_code=schul_code, school_data=school_data_enriched) | |
| except Exception as exc: | |
| logger.error("์ ์ ์ฐ์ถ ์คํจ: schul_code=%s error=%s", schul_code, exc) | |
| raise | |
| # 4. ์ ์ฑ ์๋ฎฌ๋ ์ด์ | |
| simulations: dict[str, dict[str, Any]] = {} | |
| if run_simulations: | |
| for sid in SIMULATION_SCENARIOS: | |
| try: | |
| simulations[sid] = self._run_simulation(sid, school_data_enriched) | |
| except Exception as exc: # noqa: BLE001 | |
| logger.warning("์๋ฎฌ๋ ์ด์ ์คํจ: scenario=%s error=%s", sid, exc) | |
| simulations[sid] = {"error": str(exc)} | |
| result = DiagnosticsResult( | |
| schul_code=schul_code, | |
| school_name=school_name, | |
| status_label=shap_result.status_label, | |
| status_code=shap_result.status_code, | |
| sustainability_score=score_detail.total_score, | |
| forecast=forecast, | |
| shap_result=shap_result, | |
| score_detail=score_detail, | |
| simulations=simulations, | |
| metadata={ | |
| "diagnosed_at": pd.Timestamp.now().isoformat(), | |
| "model_version": forecast.model_version, | |
| "data_quality": school_data_enriched.get("data_quality_score", 1.0), | |
| }, | |
| ) | |
| logger.info( | |
| "์ง๋จ ์๋ฃ: schul_code=%s status=%s score=%.1f", | |
| schul_code, | |
| shap_result.status_label, | |
| score_detail.total_score, | |
| ) | |
| return result | |