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
|
import numpy as np
|
||||||
|
|
||||||
from lib.filesystem import ensure_parent_dir
|
from lib.filesystem import ensure_parent_dir
|
||||||
|
from lib.milestones import load_milestones
|
||||||
from lib.rebrickable.stats import read_rows
|
from lib.rebrickable.stats import read_rows
|
||||||
|
|
||||||
|
|
||||||
@ -15,31 +16,63 @@ def load_rows(path: Path) -> List[dict]:
|
|||||||
return read_rows(path)
|
return read_rows(path)
|
||||||
|
|
||||||
|
|
||||||
def plot_translucent_share(timeline_path: Path, destination_path: Path) -> None:
|
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."""
|
"""Trace l'évolution de la part de pièces translucides et du nombre de couleurs avec jalons."""
|
||||||
rows = load_rows(timeline_path)
|
rows = load_rows(timeline_path)
|
||||||
|
milestones = load_milestones(milestones_path)
|
||||||
years = [int(row["year"]) for row in rows]
|
years = [int(row["year"]) for row in rows]
|
||||||
shares = [float(row["share_translucent"]) for row in rows]
|
shares = [float(row["share_translucent"]) for row in rows]
|
||||||
distinct_counts = [int(row["colors_distinct"]) for row in rows]
|
distinct_counts = [int(row["colors_distinct"]) for row in rows]
|
||||||
|
|
||||||
fig, ax = plt.subplots(figsize=(12, 5))
|
fig, ax = plt.subplots(figsize=(13, 5.5))
|
||||||
ax.plot(years, shares, color="#1f77b4", marker="o", linewidth=2.2, label="Part des translucides")
|
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)
|
ax.fill_between(years, shares, color="#1f77b4", alpha=0.15, zorder=2)
|
||||||
ax.set_ylabel("Part translucide")
|
ax.set_ylabel("Part translucide")
|
||||||
ax.set_ylim(0, min(1.0, max(shares) * 1.1))
|
ax.set_ylim(0, min(1.0, max(shares) * 1.1))
|
||||||
ax.set_xlabel("Année")
|
ax.set_xlabel("Année")
|
||||||
ax.grid(True, linestyle="--", alpha=0.3)
|
ax.grid(True, linestyle="--", alpha=0.3)
|
||||||
ax2 = ax.twinx()
|
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")
|
ax2.set_ylabel("Nombre de couleurs distinctes")
|
||||||
handles = [
|
handles = [
|
||||||
plt.Line2D([0], [0], color="#1f77b4", marker="o", label="Part des translucides"),
|
plt.Line2D([0], [0], color="#1f77b4", marker="o", label="Part des translucides"),
|
||||||
plt.Line2D([0], [0], color="#ff7f0e", marker="s", label="Couleurs distinctes"),
|
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_title("Evolution des palettes : translucides vs. diversité des couleurs")
|
||||||
ax.set_xticks(years)
|
ax.set_xticks(years)
|
||||||
ax.tick_params(axis="x", labelrotation=45)
|
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)
|
ensure_parent_dir(destination_path)
|
||||||
fig.tight_layout()
|
fig.tight_layout()
|
||||||
fig.savefig(destination_path, dpi=160)
|
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")
|
TIMELINE_PATH = Path("data/intermediate/colors_timeline.csv")
|
||||||
MATRIX_PATH = Path("data/intermediate/colors_year_color_matrix.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")
|
TRANSLUCENT_DESTINATION = Path("figures/step15/colors_translucent_share.png")
|
||||||
HEATMAP_LINEAR_DESTINATION = Path("figures/step15/colors_heatmap_linear.png")
|
HEATMAP_LINEAR_DESTINATION = Path("figures/step15/colors_heatmap_linear.png")
|
||||||
HEATMAP_LOG_DESTINATION = Path("figures/step15/colors_heatmap_log.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:
|
def main() -> None:
|
||||||
"""Construit les visuels d'évolution annuelle des palettes."""
|
"""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_LINEAR_DESTINATION, use_log_scale=False)
|
||||||
plot_colors_heatmap(MATRIX_PATH, HEATMAP_LOG_DESTINATION, use_log_scale=True)
|
plot_colors_heatmap(MATRIX_PATH, HEATMAP_LOG_DESTINATION, use_log_scale=True)
|
||||||
plot_colors_heatmap(MATRIX_PATH, HEATMAP_SHARE_DESTINATION, normalize_by_year=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:
|
def test_plot_translucent_share(tmp_path: Path) -> None:
|
||||||
"""Produit un graphique de part de translucides et diversité des couleurs."""
|
"""Produit un graphique de part de translucides et diversité des couleurs."""
|
||||||
timeline_path = tmp_path / "colors_timeline.csv"
|
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(
|
timeline_path.write_text(
|
||||||
"year,colors_distinct,colors_new,colors_lost,share_translucent,total_quantity,top_colors\n"
|
"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"
|
"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"
|
"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.exists()
|
||||||
assert destination.stat().st_size > 0
|
assert destination.stat().st_size > 0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user