diff --git a/config/known_character_aliases.csv b/config/known_character_aliases.csv index 77fe8fa..a6458c4 100644 --- a/config/known_character_aliases.csv +++ b/config/known_character_aliases.csv @@ -29,3 +29,4 @@ Guard with Scarf,Figurant Park Worker,Figurant Park Guest in Dark Pink Vest Jacket,Figurant Wildlife Guard,Figurant +Kid,Figurant \ No newline at end of file diff --git a/lib/plots/minifig_characters.py b/lib/plots/minifig_characters.py index 4eccf89..a3e0146 100644 --- a/lib/plots/minifig_characters.py +++ b/lib/plots/minifig_characters.py @@ -48,7 +48,7 @@ def plot_minifigs_per_character(counts_path: Path, destination_path: Path) -> No def plot_character_year_presence(presence_path: Path, destination_path: Path) -> None: - """Trace une heatmap binaire indiquant la présence d'un personnage par année.""" + """Trace une heatmap indiquant le nombre de minifigs par personnage et par année.""" rows = load_presence(presence_path) if not rows: return @@ -56,7 +56,7 @@ def plot_character_year_presence(presence_path: Path, destination_path: Path) -> characters = sorted( {row["known_character"] for row in rows}, key=lambda name: ( - -sum(1 for r in rows if r["known_character"] == name and r["present"] == "1"), + -sum(int(r["minifig_count"]) for r in rows if r["known_character"] == name), name, ), ) @@ -64,11 +64,11 @@ def plot_character_year_presence(presence_path: Path, destination_path: Path) -> for character in characters: row_values = [] for year in years: - present = next( - (r["present"] for r in rows if r["known_character"] == character and int(r["year"]) == year), + count = next( + (r["minifig_count"] for r in rows if r["known_character"] == character and int(r["year"]) == year), "0", ) - row_values.append(int(present)) + row_values.append(int(count)) matrix.append(row_values) height = max(5, len(characters) * 0.35) @@ -80,13 +80,15 @@ def plot_character_year_presence(presence_path: Path, destination_path: Path) -> ax.set_yticklabels(characters) ax.set_xlabel("Année") ax.set_ylabel("Personnage") - ax.set_title("Présence des personnages par année (hors figurants)") + ax.set_title("Nombre de minifigs par personnage et par année (hors figurants)") for i, character in enumerate(characters): for j, year in enumerate(years): value = matrix[i][j] if value == 1: ax.text(j, i, "●", ha="center", va="center", color="#0d0d0d", fontsize=7) - fig.colorbar(cax, ax=ax, fraction=0.046, pad=0.04, label="Présence (1 si minifig)") + elif value > 1: + ax.text(j, i, str(value), ha="center", va="center", color="#0d0d0d", fontsize=7) + fig.colorbar(cax, ax=ax, fraction=0.046, pad=0.04, label="Nombre de minifigs") ensure_parent_dir(destination_path) fig.tight_layout() fig.savefig(destination_path, dpi=160) diff --git a/lib/rebrickable/minifig_characters.py b/lib/rebrickable/minifig_characters.py index f3a7a69..8997374 100644 --- a/lib/rebrickable/minifig_characters.py +++ b/lib/rebrickable/minifig_characters.py @@ -56,10 +56,11 @@ def aggregate_presence_by_year( sets_years: Dict[str, str], excluded_characters: Sequence[str] | None = None, ) -> List[dict]: - """Construit la présence binaire des personnages par année (hors figurants).""" + """Compte le nombre total de minifigs par personnage et par année (hors figurants).""" excluded = set(excluded_characters or []) - presence: set[tuple[str, int]] = set() + counts: Dict[tuple[str, int], int] = defaultdict(int) years_all = {int(year) for year in sets_years.values()} + characters_all: Set[str] = set() for row in minifigs_rows: character = row["known_character"].strip() fig_num = row["fig_num"].strip() @@ -70,17 +71,20 @@ def aggregate_presence_by_year( year = sets_years.get(row["set_num"]) if year is None: continue - presence.add((character, int(year))) + year_int = int(year) + counts[(character, year_int)] += 1 + characters_all.add(character) years = sorted(years_all) - characters = sorted({character for character, _ in presence}) + characters = sorted(characters_all) results: List[dict] = [] for character in characters: for year in years: + count = counts.get((character, year), 0) results.append( { "known_character": character, "year": str(year), - "present": "1" if (character, year) in presence else "0", + "minifig_count": str(count), } ) return results @@ -89,7 +93,7 @@ def aggregate_presence_by_year( def write_presence_by_year(path: Path, rows: Sequence[dict]) -> None: """Écrit la matrice présence binaire année/personnage.""" ensure_parent_dir(path) - fieldnames = ["known_character", "year", "present"] + fieldnames = ["known_character", "year", "minifig_count"] with path.open("w", newline="") as csv_file: writer = csv.DictWriter(csv_file, fieldnames=fieldnames) writer.writeheader() diff --git a/scripts/plot_minifig_characters_timeline.py b/scripts/plot_minifig_characters_timeline.py index 98d9708..ec70a51 100644 --- a/scripts/plot_minifig_characters_timeline.py +++ b/scripts/plot_minifig_characters_timeline.py @@ -19,7 +19,7 @@ EXCLUDED_CHARACTERS = ["Figurant"] def main() -> None: - """Construit la présence par année et trace la heatmap binaire.""" + """Construit le total par année et trace la heatmap.""" minifigs = load_minifigs_by_set(MINIFIGS_BY_SET_PATH) sets_years = load_sets_enriched(SETS_ENRICHED_PATH) presence = aggregate_presence_by_year(minifigs, sets_years, excluded_characters=EXCLUDED_CHARACTERS) diff --git a/tests/test_minifig_characters.py b/tests/test_minifig_characters.py index f35e5c1..d1f28b5 100644 --- a/tests/test_minifig_characters.py +++ b/tests/test_minifig_characters.py @@ -39,7 +39,7 @@ def test_write_character_counts_outputs_csv(tmp_path: Path) -> None: def test_aggregate_presence_by_year_excludes_figurants(tmp_path: Path) -> None: - """Calcule la présence annuelle en excluant les figurants.""" + """Calcule le total annuel en excluant les figurants.""" sets_path = tmp_path / "sets_enriched.csv" sets_path.write_text( "set_num,year\n" @@ -55,6 +55,6 @@ def test_aggregate_presence_by_year_excludes_figurants(tmp_path: Path) -> None: presence = aggregate_presence_by_year(minifigs_rows, sets_years, excluded_characters=["Figurant"]) assert presence == [ - {"known_character": "Owen Grady", "year": "2020", "present": "1"}, - {"known_character": "Owen Grady", "year": "2021", "present": "0"}, + {"known_character": "Owen Grady", "year": "2020", "minifig_count": "1"}, + {"known_character": "Owen Grady", "year": "2021", "minifig_count": "0"}, ] diff --git a/tests/test_minifig_characters_timeline_plot.py b/tests/test_minifig_characters_timeline_plot.py index 74c96a1..c6f4679 100644 --- a/tests/test_minifig_characters_timeline_plot.py +++ b/tests/test_minifig_characters_timeline_plot.py @@ -14,10 +14,10 @@ def test_plot_character_year_presence(tmp_path: Path) -> None: presence_path = tmp_path / "minifig_characters_year_presence.csv" destination = tmp_path / "figures" / "step22" / "minifig_characters_timeline.png" presence_path.write_text( - "known_character,year,present\n" - "Owen Grady,2020,1\n" + "known_character,year,minifig_count\n" + "Owen Grady,2020,2\n" "Owen Grady,2021,0\n" - "Figurant,2020,1\n" + "Figurant,2020,3\n" ) plot_character_year_presence(presence_path, destination)