Violin plots

violinplot to show the full distribution per group.

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.