""" Value Objects pour les statistiques. """ from dataclasses import dataclass from typing import List @dataclass(frozen=True) class StatisticsResult: """ Résultat des calculs statistiques. Attributes: count: Nombre de valeurs mean: Moyenne median: Médiane min: Minimum max: Maximum std_dev: Écart-type """ count: int mean: float median: float min: float max: float std_dev: float def to_dict(self) -> dict: """Convertit en dictionnaire pour la sérialisation.""" return { "count": self.count, "mean": self.mean, "median": self.median, "min": self.min, "max": self.max, "std_dev": self.std_dev } @classmethod def empty(cls) -> "StatisticsResult": """Crée un résultat vide.""" return cls( count=0, mean=0.0, median=0.0, min=0.0, max=0.0, std_dev=0.0 ) @dataclass(frozen=True) class HistogramBin: """ Un bin d'histogramme. Attributes: range_start: Début de l'intervalle range_end: Fin de l'intervalle count: Nombre de valeurs dans l'intervalle """ range_start: float range_end: float count: int @property def label(self) -> str: """Génère le label pour l'affichage.""" if self.range_end == float('inf'): return f"{int(self.range_start)}+" return f"{int(self.range_start)}-{int(self.range_end)}" def to_dict(self) -> dict: """Convertit en dictionnaire pour la sérialisation.""" return { "range": self.label, "count": self.count } @dataclass class HistogramData: """ Données d'histogramme. Attributes: bins: Liste des bins max_count: Nombre maximum dans un bin (pour l'affichage) """ bins: List[HistogramBin] @property def max_count(self) -> int: """Retourne le count maximum parmi tous les bins.""" if not self.bins: return 0 return max(bin.count for bin in self.bins) @property def total_count(self) -> int: """Retourne le nombre total d'éléments.""" return sum(bin.count for bin in self.bins) def to_list(self) -> List[int]: """Convertit en liste simple de counts (pour Chart.js).""" return [bin.count for bin in self.bins] def to_dict_list(self) -> List[dict]: """Convertit en liste de dictionnaires.""" return [bin.to_dict() for bin in self.bins]