{"spec_id":"ridgeline-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nridgeline-basic: Basic Ridgeline Plot\nLibrary: plotly 6.9.0 | Python 3.13.14\nQuality: 87/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.colors\nimport plotly.graph_objects as go\nfrom scipy.stats import gaussian_kde\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\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)\"\nLINE_EDGE = \"rgba(26,26,23,0.35)\" if THEME == \"light\" else \"rgba(240,239,232,0.35)\"\n\n# Data - Monthly temperature distributions (Northern hemisphere)\nnp.random.seed(42)\n\nmonths = [\n    \"January\",\n    \"February\",\n    \"March\",\n    \"April\",\n    \"May\",\n    \"June\",\n    \"July\",\n    \"August\",\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n]\n\nbase_temps = [-2, 0, 5, 12, 18, 23, 26, 25, 20, 13, 6, 1]\ndata = {}\nfor i, month in enumerate(months):\n    std = 4 if i in [2, 3, 8, 9] else 3\n    data[month] = np.random.normal(base_temps[i], std, 200)\n\n# X range for density evaluation\nx_range = np.linspace(-15, 40, 400)\n\n# Imprint sequential colormap (brand green -> blue) sampled across the chronological ridges\nimprint_seq = [[0.0, \"#009E73\"], [1.0, \"#4467A3\"]]\ncolors = plotly.colors.sample_colorscale(imprint_seq, [i / 11 for i in range(12)])\n\n# Plot\nfig = go.Figure()\n\n# Scaling for ridge height and ~50% overlap per spec\nridge_scale = 0.12\noverlap = 0.5\n\n# Add ridges December-to-January (back-to-front) so January sits at the bottom,\n# each foreground ridge partially occluding the one behind it\nfor idx in reversed(range(len(months))):\n    month = months[idx]\n    temps = data[month]\n\n    kde = gaussian_kde(temps)\n    density = kde(x_range)\n    density = density / density.max() * ridge_scale\n    y_offset = idx * (1 - overlap) * ridge_scale\n\n    # Crop each ridge to where its own density is non-negligible (>1.5% of\n    # its peak) instead of plotting across the full shared x_range. The KDE\n    # tails are asymptotically flat near zero, so evaluating every ridge\n    # over the same wide range produced long near-baseline lines that cut\n    # straight through neighboring ridges' fills.\n    support = np.flatnonzero(density > 0.015 * ridge_scale)\n    lo = max(support[0] - 1, 0)\n    hi = min(support[-1] + 1, len(x_range) - 1)\n    x_curve = x_range[lo : hi + 1]\n    y_fill = density[lo : hi + 1] + y_offset\n\n    # Fill trace: closed polygon (curve + flat baseline return path) with an\n    # invisible line so only the shaded area shows, not the baseline itself.\n    fig.add_trace(\n        go.Scatter(\n            x=np.concatenate([[x_curve[0]], x_curve, [x_curve[-1]]]),\n            y=np.concatenate([[y_offset], y_fill, [y_offset]]),\n            fill=\"toself\",\n            fillcolor=colors[idx],\n            line={\"width\": 0},\n            mode=\"lines\",\n            name=month,\n            showlegend=False,\n            hoverinfo=\"skip\",\n        )\n    )\n\n    # Outline trace: only the density curve itself, cropped to its own\n    # support, so neighboring ridges aren't crossed by a baseline line.\n    fig.add_trace(\n        go.Scatter(\n            x=x_curve,\n            y=y_fill,\n            mode=\"lines\",\n            line={\"color\": LINE_EDGE, \"width\": 1.5},\n            showlegend=False,\n            hovertemplate=f\"{month}<br>Temperature: %{{x:.1f}}°C<extra></extra>\",\n        )\n    )\n\n# Y-tick positions aligned to ridge peaks (same idx-based offset as the traces above)\ny_ticks = [idx * (1 - overlap) * ridge_scale + ridge_scale * 0.4 for idx in range(len(months))]\n\n# Style\nfig.update_layout(\n    autosize=False,\n    title={\n        \"text\": \"ridgeline-basic · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 18, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Temperature (°C)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"range\": [-15, 40],\n        \"gridcolor\": GRID,\n        \"showgrid\": True,\n        \"zeroline\": False,\n        \"linecolor\": INK_SOFT,\n    },\n    yaxis={\n        \"title\": {\"text\": \"\", \"font\": {\"size\": 12}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"tickvals\": y_ticks,\n        \"ticktext\": months,\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"range\": [-0.02, max(y_ticks) + ridge_scale * 0.7],\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    margin={\"l\": 90, \"r\": 40, \"t\": 70, \"b\": 55},\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"}