Source code for nirs4all.visualization.chart_utils.normalizer

"""
ScoreNormalizer - Normalize scores for visualization.
"""
import numpy as np


[docs] class ScoreNormalizer: """Normalize scores for visualization. Handles normalization to [0, 1] range with support for both higher-is-better and lower-is-better metrics. """
[docs] @staticmethod def normalize( matrix: np.ndarray, higher_better: bool, per_row: bool = False, per_column: bool = False ) -> np.ndarray: """Normalize matrix values to [0, 1] range. Args: matrix: Input matrix to normalize. higher_better: Whether higher values are better. per_row: If True, normalize each row independently. per_column: If True, normalize each column independently. Takes precedence over per_row if both are True. Returns: Normalized matrix with values in [0, 1] range. """ normalized = matrix.copy() if per_column: # Normalize each column independently (best in column = 1.0) for j in range(normalized.shape[1]): col = normalized[:, j] valid_mask = ~np.isnan(col) if not np.any(valid_mask): continue valid_scores = col[valid_mask] min_val = np.min(valid_scores) max_val = np.max(valid_scores) if max_val > min_val: col[valid_mask] = (valid_scores - min_val) / (max_val - min_val) if not higher_better: col[valid_mask] = 1 - col[valid_mask] else: col[valid_mask] = 0.5 normalized[:, j] = col elif per_row: # Normalize each row independently for i in range(normalized.shape[0]): row = normalized[i, :] valid_mask = ~np.isnan(row) if not np.any(valid_mask): continue valid_scores = row[valid_mask] min_val = np.min(valid_scores) max_val = np.max(valid_scores) if max_val > min_val: row[valid_mask] = (valid_scores - min_val) / (max_val - min_val) if not higher_better: row[valid_mask] = 1 - row[valid_mask] else: row[valid_mask] = 0.5 normalized[i, :] = row else: # Global normalization valid_mask = ~np.isnan(normalized) if not np.any(valid_mask): return normalized valid_scores = normalized[valid_mask] min_val = np.min(valid_scores) max_val = np.max(valid_scores) if max_val > min_val: normalized[valid_mask] = (valid_scores - min_val) / (max_val - min_val) if not higher_better: normalized[valid_mask] = 1 - normalized[valid_mask] else: normalized[valid_mask] = 0.5 return normalized
[docs] @staticmethod def is_higher_better(metric: str) -> bool: """Check if metric is higher-is-better. Args: metric: Metric name. Returns: True if higher is better, False otherwise. """ return metric.lower() in ['r2', 'accuracy', 'f1', 'precision', 'recall', 'auc']