1

Ajoute les agrégats et visualisations globales des couleurs de têtes

This commit is contained in:
2025-12-01 23:56:03 +01:00
parent d7b4ad8031
commit 47ee76cacf
10 changed files with 502 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
"""Visualisation de la part des têtes jaunes sur le catalogue global."""
from pathlib import Path
from typing import Dict, List
import matplotlib.pyplot as plt
from lib.filesystem import ensure_parent_dir
from lib.milestones import load_milestones
from lib.rebrickable.stats import read_rows
def compute_yellow_share(rows: List[dict]) -> List[dict]:
"""Calcule la part de la couleur Yellow par année."""
aggregated: Dict[str, Dict[str, int]] = {}
for row in rows:
year = row["year"]
if year not in aggregated:
aggregated[year] = {"yellow": 0, "total": 0}
aggregated[year]["total"] += int(row["quantity"])
if row["color_name"].lower() == "yellow" or row["color_rgb"].upper() == "FFFF00":
aggregated[year]["yellow"] += int(row["quantity"])
results = []
for year in sorted(aggregated.keys(), key=int):
total = aggregated[year]["total"]
yellow = aggregated[year]["yellow"]
share = yellow / total if total > 0 else 0
results.append({"year": int(year), "yellow_share": share, "total": total})
return results
def plot_yellow_share(heads_path: Path, milestones_path: Path, destination_path: Path) -> None:
"""Trace l'évolution de la part de têtes jaunes dans le catalogue complet."""
rows = read_rows(heads_path)
milestones = load_milestones(milestones_path)
series = compute_yellow_share(rows)
years = [item["year"] for item in series]
shares = [item["yellow_share"] for item in series]
fig, ax = plt.subplots(figsize=(13, 5.5))
ax.plot(years, shares, color="#f2c300", marker="o", linewidth=2.4, label="Part Yellow")
ax.fill_between(years, shares, color="#f2c300", alpha=0.18)
ax.set_ylim(0, min(1.0, max(shares + [0.01]) * 1.1))
ax.set_ylabel("Part de têtes Yellow")
ax.set_xlabel("Année")
if len(years) > 15:
step = max(1, len(years) // 10)
ax.set_xticks(years[::step])
else:
ax.set_xticks(years)
ax.set_title("Evolution de l'usage des têtes Yellow (catalogue complet)")
ax.grid(True, linestyle="--", alpha=0.3)
if milestones:
min_year = min(years)
max_year = max(years)
milestones_in_range = sorted(
[m for m in milestones if min_year <= m["year"] <= max_year],
key=lambda m: (m["year"], m["description"]),
)
offset_map: Dict[int, int] = {}
offset_step = 0.35
top_limit = ax.get_ylim()[1] * 1.05
for milestone in milestones_in_range:
year = milestone["year"]
count_for_year = offset_map.get(year, 0)
offset_map[year] = count_for_year + 1
horizontal_offset = offset_step * (count_for_year // 2 + 1)
if count_for_year % 2 == 1:
horizontal_offset *= -1
text_x = year + horizontal_offset
ax.axvline(year, color="#d62728", linestyle="--", linewidth=1, alpha=0.65, zorder=1)
ax.text(
text_x,
top_limit,
milestone["description"],
rotation=90,
verticalalignment="top",
horizontalalignment="center",
fontsize=8,
color="#d62728",
)
ax.set_ylim(ax.get_ylim()[0], top_limit * (1 + max(offset_map.values(), default=0) * 0.02))
ensure_parent_dir(destination_path)
fig.tight_layout()
fig.savefig(destination_path, dpi=170)
plt.close(fig)