{"spec_id":"bubble-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nbubble-basic: Basic Bubble Chart\nLibrary: plotly 7.1.0 | Python 3.13.15\nQuality: 92/100 | Updated: 2026-09-27\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme\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\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Continuous colorscale — imprint_seq (brand green → blue, single-polarity)\nimprint_seq = [[0.0, \"#009E73\"], [1.0, \"#4467A3\"]]\n\n# Data — Tech companies: R&D investment vs. product-market-fit score\nnp.random.seed(42)\nn = 62\nrd_pct = np.random.uniform(4, 42, n)  # R&D spend as % of revenue\npmf = np.clip(rd_pct * 1.3 + np.random.normal(0, 17, n) + 18, 8, 98)\nrevenue_m = np.clip(np.abs(np.random.normal(750, 380, n)), 40, 2200)\n\n# Anchor key data points for visual storytelling\nrd_pct[0], pmf[0], revenue_m[0] = 39, 96, 2100  # R&D powerhouse\nrd_pct[1], pmf[1], revenue_m[1] = 36, 91, 1800\nrd_pct[2], pmf[2], revenue_m[2] = 33, 87, 1650\nrd_pct[5], pmf[5], revenue_m[5] = 7, 24, 180  # underinvestors\nrd_pct[6], pmf[6], revenue_m[6] = 5, 18, 120\nrd_pct[10], pmf[10], revenue_m[10] = 22, 92, 920  # efficient innovator\n\n# Bubble sizing via sizeref (Plotly's idiomatic area-based scaling).\n# Max on-screen diameter capped at 34 and sizemin lowered to 5 so the dense\n# low-R&D/low-PMF cluster overlaps less while the lowest-revenue bubbles\n# stay clearly visible.\nsizeref = 2.0 * float(revenue_m.max()) / (34.0**2)\ncmin, cmax = float(revenue_m.min()), float(revenue_m.max())\n\n# Legend swatch colors tinted along imprint_seq (green->blue) so each swatch\n# matches what a real data bubble of that revenue would render.\nimprint_seq_rgb = (np.array([0x00, 0x9E, 0x73]), np.array([0x44, 0x67, 0xA3]))\n\n\ntitle = \"R&D Investment vs. Product-Market Fit · bubble-basic · python · plotly · anyplot.ai\"\ntitle_size = max(11, round(16 * 67 / len(title))) if len(title) > 67 else 16\n\n# Plot\nfig = go.Figure()\n\nfig.add_trace(\n    go.Scatter(\n        x=rd_pct,\n        y=pmf,\n        mode=\"markers\",\n        customdata=revenue_m,\n        marker={\n            \"size\": revenue_m,\n            \"sizemode\": \"area\",\n            \"sizeref\": sizeref,\n            \"sizemin\": 5,\n            \"color\": revenue_m,\n            \"cmin\": cmin,\n            \"cmax\": cmax,\n            \"colorscale\": imprint_seq,\n            \"showscale\": False,\n            \"opacity\": 0.5,\n            \"line\": {\"width\": 2, \"color\": PAGE_BG},\n        },\n        hovertemplate=(\n            \"<b>R&D Spend:</b> %{x:.1f}%<br>\"\n            \"<b>PMF Score:</b> %{y:.0f}<br>\"\n            \"<b>Revenue:</b> $%{customdata:.0f}M<extra></extra>\"\n        ),\n        showlegend=False,\n    )\n)\n\n# Size legend — three reference bubbles (anchored near the data's min/mid/max\n# revenue), tinted along imprint_seq so the swatch color matches what a real\n# data bubble of that revenue would render\nfor size_label in [100, 600, 2000]:\n    t = max(0.0, min(1.0, (size_label - cmin) / (cmax - cmin)))\n    r, g, b = (imprint_seq_rgb[0] + (imprint_seq_rgb[1] - imprint_seq_rgb[0]) * t).round().astype(int)\n    fig.add_trace(\n        go.Scatter(\n            x=[None],\n            y=[None],\n            mode=\"markers\",\n            marker={\n                \"size\": size_label,\n                \"sizemode\": \"area\",\n                \"sizeref\": sizeref,\n                \"sizemin\": 5,\n                \"color\": f\"#{r:02X}{g:02X}{b:02X}\",\n                \"opacity\": 0.5,\n                \"line\": {\"width\": 2, \"color\": PAGE_BG},\n            },\n            name=f\"${size_label}M\",\n            showlegend=True,\n        )\n    )\n\nfig.update_layout(\n    autosize=False,\n    title={\"text\": title, \"font\": {\"size\": title_size, \"color\": INK}, \"x\": 0.5, \"xanchor\": \"center\"},\n    xaxis={\n        \"title\": {\"text\": \"R&D Spend (% of Revenue)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\n        \"ticks\": \"\",\n    },\n    yaxis={\n        \"title\": {\"text\": \"Product-Market-Fit Score\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\n        \"ticks\": \"\",\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK, \"family\": \"Lato, 'DejaVu Sans', sans-serif\"},\n    legend={\n        \"title\": {\"text\": \"Revenue\", \"font\": {\"size\": 10, \"color\": INK}},\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 0.5,\n        \"x\": 0.02,\n        \"y\": 0.98,\n        \"xanchor\": \"left\",\n        \"yanchor\": \"top\",\n    },\n    margin={\"l\": 80, \"r\": 40, \"t\": 80, \"b\": 60},\n    # Subtle zone highlight around the R&D-powerhouse cluster (indices 0-2:\n    # high R&D spend, high PMF, high revenue) — a third layer of visual\n    # hierarchy beyond the two point-specific callouts below.\n    shapes=[\n        {\n            \"type\": \"rect\",\n            \"x0\": 29,\n            \"x1\": 41,\n            \"y0\": 83,\n            \"y1\": 99,\n            \"line\": {\"color\": INK_SOFT, \"width\": 1, \"dash\": \"dot\"},\n            \"fillcolor\": GRID,\n            \"layer\": \"below\",\n        }\n    ],\n    annotations=[\n        {\n            \"x\": 35,\n            \"y\": 99,\n            \"yshift\": 6,\n            \"text\": \"R&D powerhouses\",\n            \"font\": {\"size\": 9, \"color\": INK_SOFT},\n            \"showarrow\": False,\n            \"xanchor\": \"center\",\n            \"yanchor\": \"bottom\",\n        },\n        {\n            \"x\": rd_pct[10],\n            \"y\": pmf[10],\n            \"text\": \"Efficient innovator<br>(low R&D, high PMF)\",\n            \"font\": {\"size\": 10, \"color\": INK},\n            \"showarrow\": True,\n            \"arrowhead\": 2,\n            \"arrowcolor\": INK_SOFT,\n            \"ax\": 40,\n            \"ay\": -40,\n            \"bgcolor\": ELEVATED_BG,\n            \"bordercolor\": INK_SOFT,\n            \"borderwidth\": 0.5,\n            \"borderpad\": 4,\n        },\n        {\n            \"x\": rd_pct[6],\n            \"y\": pmf[6],\n            \"text\": \"Underinvested laggard<br>(low R&D, low PMF)\",\n            \"font\": {\"size\": 10, \"color\": INK},\n            \"showarrow\": True,\n            \"arrowhead\": 2,\n            \"arrowcolor\": INK_SOFT,\n            \"ax\": -50,\n            \"ay\": 40,\n            \"bgcolor\": ELEVATED_BG,\n            \"bordercolor\": INK_SOFT,\n            \"borderwidth\": 0.5,\n            \"borderpad\": 4,\n        },\n    ],\n)\n\n# Save\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}