{"spec_id":"forest-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nforest-basic: Meta-Analysis Forest Plot\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 83/100 | Updated: 2026-05-11\n\"\"\"\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Data: Meta-analysis of blood pressure reduction trials (mmHg)\nnp.random.seed(42)\n\nstudies = [\n    \"Smith et al. 2018\",\n    \"Johnson & Lee 2019\",\n    \"Garcia et al. 2019\",\n    \"Williams 2020\",\n    \"Chen et al. 2020\",\n    \"Anderson et al. 2021\",\n    \"Thompson 2021\",\n    \"Martinez et al. 2022\",\n    \"Brown & Davis 2022\",\n    \"Wilson et al. 2023\",\n    \"Taylor 2023\",\n    \"Robinson et al. 2024\",\n]\n\n# Effect sizes (mean difference in mmHg) and confidence intervals\neffect_sizes = np.array([-8.2, -5.1, -12.3, -6.8, -9.5, -4.2, -7.8, -11.0, -3.5, -8.9, -6.2, -10.1])\nci_lower = effect_sizes - np.array([3.5, 4.2, 4.8, 3.1, 3.8, 5.2, 2.9, 4.1, 4.5, 3.3, 3.7, 4.0])\nci_upper = effect_sizes + np.array([3.2, 3.8, 4.5, 2.8, 3.5, 4.8, 2.6, 3.8, 4.2, 3.0, 3.4, 3.7])\nweights = np.array([8.5, 7.2, 9.8, 6.5, 8.9, 5.8, 7.8, 9.2, 6.2, 8.1, 7.5, 8.8])\n\n# Pooled estimate (random effects meta-analysis)\npooled_effect = -7.8\npooled_ci_lower = -9.2\npooled_ci_upper = -6.4\n\n# Y positions for studies (reversed for top-to-bottom display)\ny_positions = list(range(len(studies), 0, -1))\npooled_y = 0\n\n# Normalize weights for marker sizing (scale 8-24)\nweight_normalized = 8 + (weights - weights.min()) / (weights.max() - weights.min()) * 16\n\n# Create figure\nfig = go.Figure()\n\n# Add confidence interval lines for each study\nfor i, (y, lower, upper) in enumerate(zip(y_positions, ci_lower, ci_upper)):\n    fig.add_trace(\n        go.Scatter(\n            x=[lower, upper],\n            y=[y, y],\n            mode=\"lines\",\n            line=dict(color=\"#306998\", width=2),\n            showlegend=False,\n            hoverinfo=\"skip\",\n        )\n    )\n\n# Add study point estimates\nfig.add_trace(\n    go.Scatter(\n        x=effect_sizes,\n        y=y_positions,\n        mode=\"markers\",\n        marker=dict(size=weight_normalized, color=\"#306998\", symbol=\"square\", line=dict(color=\"#1a3d5c\", width=1)),\n        text=[\n            f\"{s}<br>Effect: {e:.1f} [{l:.1f}, {u:.1f}]\"\n            for s, e, l, u in zip(studies, effect_sizes, ci_lower, ci_upper)\n        ],\n        hovertemplate=\"%{text}<extra></extra>\",\n        name=\"Studies\",\n        showlegend=False,\n    )\n)\n\n# Add pooled estimate diamond\ndiamond_width = (pooled_ci_upper - pooled_ci_lower) / 2\ndiamond_height = 0.4\nfig.add_trace(\n    go.Scatter(\n        x=[pooled_ci_lower, pooled_effect, pooled_ci_upper, pooled_effect, pooled_ci_lower],\n        y=[pooled_y, pooled_y + diamond_height, pooled_y, pooled_y - diamond_height, pooled_y],\n        mode=\"lines\",\n        fill=\"toself\",\n        fillcolor=\"#FFD43B\",\n        line=dict(color=\"#b8960f\", width=2),\n        name=\"Pooled Estimate\",\n        hovertemplate=f\"Pooled Effect: {pooled_effect:.1f} [{pooled_ci_lower:.1f}, {pooled_ci_upper:.1f}]<extra></extra>\",\n        showlegend=False,\n    )\n)\n\n# Add vertical reference line at null effect (0)\nfig.add_vline(x=0, line=dict(color=\"#666666\", width=2, dash=\"dash\"))\n\n# Add annotation for null line\nfig.add_annotation(\n    x=0, y=len(studies) + 1, text=\"No Effect\", showarrow=False, font=dict(size=18, color=\"#666666\"), yanchor=\"bottom\"\n)\n\n# Update layout\nfig.update_layout(\n    title=dict(text=\"forest-basic · plotly · pyplots.ai\", font=dict(size=28), x=0.5, xanchor=\"center\"),\n    xaxis=dict(\n        title=dict(text=\"Mean Difference in Blood Pressure (mmHg)\", font=dict(size=22)),\n        tickfont=dict(size=18),\n        zeroline=False,\n        showgrid=True,\n        gridcolor=\"rgba(0,0,0,0.1)\",\n        gridwidth=1,\n        range=[-20, 5],\n    ),\n    yaxis=dict(\n        tickmode=\"array\",\n        tickvals=[0] + y_positions,\n        ticktext=[\"Pooled\"] + studies,\n        tickfont=dict(size=18),\n        showgrid=False,\n        range=[-1, len(studies) + 1.5],\n    ),\n    template=\"plotly_white\",\n    plot_bgcolor=\"white\",\n    paper_bgcolor=\"white\",\n    margin=dict(l=200, r=50, t=80, b=80),\n    showlegend=False,\n)\n\n# Add annotation for \"Favors Treatment\" and \"Favors Control\"\nfig.add_annotation(\n    x=-15, y=-0.8, text=\"← Favors Treatment\", showarrow=False, font=dict(size=16, color=\"#306998\"), xanchor=\"center\"\n)\n\nfig.add_annotation(\n    x=2.5, y=-0.8, text=\"Favors Control →\", showarrow=False, font=dict(size=16, color=\"#306998\"), xanchor=\"center\"\n)\n\n# Save as PNG (4800x2700 via scale)\nfig.write_image(\"plot.png\", width=1600, height=900, scale=3)\n\n# Save as HTML for interactivity\nfig.write_html(\"plot.html\", include_plotlyjs=\"cdn\")\n"}