{"spec_id":"forest-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# forest-basic: Meta-Analysis Forest Plot\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 92/100 | Created: 2026-09-05\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# --- Theme tokens -----------------------------------------------------------\nconst THEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\nconst BRAND = IMPRINT_PALETTE[1]\n\n# --- Data: fixed-effect meta-analysis of 12 RCTs (risk ratio, event vs control) ---\nn_studies = 12\nsample_sizes = rand(60:900, n_studies)\nstudy_se = 1.1 ./ sqrt.(sample_sizes)\nbetween_study_tau = 0.22\noverall_log_rr = log(0.75)\nstudy_true_log_rr = overall_log_rr .+ randn(n_studies) .* between_study_tau\nlog_rr = study_true_log_rr .+ randn(n_studies) .* study_se\n\neffect_size = exp.(log_rr)\nci_lower = exp.(log_rr .- 1.96 .* study_se)\nci_upper = exp.(log_rr .+ 1.96 .* study_se)\ninverse_variance = 1 ./ study_se .^ 2\nweight_pct = inverse_variance ./ sum(inverse_variance) .* 100\n\npooled_log_rr = sum(log_rr .* inverse_variance) / sum(inverse_variance)\npooled_se = sqrt(1 / sum(inverse_variance))\npooled_estimate = exp(pooled_log_rr)\npooled_lower = exp(pooled_log_rr - 1.96 * pooled_se)\npooled_upper = exp(pooled_log_rr + 1.96 * pooled_se)\n\n# Landmark statin-vs-placebo RCTs for major adverse cardiovascular events\ntrial_names = [\n    \"4S (1994)\", \"WOSCOPS (1995)\", \"CARE (1996)\", \"AFCAPS/TexCAPS (1998)\",\n    \"LIPID (1998)\", \"HPS (2002)\", \"PROSPER (2002)\", \"ASCOT-LLA (2003)\",\n    \"CARDS (2004)\", \"TNT (2005)\", \"SPARCL (2006)\", \"JUPITER (2008)\",\n]\n\norder = sortperm(effect_size; rev = true)\ntrial_names = trial_names[order]\neffect_size = effect_size[order]\nci_lower = ci_lower[order]\nci_upper = ci_upper[order]\nweight_pct = weight_pct[order]\n\nstudy_ys = collect((n_studies + 1):-1:2)\npooled_y = 1\nmarker_sizes = 14 .+ 26 .* (weight_pct ./ maximum(weight_pct))\n\n# --- Plot ---------------------------------------------------------------\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title              = \"forest-basic · julia · makie · anyplot.ai\",\n    titlesize          = 20,\n    titlecolor         = INK,\n    xlabel             = \"Risk Ratio for Major Cardiovascular Events (95% CI)\",\n    xlabelsize         = 14,\n    xlabelcolor        = INK,\n    xticks             = 0.4:0.2:1.4,\n    xticklabelsize     = 12,\n    xticklabelcolor    = INK_SOFT,\n    yticks             = (vcat(study_ys, pooled_y), vcat(trial_names, \"Pooled effect\")),\n    yticklabelsize     = 12,\n    yticklabelcolor    = INK_SOFT,\n    backgroundcolor    = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinecolor     = INK_SOFT,\n    bottomspinecolor   = INK_SOFT,\n    xgridvisible       = true,\n    ygridvisible       = false,\n    xgridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.15),\n)\n\nxlims!(ax, 0.4, 1.35)\nylims!(ax, -0.5, n_studies + 1.8)\n\n# --- Row banding: subtle zebra striping + a highlighted pooled row ---------\nband_color = RGBAf(INK.r, INK.g, INK.b, THEME == \"light\" ? 0.035 : 0.05)\nfor (i, y) in enumerate(study_ys)\n    if isodd(i)\n        hspan!(ax, y - 0.5, y + 0.5; color = band_color)\n    end\nend\npooled_band_color = RGBAf(BRAND.r, BRAND.g, BRAND.b, 0.10)\nhspan!(ax, pooled_y - 0.5, pooled_y + 0.5; color = pooled_band_color)\n\nvlines!(ax, [1.0]; color = INK_SOFT, linestyle = :dash, linewidth = 2)\n\nrangebars!(ax, study_ys, ci_lower, ci_upper;\n    direction = :x, color = BRAND, linewidth = 2.5, whiskerwidth = 14)\nscatter!(ax, effect_size, study_ys;\n    markersize = marker_sizes, color = BRAND, strokewidth = 1.5, strokecolor = PAGE_BG)\n\npooled_half_height = 0.32\ndiamond = Point2f[\n    (pooled_lower, pooled_y),\n    (pooled_estimate, pooled_y + pooled_half_height),\n    (pooled_upper, pooled_y),\n    (pooled_estimate, pooled_y - pooled_half_height),\n]\npoly!(ax, diamond; color = INK, strokewidth = 0)\n\npooled_label = \"Pooled RR = $(round(pooled_estimate, digits = 2)) \" *\n               \"(95% CI $(round(pooled_lower, digits = 2))–$(round(pooled_upper, digits = 2)))\"\ntext!(ax, pooled_estimate, pooled_y - 0.75;\n    text = pooled_label, align = (:center, :top), fontsize = 13, color = INK, font = :bold)\n\nlegend_elements = [\n    MarkerElement(marker = :circle, color = BRAND, markersize = 16,\n        strokewidth = 1.5, strokecolor = PAGE_BG),\n    PolyElement(color = INK),\n]\nLegend(fig[1, 1], legend_elements, [\"Individual study\", \"Pooled effect\"];\n    tellwidth = false, tellheight = false, halign = :right, valign = :top,\n    margin = (10, 10, 10, 10), framevisible = false, labelcolor = INK_SOFT,\n    labelsize = 12)\n\nLabel(fig[2, 1], \"← Favors treatment                                                        Favors control →\";\n    fontsize = 12, color = INK_SOFT, font = :italic, tellwidth = false, halign = :center)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}