{"spec_id":"bland-altman-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nbland-altman-basic: Bland-Altman Agreement Plot\nLibrary: plotnine 0.15.7 | Python 3.13.14\nQuality: 93/100 | Updated: 2026-08-11\n\"\"\"\n\nimport os\nimport pathlib\nimport sys\n\n\nscript_dir = str(pathlib.Path(__file__).parent)\nsys.path = [p for p in sys.path if os.path.abspath(p) != os.path.abspath(script_dir)]\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_point,\n    geom_text,\n    ggplot,\n    ggsave,\n    labs,\n    scale_fill_manual,\n    theme,\n)\n\n\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\"\nBRAND = \"#009E73\"\nOUTLIER = \"#AE3030\"\n\n# Data\nnp.random.seed(42)\nn = 100\nmethod1 = np.random.normal(loc=130, scale=15, size=n)\nmethod2 = method1 + np.random.normal(loc=2, scale=8, size=n)\n\nmean_values = (method1 + method2) / 2\ndifferences = method1 - method2\n\nmean_diff = np.mean(differences)\nstd_diff = np.std(differences, ddof=1)\nupper_limit = mean_diff + 1.96 * std_diff\nlower_limit = mean_diff - 1.96 * std_diff\n\noutside_limits = (differences > upper_limit) | (differences < lower_limit)\nagreement = np.where(outside_limits, \"Outside limits of agreement\", \"Within limits of agreement\")\n\ndf = pd.DataFrame({\"mean\": mean_values, \"difference\": differences, \"agreement\": agreement})\n\nlabel_x = np.min(df[\"mean\"]) * 0.98\nn_outside = int(outside_limits.sum())\npct_outside = 100 * n_outside / n\n\n# Plot — Imprint palette: brand green for in-range pairs, matte red (semantic\n# \"error / outside tolerance\" anchor) for pairs outside the 95% limits of agreement\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n    panel_border=element_blank(),\n    axis_line_x=element_line(color=INK_SOFT, size=0.8),\n    axis_line_y=element_line(color=INK_SOFT, size=0.8),\n    axis_title=element_text(size=10, color=INK),\n    axis_text=element_text(size=8, color=INK_SOFT),\n    plot_title=element_text(size=12, color=INK),\n    plot_subtitle=element_text(size=9, color=INK_SOFT),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(size=8, color=INK_SOFT),\n    legend_title=element_blank(),\n    legend_position=\"right\",\n    figure_size=(8, 4.5),\n)\n\nplot = (\n    ggplot(df, aes(x=\"mean\", y=\"difference\"))\n    + geom_point(aes(fill=\"agreement\"), color=ELEVATED_BG, stroke=0.35, size=3.0, alpha=0.7)\n    + geom_hline(yintercept=mean_diff, color=INK_SOFT, linetype=\"solid\", size=1)\n    + geom_hline(yintercept=upper_limit, color=INK_SOFT, linetype=\"dashed\", size=0.8, alpha=0.7)\n    + geom_hline(yintercept=lower_limit, color=INK_SOFT, linetype=\"dashed\", size=0.8, alpha=0.7)\n    + geom_text(\n        aes(x=label_x, y=mean_diff), label=f\"Mean: {mean_diff:.2f}\", size=3.5, color=INK_SOFT, ha=\"left\", nudge_y=1.3\n    )\n    + geom_text(\n        aes(x=label_x, y=upper_limit),\n        label=f\"+1.96 SD: {upper_limit:.2f}\",\n        size=3.5,\n        color=INK_SOFT,\n        ha=\"left\",\n        nudge_y=1.3,\n    )\n    + geom_text(\n        aes(x=label_x, y=lower_limit),\n        label=f\"-1.96 SD: {lower_limit:.2f}\",\n        size=3.5,\n        color=INK_SOFT,\n        ha=\"left\",\n        nudge_y=-1.3,\n    )\n    + scale_fill_manual(values={\"Within limits of agreement\": BRAND, \"Outside limits of agreement\": OUTLIER})\n    + labs(\n        x=\"Mean of Two Methods (mmHg)\",\n        y=\"Difference (Method 1 - Method 2, mmHg)\",\n        title=\"bland-altman-basic · python · plotnine · anyplot.ai\",\n        subtitle=f\"{n_outside}/{n} pairs ({pct_outside:.0f}%) fall outside the limits of agreement\",\n    )\n    + anyplot_theme\n)\n\noutput_path = os.path.join(os.path.dirname(__file__), f\"plot-{THEME}.png\")\nggsave(plot, filename=output_path, dpi=400, width=8, height=4.5)\n"}