{"spec_id":"density-rug","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ndensity-rug: Density Plot with Rug Marks\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Okabe-Ito palette (first series = brand green)\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# Data - bimodal distribution to show interesting density shape\nnp.random.seed(42)\nvalues = np.concatenate(\n    [\n        np.random.normal(loc=35, scale=8, size=60),  # First mode\n        np.random.normal(loc=65, scale=10, size=90),  # Second mode\n    ]\n)\n\n# Calculate KDE\nkde = gaussian_kde(values)\nx_range = np.linspace(values.min() - 10, values.max() + 10, 200)\ndensity = kde(x_range)\n\n# Scale density for visibility\ndensity_scaled = density / density.max()\n\n# Custom style (scaled for 4800x2700 canvas)\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n)\n\n# Create XY chart for continuous data\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"density-rug · Python · pygal · anyplot.ai\",\n    x_title=\"Measurement Value\",\n    y_title=\"Density (normalized)\",\n    show_legend=True,\n    legend_at_bottom=True,\n    stroke=True,\n    fill=True,\n    show_dots=False,\n    show_x_guides=True,\n    show_y_guides=True,\n    range=(0, 1.15),\n    include_x_axis=True,\n    truncate_legend=-1,\n)\n\n# Add KDE curve as line with fill\nkde_points = [(float(x), float(y)) for x, y in zip(x_range, density_scaled, strict=True)]\nchart.add(\"KDE Density Curve\", kde_points, stroke_style={\"width\": 5})\n\n# Add rug marks as dots along the x-axis at y=0\nsorted_values = np.sort(values)\nrug_points = [(float(v), 0.0) for v in sorted_values]\nchart.add(\"Rug Marks\", rug_points, stroke=False, show_dots=True, fill=False, dots_size=10)\n\n# Save as PNG and HTML with theme-suffixed names\nchart.render_to_png(f\"plot-{THEME}.png\")\nchart.render_to_file(f\"plot-{THEME}.html\")\n"}