Ajoute deux variantes de heatmap couleurs et pastilles alignées
This commit is contained in:
parent
640c6333f0
commit
d494c1a695
@ -183,4 +183,4 @@ Le script lit `data/intermediate/colors_by_set.csv` et produit deux agrégats :
|
||||
1. `source .venv/bin/activate`
|
||||
2. `python -m scripts.plot_colors_timeline`
|
||||
|
||||
Le script lit les deux agrégats de l'étape précédente et produit `figures/step14/colors_translucent_share.png` (part des pièces translucides par année et nombre de couleurs distinctes) ainsi que `figures/step14/colors_heatmap.png` (heatmap année × couleur basée sur les quantités totales en échelle log1p).
|
||||
Le script lit les deux agrégats de l'étape précédente et produit `figures/step14/colors_translucent_share.png` (part des pièces translucides par année et nombre de couleurs distinctes), `figures/step14/colors_heatmap_linear.png` (heatmap année × couleur en quantités brutes) et `figures/step14/colors_heatmap_log.png` (heatmap avec échelle log1p).
|
||||
|
||||
@ -67,26 +67,53 @@ def build_heatmap_data(rows: Iterable[dict]) -> Tuple[List[int], List[str], np.n
|
||||
y_index = index_by_color[color_key]
|
||||
x_index = index_by_year[int(row["year"])]
|
||||
matrix[y_index, x_index] += int(row["quantity_total"])
|
||||
return years, labels, matrix
|
||||
swatches = [
|
||||
{
|
||||
"name": name,
|
||||
"rgb": color_rgb,
|
||||
"is_translucent": is_trans,
|
||||
"display_color": f"#{color_rgb}",
|
||||
}
|
||||
for name, color_rgb, is_trans in color_keys
|
||||
]
|
||||
return years, labels, matrix, swatches
|
||||
|
||||
|
||||
def plot_colors_heatmap(matrix_path: Path, destination_path: Path) -> None:
|
||||
"""Génère une heatmap année × couleur basée sur les quantités totales."""
|
||||
def plot_colors_heatmap(matrix_path: Path, destination_path: Path, use_log_scale: bool = False) -> None:
|
||||
"""Génère une heatmap année × couleur basée sur les quantités totales avec pastilles."""
|
||||
rows = load_rows(matrix_path)
|
||||
years, labels, matrix = build_heatmap_data(rows)
|
||||
values = np.log1p(matrix)
|
||||
years, labels, matrix, swatches = build_heatmap_data(rows)
|
||||
values = np.log1p(matrix) if use_log_scale else matrix
|
||||
|
||||
fig, ax = plt.subplots(figsize=(14, max(6, len(labels) * 0.24)))
|
||||
fig, ax = plt.subplots(figsize=(14, max(6, len(labels) * 0.26)))
|
||||
y_positions = np.arange(len(labels))
|
||||
heatmap = ax.imshow(values, aspect="auto", cmap="magma", origin="lower")
|
||||
ax.set_xticks(range(len(years)))
|
||||
ax.set_xticklabels(years, rotation=45)
|
||||
ax.set_yticks(range(len(labels)))
|
||||
ax.set_yticklabels(labels)
|
||||
facecolors = [swatch["display_color"] for swatch in swatches]
|
||||
edgecolors = ["#f2f2f2" if swatch["is_translucent"] == "true" else "#0b0b0b" for swatch in swatches]
|
||||
for y, face, edge in zip(y_positions, facecolors, edgecolors):
|
||||
ax.scatter(
|
||||
-0.8,
|
||||
y,
|
||||
s=150,
|
||||
marker="o",
|
||||
color=face,
|
||||
edgecolors=edge,
|
||||
linewidths=1.1,
|
||||
zorder=3,
|
||||
clip_on=False,
|
||||
)
|
||||
ax.set_xlim(-1.1, len(years) - 0.5)
|
||||
ax.set_xlabel("Année")
|
||||
ax.set_title("Intensité des couleurs par année (log1p des quantités)")
|
||||
cbar = fig.colorbar(heatmap, ax=ax, shrink=0.82)
|
||||
cbar.set_label("log1p(quantité totale)")
|
||||
title_scale = "log1p des quantités" if use_log_scale else "quantités totales"
|
||||
ax.set_title(f"Intensité des couleurs par année ({title_scale})")
|
||||
cbar = fig.colorbar(heatmap, ax=ax, shrink=0.82, pad=0.018)
|
||||
cbar_label = "log1p(quantité totale)" if use_log_scale else "quantité totale"
|
||||
cbar.set_label(cbar_label)
|
||||
ensure_parent_dir(destination_path)
|
||||
fig.tight_layout()
|
||||
fig.subplots_adjust(left=0.26, right=0.97, bottom=0.08, top=0.94)
|
||||
fig.savefig(destination_path, dpi=170)
|
||||
plt.close(fig)
|
||||
|
||||
@ -8,13 +8,15 @@ 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")
|
||||
TRANSLUCENT_DESTINATION = Path("figures/step14/colors_translucent_share.png")
|
||||
HEATMAP_DESTINATION = Path("figures/step14/colors_heatmap.png")
|
||||
HEATMAP_LINEAR_DESTINATION = Path("figures/step14/colors_heatmap_linear.png")
|
||||
HEATMAP_LOG_DESTINATION = Path("figures/step14/colors_heatmap_log.png")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Construit les visuels d'évolution annuelle des palettes."""
|
||||
plot_translucent_share(TIMELINE_PATH, TRANSLUCENT_DESTINATION)
|
||||
plot_colors_heatmap(MATRIX_PATH, HEATMAP_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)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -28,7 +28,8 @@ def test_plot_translucent_share(tmp_path: Path) -> None:
|
||||
def test_plot_colors_heatmap(tmp_path: Path) -> None:
|
||||
"""Génère une heatmap année × couleur."""
|
||||
matrix_path = tmp_path / "colors_year_color_matrix.csv"
|
||||
destination = tmp_path / "figures" / "step14" / "colors_heatmap.png"
|
||||
destination_linear = tmp_path / "figures" / "step14" / "colors_heatmap_linear.png"
|
||||
destination_log = tmp_path / "figures" / "step14" / "colors_heatmap_log.png"
|
||||
matrix_path.write_text(
|
||||
"year,color_rgb,is_translucent,color_name,quantity_total\n"
|
||||
"2020,AAAAAA,false,Gray,5\n"
|
||||
@ -37,7 +38,10 @@ def test_plot_colors_heatmap(tmp_path: Path) -> None:
|
||||
"2021,CCCCCC,false,Blue,4\n"
|
||||
)
|
||||
|
||||
plot_colors_heatmap(matrix_path, destination)
|
||||
plot_colors_heatmap(matrix_path, destination_linear, use_log_scale=False)
|
||||
plot_colors_heatmap(matrix_path, destination_log, use_log_scale=True)
|
||||
|
||||
assert destination.exists()
|
||||
assert destination.stat().st_size > 0
|
||||
assert destination_linear.exists()
|
||||
assert destination_linear.stat().st_size > 0
|
||||
assert destination_log.exists()
|
||||
assert destination_log.stat().st_size > 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user