Ajoute jalons et variantes de heatmaps normalisées à l'étape 15
This commit is contained in:
parent
7a193136e5
commit
7fe02ea263
@ -7,6 +7,7 @@ import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from lib.filesystem import ensure_parent_dir
|
||||
from lib.milestones import load_milestones
|
||||
from lib.rebrickable.stats import read_rows
|
||||
|
||||
|
||||
@ -15,31 +16,63 @@ def load_rows(path: Path) -> List[dict]:
|
||||
return read_rows(path)
|
||||
|
||||
|
||||
def plot_translucent_share(timeline_path: Path, destination_path: Path) -> None:
|
||||
"""Trace l'évolution de la part de pièces translucides et du nombre de couleurs."""
|
||||
def plot_translucent_share(timeline_path: Path, milestones_path: Path, destination_path: Path) -> None:
|
||||
"""Trace l'évolution de la part de pièces translucides et du nombre de couleurs avec jalons."""
|
||||
rows = load_rows(timeline_path)
|
||||
milestones = load_milestones(milestones_path)
|
||||
years = [int(row["year"]) for row in rows]
|
||||
shares = [float(row["share_translucent"]) for row in rows]
|
||||
distinct_counts = [int(row["colors_distinct"]) for row in rows]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 5))
|
||||
ax.plot(years, shares, color="#1f77b4", marker="o", linewidth=2.2, label="Part des translucides")
|
||||
ax.fill_between(years, shares, color="#1f77b4", alpha=0.15)
|
||||
fig, ax = plt.subplots(figsize=(13, 5.5))
|
||||
ax.plot(years, shares, color="#1f77b4", marker="o", linewidth=2.2, label="Part des translucides", zorder=3)
|
||||
ax.fill_between(years, shares, color="#1f77b4", alpha=0.15, zorder=2)
|
||||
ax.set_ylabel("Part translucide")
|
||||
ax.set_ylim(0, min(1.0, max(shares) * 1.1))
|
||||
ax.set_xlabel("Année")
|
||||
ax.grid(True, linestyle="--", alpha=0.3)
|
||||
ax2 = ax.twinx()
|
||||
ax2.plot(years, distinct_counts, color="#ff7f0e", marker="s", linewidth=1.8, label="Couleurs distinctes")
|
||||
ax2.plot(years, distinct_counts, color="#ff7f0e", marker="s", linewidth=1.8, label="Couleurs distinctes", zorder=3)
|
||||
ax2.set_ylabel("Nombre de couleurs distinctes")
|
||||
handles = [
|
||||
plt.Line2D([0], [0], color="#1f77b4", marker="o", label="Part des translucides"),
|
||||
plt.Line2D([0], [0], color="#ff7f0e", marker="s", label="Couleurs distinctes"),
|
||||
]
|
||||
ax.legend(handles=handles, loc="upper left")
|
||||
ax.legend(handles=handles, loc="upper left", bbox_to_anchor=(1.02, 1))
|
||||
ax.set_title("Evolution des palettes : translucides vs. diversité des couleurs")
|
||||
ax.set_xticks(years)
|
||||
ax.tick_params(axis="x", labelrotation=45)
|
||||
|
||||
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_step = 0.35
|
||||
offset_map: Dict[int, int] = {}
|
||||
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=160)
|
||||
|
||||
@ -7,6 +7,7 @@ from lib.plots.colors_timeline import plot_colors_heatmap, plot_translucent_shar
|
||||
|
||||
TIMELINE_PATH = Path("data/intermediate/colors_timeline.csv")
|
||||
MATRIX_PATH = Path("data/intermediate/colors_year_color_matrix.csv")
|
||||
MILESTONES_PATH = Path("config/milestones.csv")
|
||||
TRANSLUCENT_DESTINATION = Path("figures/step15/colors_translucent_share.png")
|
||||
HEATMAP_LINEAR_DESTINATION = Path("figures/step15/colors_heatmap_linear.png")
|
||||
HEATMAP_LOG_DESTINATION = Path("figures/step15/colors_heatmap_log.png")
|
||||
@ -15,7 +16,7 @@ HEATMAP_SHARE_DESTINATION = Path("figures/step15/colors_heatmap_share.png")
|
||||
|
||||
def main() -> None:
|
||||
"""Construit les visuels d'évolution annuelle des palettes."""
|
||||
plot_translucent_share(TIMELINE_PATH, TRANSLUCENT_DESTINATION)
|
||||
plot_translucent_share(TIMELINE_PATH, MILESTONES_PATH, TRANSLUCENT_DESTINATION)
|
||||
plot_colors_heatmap(MATRIX_PATH, HEATMAP_LINEAR_DESTINATION, use_log_scale=False)
|
||||
plot_colors_heatmap(MATRIX_PATH, HEATMAP_LOG_DESTINATION, use_log_scale=True)
|
||||
plot_colors_heatmap(MATRIX_PATH, HEATMAP_SHARE_DESTINATION, normalize_by_year=True)
|
||||
|
||||
@ -12,14 +12,16 @@ matplotlib.use("Agg")
|
||||
def test_plot_translucent_share(tmp_path: Path) -> None:
|
||||
"""Produit un graphique de part de translucides et diversité des couleurs."""
|
||||
timeline_path = tmp_path / "colors_timeline.csv"
|
||||
destination = tmp_path / "figures" / "step14" / "colors_translucent_share.png"
|
||||
milestones_path = tmp_path / "milestones.csv"
|
||||
destination = tmp_path / "figures" / "step15" / "colors_translucent_share.png"
|
||||
timeline_path.write_text(
|
||||
"year,colors_distinct,colors_new,colors_lost,share_translucent,total_quantity,top_colors\n"
|
||||
"2020,2,2,0,0.25,100,Blue (60),Red (40)\n"
|
||||
"2021,3,1,0,0.40,120,Blue (50),Trans-Black (48)\n"
|
||||
)
|
||||
milestones_path.write_text("year,description\n2020,Jalon Test\n")
|
||||
|
||||
plot_translucent_share(timeline_path, destination)
|
||||
plot_translucent_share(timeline_path, milestones_path, destination)
|
||||
|
||||
assert destination.exists()
|
||||
assert destination.stat().st_size > 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user