{"spec_id":"bubble-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nbubble-basic: Basic Bubble Chart\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-28\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 = 38\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, 14, 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)\nsizeref = 2.0 * float(revenue_m.max()) / (46.0**2)\n\ntitle = \"bubble-basic · python · plotly · anyplot.ai\"\ntitle_size = 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            \"colorscale\": imprint_seq,\n            \"colorbar\": {\n                \"title\": {\"text\": \"Revenue<br>($ millions)\", \"font\": {\"size\": 12, \"color\": INK}},\n                \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n                \"thickness\": 16,\n                \"len\": 0.65,\n                \"y\": 0.5,\n                \"bgcolor\": ELEVATED_BG,\n                \"bordercolor\": INK_SOFT,\n                \"borderwidth\": 1,\n            },\n            \"opacity\": 0.75,\n            \"line\": {\"width\": 1.5, \"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\nfor size_label in [100, 500, 1500]:\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\": 4,\n                \"color\": \"#009E73\",\n                \"opacity\": 0.75,\n                \"line\": {\"width\": 1.5, \"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        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\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        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\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\": 1,\n        \"x\": 0.02,\n        \"y\": 0.98,\n        \"xanchor\": \"left\",\n        \"yanchor\": \"top\",\n    },\n    margin={\"l\": 80, \"r\": 160, \"t\": 80, \"b\": 60},\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"}