Violin plots
Matplotlib Basics
2 min read
Published Oct 5 2025, updated Aug 17 2026
Guide Sections
Guide Comments
A violin plot combines a box plot and a kernel density plot (KDE) — it shows:
- The distribution shape (density) of the data on each side.
- The median and interquartile range (like a box plot).
- Possible multiple datasets side by side.
It’s ideal for comparing data distributions and spread between groups.
Syntax:
plt.violinplot(dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False,bw_method=None)Parameters:
dataset= Array-like data or list of datasetspositions= X-axis positions of violinsvert= Vertical (True) or horizontal (False)widths= Width of each violinshowmeans= Show mean markershowextrema= Show min/max barsshowmedians= Show median linebw_method= Controls smoothing of KDE (e.g., 'scott', 'silverman', or float)
Basic violin plot example
import matplotlib.pyplot as pltimport numpy as npdata = np.random.normal(0, 1, 100)plt.violinplot(data)plt.title("Basic Violin Plot")plt.show()Displays one violin — vertical, centred at x = 1.

Multiple violin plots
Pass multiple datasets as a list (or 2D array):
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]plt.violinplot(data)plt.title("Multiple Violin Plots")plt.show()
Add labels
By default, violin plots have no x-axis labels — you can add them manually:
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]plt.violinplot(data)plt.xticks([1, 2, 3], ['A', 'B', 'C'])plt.title("Violin Plots with Labels")plt.show()
Show median, mean, and extremes
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]plt.violinplot(data, showmeans=True, showmedians=True, showextrema=True)plt.xticks([1, 2, 3], ['A', 'B', 'C'])plt.title("Violin Plots with Mean and Median")plt.show()- Mean: typically a dot or horizontal line.
- Median: bold centre line.
- Extrema: thin top/bottom lines.

Horizontal violin plots
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]plt.violinplot(data, vert=False, showmedians=True)plt.yticks([1, 2, 3], ['A', 'B', 'C'])plt.title("Horizontal Violin Plots")plt.show()
Adjust widths and positions
You can manually set the positions and relative widths of violins:
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]# skip position 3positions = [1, 2, 4]plt.violinplot(data, positions=positions, widths=0.7, showmedians=True)plt.xticks(positions, ['A', 'B', 'C'])plt.title("Custom Positions and Widths")plt.show()
Customise appearance
The violinplot function returns a dictionary-like object with components (bodies, cmeans, cmedians, etc.), so you can modify their styles individually:
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]parts = plt.violinplot(data, showmeans=True, showmedians=True)for pc in parts['bodies']: pc.set_facecolor("#FFC9D6") pc.set_edgecolor('black') pc.set_alpha(0.7)plt.title("Styled Violin Plots")plt.xticks([1, 2, 3], ['A', 'B', 'C'])plt.show()
Change smoothing (bandwidth) of KDE
The bw_method parameter controls how smooth the violins are:
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]plt.violinplot(data, bw_method=0.3, showmedians=True)plt.title("Violin Plots with Custom Bandwidth")plt.show()Smaller bw_method = more jagged, larger = smoother.

Overlay box plot or scatter points
You can combine violin plots with box plots or raw data points for more detail:
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]plt.violinplot(data, showmedians=True)plt.boxplot(data, positions=[1, 2, 3], widths=0.1)plt.xticks([1, 2, 3], ['A', 'B', 'C'])plt.title("Violin + Box Plot Overlay")plt.show()
Or scatter points:
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]for i, d in enumerate(data, start=1): plt.scatter(np.random.normal(i, 0.05, len(d)), d, alpha=0.3) plt.violinplot(data, showmedians=True)plt.title("Violin Plot with Raw Data Overlay")plt.show()
Full styled example
import matplotlib.pyplot as pltimport numpy as npdata = [np.random.normal(0, 1, 100), np.random.normal(2, 1.5, 100), np.random.normal(-1, 0.8, 100)]parts = plt.violinplot( data, showmeans=True, showmedians=True, showextrema=False, bw_method=0.3)colors = ['#99ccff', '#ff9999', '#99ff99']for pc, color in zip(parts['bodies'], colors): pc.set_facecolor(color) pc.set_edgecolor('black') pc.set_alpha(0.8)plt.xticks([1, 2, 3], ['A', 'B', 'C'])plt.title("Styled Violin Plots with Mean and Median")plt.show()