{"spec_id":"contour-density","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\ncontour-density: Density Contour Plot\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 95/100 | Updated: 2026-05-16\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens (see prompts/default-style-guide.md)\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.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Data - bivariate distribution with three clusters\nnp.random.seed(42)\n\n# Create three clusters with realistic domain context (height vs weight measurements)\nn_points = 500\n\n# Cluster 1: Lighter individuals\ncluster1_x = np.random.normal(160, 8, n_points // 3)  # Height in cm\ncluster1_y = np.random.normal(60, 6, n_points // 3)  # Weight in kg\n\n# Cluster 2: Heavier individuals\ncluster2_x = np.random.normal(175, 10, n_points // 3)\ncluster2_y = np.random.normal(80, 8, n_points // 3)\n\n# Cluster 3: Tall but lighter individuals\ncluster3_x = np.random.normal(180, 7, n_points // 3)\ncluster3_y = np.random.normal(70, 7, n_points // 3)\n\nx = np.concatenate([cluster1_x, cluster2_x, cluster3_x])\ny = np.concatenate([cluster1_y, cluster2_y, cluster3_y])\n\n# Define theme-aware colorscale for density (continuous data)\n# Use viridis-like progression that works on both light and dark backgrounds\ncolorscale = [\n    [0, \"rgba(255,255,255,0)\"],  # Transparent at low density\n    [0.2, \"#FDB462\"],  # Light orange (visible on both themes)\n    [0.5, \"#4467A3\"],  # Blue (Okabe-Ito position 3)\n    [1, \"#005073\"],  # Dark blue (increased contrast)\n]\n\n# Create figure with density contour\nfig = go.Figure()\n\n# Add density contour plot with interactive hover\nfig.add_trace(\n    go.Histogram2dContour(\n        x=x,\n        y=y,\n        colorscale=colorscale,\n        contours=dict(showlabels=False, coloring=\"fill\"),\n        ncontours=14,\n        showscale=True,\n        colorbar=dict(\n            title=dict(text=\"Density\", font=dict(size=20, color=INK)),\n            tickfont=dict(size=16, color=INK_SOFT),\n            len=0.8,\n            bgcolor=ELEVATED_BG,\n            bordercolor=INK_SOFT,\n            borderwidth=1,\n        ),\n        line=dict(width=2, color=\"rgba(0,0,0,0.2)\"),\n        hovertemplate=\"<b>Density Region</b><br>Height: %{x:.1f} cm<br>Weight: %{y:.1f} kg<extra></extra>\",\n    )\n)\n\n# Add scatter points for context (semi-transparent, interactive)\nfig.add_trace(\n    go.Scatter(\n        x=x,\n        y=y,\n        mode=\"markers\",\n        marker=dict(\n            size=6,\n            color=\"#009E73\",  # Okabe-Ito position 1 (brand color, theme-independent)\n            opacity=0.25,\n            line=dict(width=0),\n        ),\n        showlegend=False,\n        name=\"Individual measurements\",\n        hovertemplate=\"<b>Measurement</b><br>Height: %{x:.1f} cm<br>Weight: %{y:.1f} kg<extra></extra>\",\n    )\n)\n\n# Update layout with theme-adaptive styling\nfig.update_layout(\n    title=dict(text=\"contour-density · plotly · pyplots.ai\", font=dict(size=32, color=INK), x=0.5, xanchor=\"center\"),\n    xaxis=dict(\n        title=dict(text=\"Height (cm)\", font=dict(size=24, color=INK)),\n        tickfont=dict(size=18, color=INK_SOFT),\n        showgrid=True,\n        gridcolor=GRID,\n        gridwidth=1,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n    ),\n    yaxis=dict(\n        title=dict(text=\"Weight (kg)\", font=dict(size=24, color=INK)),\n        tickfont=dict(size=18, color=INK_SOFT),\n        showgrid=True,\n        gridcolor=GRID,\n        gridwidth=1,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font=dict(color=INK),\n    width=1600,\n    height=900,\n    margin=dict(l=100, r=120, t=100, b=100),\n    hovermode=\"closest\",\n)\n\n# Save as PNG and HTML with theme-suffixed filenames\nfig.write_image(f\"plot-{THEME}.png\", width=1600, height=900, scale=3)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}