import os import pandas as pd import numpy as np import matplotlib.pyplot as plt OUT = os.path.join("repport", "images") os.makedirs(OUT, exist_ok=True) # Supprime l'ancienne figure règle 3 si elle existe encore STALE = os.path.join(OUT, "regle3_vieillissement.png") if os.path.exists(STALE): os.remove(STALE) print(f"Ancien fichier supprimé : {STALE}") def save_fig(fig, name): path = os.path.join(OUT, f"{name}.png") fig.savefig(path, dpi=300, bbox_inches="tight") plt.close(fig) print(f"OK -> {path}") # --- Chargement --- df = pd.read_csv("rabbits.csv", skipinitialspace=True) df.columns = df.columns.str.strip() df = df.loc[:, ~df.columns.str.startswith("Unnamed")] cols = df.columns.drop("Month") df[cols] = df[cols].astype("float64") # effectifs > int64 max m = df["Month"].values.astype(int) litters = df["Litters"].values births = (df["Kitten Male (age 1)"] + df["Kitten Female (age 1)"]).values adult_1 = (df["Male (age 1)"] + df["Female (age 1)"]).values adult_f = df.filter(regex=r"^Female \(age").sum(axis=1).values km = df[[f"Kitten Male (age {i})" for i in range(1, 9)]].values kf = df[[f"Kitten Female (age {i})" for i in range(1, 9)]].values kit = km + kf # (N, 8), âges en mois # Buckets adultes etanches (age 1 exclu : il recoit la maturation), ages 2..16 ans ages = list(range(2, 17)) ad = df[[f"Male (age {i})" for i in ages] + [f"Female (age {i})" for i in ages]].values # (N, 30) # --- Controle d'integrite : saut du ratio naissances/portees ? --- ratio_g = births[1:] / np.maximum(litters[:-1], 1) jumps = np.where(np.abs(np.diff(ratio_g)) > 0.75)[0] if jumps.size: print(f"⚠️ CSV incoherent : le ratio naissances/portees saute autour du mois " f"{m[jumps[0] + 1]} — verifie le regime de gestation !") # === Regle 1 : gestation a 50 %/mois === fig, ax = plt.subplots(figsize=(9, 5)) ax.plot(m, litters / np.maximum(adult_f, 1), lw=1, color="tab:blue") ax.axhline(0.5, color="red", ls="--", label="attendu : 0.50") ax.set_ylim(0, 1) ax.set_xlabel("Mois"); ax.set_ylabel("Litters / femelles adultes") ax.set_title("Règle 1 — Taux de gestation"); ax.legend(); ax.grid(alpha=0.3) fig.tight_layout(); save_fig(fig, "regle1_gestation") # === Regle 2 : 3 a 6 bebes par gestation === fig, ax = plt.subplots(figsize=(9, 5)) ax.plot(m[1:], ratio_g, lw=1, color="tab:purple") ax.axhspan(3, 6, color="green", alpha=0.15, label="zone [3, 6]") ax.axhline(4.5, color="red", ls="--", label="moyenne uniforme : 4.5") ax.set_ylim(0, 8) ax.set_xlabel("Mois"); ax.set_ylabel("Nouveaux-nés / portée") ax.set_title("Règle 2 — Bébés par gestation"); ax.legend(); ax.grid(alpha=0.3) fig.tight_layout(); save_fig(fig, "regle2_portees") # === Regle 2bis : distribution des portees === fig, ax = plt.subplots(figsize=(9, 5)) valid = ratio_g[(ratio_g > 1) & (ratio_g < 12)] ax.hist(valid, bins=60, color="tab:purple", alpha=0.8) ax.axvline(4.5, color="red", ls="--", label="4.5 attendu") ax.set_xlabel("Bébés / portée"); ax.set_ylabel("Occurrences") ax.set_title("Règle 2 — Distribution des portées"); ax.legend(); ax.grid(alpha=0.3) fig.tight_layout(); save_fig(fig, "regle2_distribution") # === Regle 3 : maturation 5-8 mois (mesure seule, sans prediction) === fig, ax = plt.subplots(figsize=(9, 5)) ok3 = (m % 12 != 1) & (m >= 14) & (adult_1 > 100) ax.plot(m[ok3], adult_1[ok3], lw=1.5, color="tab:green", label="Adultes âge 1 (mesuré)") ax.set_yscale("log") ax.set_xlabel("Mois"); ax.set_ylabel("Effectifs (log)") ax.set_title("Règle 3 — Maturation entre 5 et 8 mois"); ax.legend(); ax.grid(alpha=0.3) fig.tight_layout(); save_fig(fig, "regle3_maturation") # === Regle 5 : survie des kittens ~95 %/mois === fig, ax = plt.subplots(figsize=(9, 5)) surv = kit[1:, 1:] / np.maximum(kit[:-1, :-1], 1) # (N-1, 7) mask = kit[:-1, :-1] > 100 # (N-1, 7) xs = np.repeat(m[1:, None], 7, axis=1) # (N-1, 7) ax.plot(xs[mask], surv[mask], ".", ms=2, alpha=0.2, color="tab:cyan") ax.axhline(0.95, color="red", ls="--", label="attendu : 0.95") ax.set_ylim(0.8, 1.05) ax.set_xlabel("Mois"); ax.set_ylabel("Taux de survie mensuel") ax.set_title("Règle 5 — Survie des kittens (5 %/mois)"); ax.legend(); ax.grid(alpha=0.3) fig.tight_layout(); save_fig(fig, "regle5_survie_kittens") # === Regle 4 : mortalite adultes — 40 %/an, +10 pts/an apres 10 ans === trans1 = (m[1:] % 12 == 1) # (N-1,) : mois cible = bascule s_reg = ad[1:] / np.maximum(ad[:-1], 1) # (N-1, 30) mois reguliers s_diag = np.full_like(s_reg, np.nan) s_diag[trans1, 1:] = ad[1:][trans1, 1:] / np.maximum(ad[:-1][trans1, :-1], 1) s_meas = np.where(trans1[:, None], s_diag, s_reg) # (N-1, 30) d_annuel = np.array([0.40 + 0.10 * max(0, a - 10) for a in ages]) s_attendu = 1 - d_annuel / 12 fig, ax = plt.subplots(figsize=(10, 5.5)) for j, a in enumerate(ages): ok = (ad[:-1, j] > 100) & ~np.isnan(s_meas[:, j]) & \ (s_meas[:, j] > 0.5) & (s_meas[:, j] < 1.02) ax.plot(m[1:][ok], s_meas[ok, j], ".", ms=2.5, alpha=0.45, label=f"âge {a} ans") for a, s in zip(ages, s_attendu): ax.axhline(s, color="black", ls=":", lw=0.7, alpha=0.6) ax.text(1.01, s, f"{s:.4f}", transform=ax.get_yaxis_transform(), va="center", fontsize=7, color="black") ax.set_ylim(0.88, 1.0) ax.set_xlabel("Mois"); ax.set_ylabel("Taux de survie mensuel par bucket d'âge") ax.set_title("Règle 4 — 40 %/an jusqu'à 10 ans, +10 pts/an ensuite (appliqué /12 par mois)") ax.legend(ncol=2, fontsize=8, loc="lower left"); ax.grid(alpha=0.3) fig.tight_layout(); save_fig(fig, "regle4_mortalite_adultes") print("\nTaux de survie mensuels attendus (règle 4) :") for a, d, s in zip(ages, d_annuel, s_attendu): print(f" âge {a:2d} ans : {100*d:5.1f} %/an -> survie mensuelle {s:.5f}") print("\nToutes les images sont dans", OUT)