"""
Score Calculator - Calculate evaluation scores consistently
This component centralizes score calculation logic using ModelUtils and Evaluator.
Extracted from launch_training() lines 449-461 and various controller methods.
"""
from dataclasses import dataclass
from typing import Dict, Optional, Any
import numpy as np
from ..utilities import ModelControllerUtils as ModelUtils
from nirs4all.core import metrics as evaluator
[docs]
@dataclass
class PartitionScores:
"""Scores for a single partition."""
train: float
val: float
test: float
metric: str
higher_is_better: bool
detailed_scores: Optional[Dict[str, float]] = None
[docs]
class ScoreCalculator:
"""Calculates evaluation scores for models.
Uses ModelUtils to select appropriate metrics based on task type,
and Evaluator to compute scores.
Example:
>>> calculator = ScoreCalculator()
>>> scores = calculator.calculate(
... y_true={'train': y_train, 'val': y_val, 'test': y_test},
... y_pred={'train': y_train_pred, 'val': y_val_pred, 'test': y_test_pred},
... task_type='regression'
... )
>>> scores.test
0.88
"""
[docs]
def calculate(
self,
y_true: Dict[str, np.ndarray],
y_pred: Dict[str, np.ndarray],
task_type: str
) -> PartitionScores:
"""Calculate scores for all partitions.
Args:
y_true: Dictionary of true values per partition
y_pred: Dictionary of predictions per partition
task_type: Task type string (e.g., 'regression', 'classification')
Returns:
PartitionScores with scores for train, val, test
"""
# Get best metric for task type
metric, higher_is_better = ModelUtils.get_best_score_metric(task_type)
# Calculate scores for each partition
scores = {}
for partition in ['train', 'val', 'test']:
if partition in y_true and partition in y_pred:
if y_true[partition].shape[0] > 0 and y_pred[partition].shape[0] > 0:
scores[partition] = evaluator.eval(
y_true[partition],
y_pred[partition],
metric
)
else:
scores[partition] = 0.0
else:
scores[partition] = 0.0
return PartitionScores(
train=scores.get('train', 0.0),
val=scores.get('val', 0.0),
test=scores.get('test', 0.0),
metric=metric,
higher_is_better=higher_is_better
)
[docs]
def calculate_single(
self,
y_true: np.ndarray,
y_pred: np.ndarray,
task_type: str,
metric: Optional[str] = None
) -> float:
"""Calculate score for a single partition.
Args:
y_true: True values
y_pred: Predictions
task_type: Task type string
metric: Optional metric name (if None, uses best metric for task)
Returns:
Score value
"""
if y_true.shape[0] == 0 or y_pred.shape[0] == 0:
return 0.0
if metric is None:
metric, _ = ModelUtils.get_best_score_metric(task_type)
return evaluator.eval(y_true, y_pred, metric)