A violin is a KDE mirrored around a box (or a stick). It shows the full shape: one peak, two peaks, or a long tail.
Goal
Compare city violins, then split each violin by product.
Basic
rng = np.random.default_rng(0)
rows = []
for city, loc in [("Nairobi", 12), ("Mombasa", 8), ("Kisumu", 15)]:
rows.append(pd.DataFrame({"city": city, "units": rng.normal(loc, 3, size=40)}))
df = pd.concat(rows, ignore_index=True)
sns.violinplot(data=df, x="city", y="units")
plt.title("Units by city")
plt.show()Split hue
rng = np.random.default_rng(0)
rows = []
for city in ["Nairobi", "Mombasa", "Kisumu"]:
for product, loc in [("A", 9), ("B", 14)]:
rows.append(
pd.DataFrame(
{
"city": city,
"product": product,
"units": rng.normal(loc, 2.2, size=30),
}
)
)
df = pd.concat(rows, ignore_index=True)
sns.violinplot(data=df, x="city", y="units", hue="product", split=True)
plt.title("Split by product")
plt.show()split=True needs exactly two hue levels. Each half of the violin is one product.
Inner quartile
rng = np.random.default_rng(1)
df = pd.DataFrame(
{
"city": rng.choice(["Nairobi", "Mombasa", "Kisumu"], size=90),
"revenue": rng.normal(120, 35, size=90).clip(20),
}
)
sns.violinplot(data=df, x="city", y="revenue", inner="quartile")
plt.title("Quartile lines inside")
plt.show()inner="box" (default) draws a mini box. "quartile" draws dashed quartile lines. "point" marks each observation.
Pitfall
Violins need enough rows per group to estimate a density. A 12-row sales snapshot is too small — generate a sample as in this chapter, or use a box/strip plot.