{"spec_id":"ridgeline-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# ridgeline-basic: Basic Ridgeline Plot\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 95/100 | Created: 2026-07-25\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Statistics\n\nRandom.seed!(42)\n\n# --- Theme tokens ------------------------------------------------------------\nTHEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nPAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nINK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nINK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\n\n# Imprint sequential colormap (brand green -> blue) — the 12 ridges form a\n# seasonal cycle, so a gradient reads better here than 12 discrete hues.\nIMPRINT_SEQ = cgrad([colorant\"#009E73\", colorant\"#4467A3\"])\n\n# --- Data ----------------------------------------------------------------------\n# Daily mean temperature normals for a temperate climate (approx. Berlin, DE)\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n          \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nmean_temp = [1.6, 2.5, 6.1, 10.4, 14.6, 17.9, 19.8, 19.4, 14.9, 10.2, 5.5, 2.5]\nstd_temp = [4.0, 4.0, 3.7, 3.3, 3.0, 2.7, 2.5, 2.5, 2.8, 3.2, 3.6, 3.9]\nn_obs = 250\n\n# Most months are a single symmetric Gaussian; Jan and Jul get an extra\n# minority component (a cold-snap / heat-spike bump) so the ridgeline also\n# demonstrates shape differences, not just shift/spread differences.\nn_bump = 40\ndaily_temps = Vector{Vector{Float64}}(undef, 12)\nfor m in 1:12\n    main = randn(n_obs - (m in (1, 7) ? n_bump : 0)) .* std_temp[m] .+ mean_temp[m]\n    if m == 1\n        cold_snap = randn(n_bump) .* (std_temp[m] * 0.5) .+ (mean_temp[m] - 9.0)\n        daily_temps[m] = vcat(main, cold_snap)\n    elseif m == 7\n        heat_spike = randn(n_bump) .* (std_temp[m] * 0.5) .+ (mean_temp[m] + 7.5)\n        daily_temps[m] = vcat(main, heat_spike)\n    else\n        daily_temps[m] = main\n    end\nend\n\n# Shared temperature grid for the kernel density estimate\ngrid_min = minimum(minimum.(daily_temps)) - 3.0\ngrid_max = maximum(maximum.(daily_temps)) + 3.0\ntemp_grid = range(grid_min, grid_max; length = 300)\n\n# Gaussian KDE per month (Silverman bandwidth), each normalized to a shared\n# ridge height so the vertical overlap stays consistent across all 12 ridges\nridge_step = 1.0\nridge_height = 2.3\nbaselines = [(m - 1) * ridge_step for m in 1:12]\nridge_curves = Vector{Vector{Float64}}(undef, 12)\nfor m in 1:12\n    data = daily_temps[m]\n    bandwidth = 1.06 * std(data) * length(data)^(-1 / 5)\n    density = [sum(exp.(-0.5 .* ((t .- data) ./ bandwidth) .^ 2)) for t in temp_grid]\n    density ./= (length(data) * bandwidth * sqrt(2π))\n    ridge_curves[m] = density ./ maximum(density) .* ridge_height .+ baselines[m]\nend\n\n# --- Plot ------------------------------------------------------------------\nfig = Figure(size = (1600, 900), fontsize = 14, backgroundcolor = PAGE_BG)\n\nax = Axis(\n    fig[1, 1];\n    title = \"ridgeline-basic · julia · makie · anyplot.ai\",\n    titlesize = 20,\n    titlecolor = INK,\n    xlabel = \"Temperature (°C)\",\n    xlabelsize = 16,\n    xlabelcolor = INK,\n    xticklabelsize = 13,\n    xticklabelcolor = INK_SOFT,\n    xtickcolor = INK_SOFT,\n    yticks = (baselines, months),\n    yticklabelsize = 13,\n    yticklabelcolor = INK_SOFT,\n    yticksvisible = false,\n    backgroundcolor = PAGE_BG,\n    topspinevisible = false,\n    rightspinevisible = false,\n    leftspinevisible = false,\n    bottomspinecolor = INK_SOFT,\n    xgridcolor = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    ygridvisible = false,\n)\n\n# Color keyed to actual mean temperature (min-max normalized), not month\n# index, so the gradient reinforces the warm/cold story instead of just\n# re-encoding the chronological order the Y-axis already shows.\ntemp_norm = (mean_temp .- minimum(mean_temp)) ./ (maximum(mean_temp) - minimum(mean_temp))\n\nfor m in 12:-1:1\n    ridge_color = get(IMPRINT_SEQ, temp_norm[m])\n    band!(ax, temp_grid, fill(baselines[m], length(temp_grid)), ridge_curves[m];\n          color = ridge_color)\n    lines!(ax, temp_grid, ridge_curves[m]; color = INK, linewidth = 1.3)\nend\n\nylims!(ax, -0.5, baselines[end] + ridge_height + 0.4)\nxlims!(ax, grid_min, grid_max)\n\n# --- Save --------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}