A violin plot combining a box plot with a kernel density estimation on each side, showing the distribution shape of numerical data. The width of the violin at each point represents the frequency of data values at that level. Excellent for comparing distributions across categories while revealing their underlying shape, providing more detail than a traditional box plot.

""" anyplot.ai
violin-basic: Basic Violin Plot
Library: plotnine 0.15.4 | Python 3.13.13
Quality: 88/100 | Updated: 2026-05-29
"""
import os
import numpy as np
import pandas as pd
from plotnine import (
aes,
annotate,
element_blank,
element_line,
element_rect,
element_text,
geom_violin,
ggplot,
labs,
scale_fill_manual,
stat_summary,
theme,
theme_minimal,
)
# Theme tokens (Imprint palette — theme-adaptive chrome)
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
# Imprint categorical palette — positions 1–4 in canonical order
IMPRINT_PALETTE = ["#009E73", "#C475FD", "#4467A3", "#BD8233"]
# Data — annual salary distributions across 4 departments
np.random.seed(42)
records = []
# Engineering: right-skewed (many mid-range, few senior high earners)
salaries_eng = np.concatenate([np.random.normal(95000, 12000, 180), np.random.normal(148000, 7000, 40)])
records.extend([("Engineering", s) for s in salaries_eng])
# Design: bimodal (junior / senior pay split)
salaries_design = np.concatenate([np.random.normal(70000, 7000, 100), np.random.normal(108000, 7000, 100)])
records.extend([("Design", s) for s in salaries_design])
# Marketing: tight normal (narrow pay band)
salaries_mkt = np.random.normal(82000, 5000, 200)
records.extend([("Marketing", s) for s in salaries_mkt])
# Sales: wide spread (base + commission-driven variance)
salaries_sales = np.random.normal(90000, 22000, 200)
records.extend([("Sales", s) for s in salaries_sales])
df = pd.DataFrame(records, columns=["department", "salary"])
df["salary"] = df["salary"].clip(35000, 200000)
df["department"] = pd.Categorical(
df["department"], categories=["Engineering", "Design", "Marketing", "Sales"], ordered=True
)
departments = ["Engineering", "Design", "Marketing", "Sales"]
palette = dict(zip(departments, IMPRINT_PALETTE, strict=True))
# Plot
title = "violin-basic · python · plotnine · anyplot.ai"
plot = (
ggplot(df, aes(x="department", y="salary", fill="department"))
+ geom_violin(draw_quantiles=[0.25, 0.5, 0.75], alpha=0.82, color=INK_SOFT, size=0.6, trim=False)
+ stat_summary(geom="point", fun_y=np.mean, shape="D", size=2.5, color=INK, fill=INK)
+ scale_fill_manual(values=palette)
+ labs(x="Department", y="Annual Salary (USD)", title=title)
+ annotate("text", x=2, y=148000, label="Bimodal:\njunior/senior split", size=2.8, color=INK_SOFT)
+ theme_minimal()
+ theme(
figure_size=(8, 4.5),
text=element_text(size=9, color=INK),
axis_title=element_text(size=10, color=INK),
axis_text=element_text(size=8, color=INK_SOFT),
plot_title=element_text(size=12, color=INK),
legend_position="none",
panel_grid_major_x=element_blank(),
panel_grid_minor=element_blank(),
panel_grid_major_y=element_line(color=INK, size=0.3, alpha=0.15),
axis_line=element_line(color=INK_SOFT),
panel_border=element_blank(),
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
panel_background=element_rect(fill=PAGE_BG),
)
)
# Save
plot.save(f"plot-{THEME}.png", dpi=400, width=8, height=4.5, units="in", verbose=False)
Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/violin-basic/plotnine/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.
{
"spec_id": "violin-basic",
"language": "python",
"library": "plotnine",
"page": "https://anyplot.ai/violin-basic/python/plotnine",
"hub": "https://anyplot.ai/violin-basic",
"code_json": "https://api.anyplot.ai/specs/violin-basic/plotnine/code",
"spec_json": "https://api.anyplot.ai/specs/violin-basic",
"render_light_png": "https://storage.googleapis.com/anyplot-images/plots/violin-basic/python/plotnine/plot-light.png",
"render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/violin-basic/python/plotnine/plot-dark.png",
"quality_score": 88.0,
"license": "MIT",
"guide": "https://anyplot.ai/llms.txt"
}Part of Basic Violin Plot on anyplot.ai.