{"spec_id":"line-growth-percentile","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nline-growth-percentile: Pediatric Growth Chart with Percentile Curves\nLibrary: seaborn 0.13.2 | Python 3.13.14\nQuality: 92/100 | Updated: 2026-06-20\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Seaborn theme — theme-adaptive chrome\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n        \"font.family\": \"sans-serif\",\n    },\n)\n\n# Data — WHO weight-for-age reference for boys, 0–36 months (realistic values)\nnp.random.seed(42)\n\nage_months = np.arange(0, 37, 1)\n\nwho_ref_ages = np.array([0, 1, 2, 3, 4, 5, 6, 9, 12, 15, 18, 24, 30, 36])\nwho_ref_p50 = np.array([3.3, 4.5, 5.6, 6.4, 7.0, 7.5, 7.9, 9.2, 9.6, 10.3, 11.0, 12.2, 13.3, 14.3])\nmedian_weight = np.interp(age_months, who_ref_ages, who_ref_p50)\nsd = 0.4 + 0.028 * age_months\n\npercentile_z = {\"P3\": -1.8808, \"P10\": -1.2816, \"P25\": -0.6745, \"P50\": 0.0, \"P75\": 0.6745, \"P90\": 1.2816, \"P97\": 1.8808}\npercentiles = {label: median_weight + z * sd for label, z in percentile_z.items()}\n\n# Long-format DataFrame — seaborn-idiomatic for lineplot with hue/size\nrecords = []\nfor label, values in percentiles.items():\n    for i, age in enumerate(age_months):\n        records.append({\"Age (months)\": age, \"Weight (kg)\": values[i], \"Percentile\": label})\npercentile_df = pd.DataFrame(records)\n\n# Individual patient — boy drifting below P25 (growth faltering narrative)\npatient_ages = np.array([0, 1, 2, 4, 6, 9, 12, 15, 18, 24, 30, 36])\npatient_weights = np.array([3.5, 4.4, 5.4, 6.8, 7.6, 8.8, 9.1, 9.7, 10.2, 11.2, 12.0, 12.5])\npatient_df = pd.DataFrame({\"Age (months)\": patient_ages, \"Weight (kg)\": patient_weights})\n\n# Imprint blue (#4467A3) — clinical boys convention (semantic color exception)\nIMPRINT_BLUE = \"#4467A3\"\nIMPRINT_RED = \"#AE3030\"  # semantic: growth faltering / clinical alert\n\n# Percentile line sizes: P50 emphasized via thickness\npercentile_color_map = dict.fromkeys(percentile_z, IMPRINT_BLUE)\nline_sizes = {\"P3\": 0.8, \"P10\": 0.8, \"P25\": 1.0, \"P50\": 2.5, \"P75\": 1.0, \"P90\": 0.8, \"P97\": 0.8}\n\n# Band fills — graduated alpha, theme-adjusted for visibility on dark background\nband_alphas = [0.30, 0.22, 0.14, 0.14, 0.22, 0.30] if THEME == \"light\" else [0.45, 0.32, 0.20, 0.20, 0.32, 0.45]\n\n# Plot — 3200 × 1800 px canvas (figsize=(8, 4.5) × dpi=400)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Percentile bands (graduated fill between adjacent curves)\nband_labels = list(percentiles.keys())\nfor i in range(len(band_labels) - 1):\n    ax.fill_between(\n        age_months,\n        percentiles[band_labels[i]],\n        percentiles[band_labels[i + 1]],\n        color=IMPRINT_BLUE,\n        alpha=band_alphas[i],\n    )\n\n# Percentile lines — seaborn lineplot with hue and size encoding\nsns.lineplot(\n    data=percentile_df,\n    x=\"Age (months)\",\n    y=\"Weight (kg)\",\n    hue=\"Percentile\",\n    hue_order=list(percentile_z.keys()),\n    palette=percentile_color_map,\n    size=\"Percentile\",\n    sizes=line_sizes,\n    alpha=0.60,\n    legend=False,\n    ax=ax,\n)\n# Re-draw P50 at full opacity for clinical emphasis\np50_data = percentile_df[percentile_df[\"Percentile\"] == \"P50\"]\nsns.lineplot(\n    data=p50_data, x=\"Age (months)\", y=\"Weight (kg)\", color=IMPRINT_BLUE, linewidth=2.5, alpha=1.0, legend=False, ax=ax\n)\n\n# Patient trajectory — Imprint matte red (semantic: growth faltering alert)\nsns.lineplot(\n    data=patient_df, x=\"Age (months)\", y=\"Weight (kg)\", color=IMPRINT_RED, linewidth=2.5, zorder=5, legend=False, ax=ax\n)\nsns.scatterplot(\n    data=patient_df,\n    x=\"Age (months)\",\n    y=\"Weight (kg)\",\n    color=IMPRINT_RED,\n    s=80,\n    zorder=6,\n    edgecolor=PAGE_BG,\n    linewidth=1.5,\n    legend=False,\n    ax=ax,\n)\n\n# Percentile labels on right margin\nfor label, values in percentiles.items():\n    is_p50 = label == \"P50\"\n    ax.text(\n        36.8,\n        values[-1],\n        label,\n        fontsize=8,\n        fontweight=\"bold\" if is_p50 else \"normal\",\n        color=IMPRINT_BLUE if is_p50 else INK_SOFT,\n        va=\"center\",\n    )\n\n# Axes and title\ntitle = \"line-growth-percentile · python · seaborn · anyplot.ai\"\nax.set_title(title, fontsize=12, fontweight=\"medium\", pad=10, color=INK)\nax.set_xlabel(\"Age (months)\", fontsize=10, color=INK)\nax.set_ylabel(\"Weight (kg)\", fontsize=10, color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\nax.set_xlim(-0.5, 40.0)\nax.set_xticks(np.arange(0, 37, 3))\nax.xaxis.grid(False)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.6, color=INK)\nax.set_ylim(0, None)\n\n# Seaborn despine — idiomatic\nsns.despine(ax=ax, top=True, right=True)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}