{"spec_id":"parallel-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# parallel-basic: Basic Parallel Coordinates Plot\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 88/100 | Created: 2026-07-24\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# Theme tokens\nconst THEME     = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG   = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK       = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT  = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst INK_MUTED = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\",  # 1 — brand green, ALWAYS first series (Imprint palette)\n    colorant\"#C475FD\",  # 2 — lavender\n    colorant\"#4467A3\",  # 3 — blue\n    colorant\"#BD8233\",  # 4 — ochre\n    colorant\"#AE3030\",  # 5 — matte red\n    colorant\"#2ABCCD\",  # 6 — cyan\n    colorant\"#954477\",  # 7 — rose\n    colorant\"#99B314\",  # 8 — lime\n]\n\n# Data — smartphone specs across three price tiers, six comparison dimensions.\n# Axis order groups the four dimensions that move together with price\n# (Price, Rating, Storage, Camera) so adjacent axes stay low-crossing, and\n# places the two dimensions that buck that trend (Weight, Battery) together\n# at the end instead of interleaved among the correlated block.\ndimension_names = [\"Price (\\$)\", \"Rating (1-5)\", \"Storage (GB)\", \"Camera (MP)\", \"Weight (g)\", \"Battery (hrs)\"]\nn_dims = length(dimension_names)\ntiers = [\"Budget\", \"Mid-range\", \"Premium\"]\nn_per_tier = 20\n\ntier_params = [\n    (300.0, 50.0, 3.5, 0.4, 20.0, 3.0, 190.0, 15.0, 64.0, 16.0, 12.0, 3.0),\n    (600.0, 80.0, 4.0, 0.3, 15.0, 3.0, 175.0, 12.0, 128.0, 20.0, 48.0, 8.0),\n    (1100.0, 120.0, 4.5, 0.25, 12.0, 2.5, 200.0, 10.0, 256.0, 32.0, 108.0, 15.0),\n]\n\nprice = Float64[]\nrating = Float64[]\nbattery = Float64[]\nweight = Float64[]\nstorage = Float64[]\ncamera = Float64[]\ntier = String[]\n\nfor (t, (p_mu, p_sd, r_mu, r_sd, b_mu, b_sd, w_mu, w_sd, s_mu, s_sd, c_mu, c_sd)) in zip(tiers, tier_params)\n    append!(price, p_mu .+ p_sd .* randn(n_per_tier))\n    append!(rating, clamp.(r_mu .+ r_sd .* randn(n_per_tier), 1.0, 5.0))\n    append!(battery, clamp.(b_mu .+ b_sd .* randn(n_per_tier), 4.0, 40.0))\n    append!(weight, clamp.(w_mu .+ w_sd .* randn(n_per_tier), 60.0, 400.0))\n    append!(storage, clamp.(s_mu .+ s_sd .* randn(n_per_tier), 16.0, 512.0))\n    append!(camera, clamp.(c_mu .+ c_sd .* randn(n_per_tier), 5.0, 200.0))\n    append!(tier, fill(t, n_per_tier))\nend\n\ndata = hcat(price, rating, storage, camera, weight, battery)\nn_obs = size(data, 1)\n\n# Min-max normalize each dimension to a shared [0, 1] vertical scale\ndata_min = vec(minimum(data; dims = 1))\ndata_max = vec(maximum(data; dims = 1))\ndata_norm = (data .- data_min') ./ (data_max' .- data_min')\n\ntier_color = Dict(tiers[i] => IMPRINT_PALETTE[i] for i in eachindex(tiers))\n\n# Plot\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title               = \"parallel-basic · julia · makie · anyplot.ai\",\n    titlesize           = 20,\n    titlecolor          = INK,\n    titlefont           = :bold,\n    backgroundcolor     = PAGE_BG,\n    xticks              = (1:n_dims, dimension_names),\n    xticklabelsize      = 13,\n    xticklabelcolor     = INK_SOFT,\n    xticksvisible       = false,\n    yticksvisible       = false,\n    yticklabelsvisible  = false,\n    ylabelvisible       = false,\n    topspinevisible     = false,\n    rightspinevisible   = false,\n    leftspinevisible    = false,\n    bottomspinevisible  = false,\n    xgridvisible        = false,\n    ygridvisible        = false,\n)\n\nylims!(ax, -0.12, 1.12)\nxlims!(ax, 0.6, n_dims + 0.4)\n\n# One vertical reference axis per dimension\nvlines!(ax, 1:n_dims; color = INK_SOFT, linewidth = 1.2)\n\n# One connecting line per observation, colored by tier, drawn brand-first\nfor t in tiers\n    idx = findall(==(t), tier)\n    for i in idx\n        lines!(ax, 1:n_dims, data_norm[i, :]; color = (tier_color[t], 0.38), linewidth = 1.5)\n    end\nend\n\n# Original min/max labels at each axis end — the normalized scale alone hides units\nfor j in 1:n_dims\n    text!(ax, j, 1.06; text = string(round(data_max[j]; digits = 1)),\n          align = (:center, :bottom), fontsize = 12, color = INK_MUTED)\n    text!(ax, j, -0.06; text = string(round(data_min[j]; digits = 1)),\n          align = (:center, :top), fontsize = 12, color = INK_MUTED)\nend\n\n# Legend\nLegend(\n    fig[1, 2],\n    [LineElement(color = tier_color[t], linewidth = 3) for t in tiers],\n    tiers,\n    \"Tier\";\n    backgroundcolor = PAGE_BG,\n    framevisible     = false,\n    labelcolor       = INK_SOFT,\n    labelsize        = 12,\n    titlecolor       = INK,\n    titlesize        = 13,\n)\n\ncolsize!(fig.layout, 1, Relative(0.88))\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}