Skip to content

Commit 020127f

Browse files
Anja Beckksunden
Anja Beck
andcommitted
add side option for violinplot
add test for side option in violinplot add whats new for side in violinplot add violin side option to _axes.pyi and pyplot.py use side option in violinplot also for lines add side option to violinplot example use newer style and correct seed in violin test update image change option naming use proper syntax for code in violin docs Co-authored-by: Kyle Sunden <git@ksunden.space>
1 parent a6da814 commit 020127f

File tree

7 files changed

+80
-20
lines changed

7 files changed

+80
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add option to plot only one half of violin plot
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Setting the parameter *side* to 'low' or 'high' allows to only plot one half of the violin plot.

galleries/examples/statistics/violinplot.py

+29-11
Original file line numberDiff line numberDiff line change
@@ -28,55 +28,73 @@
2828
pos = [1, 2, 4, 5, 7, 8]
2929
data = [np.random.normal(0, std, size=100) for std in pos]
3030

31-
fig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10, 6))
31+
fig, axs = plt.subplots(nrows=2, ncols=6, figsize=(10, 4))
3232

3333
axs[0, 0].violinplot(data, pos, points=20, widths=0.3,
3434
showmeans=True, showextrema=True, showmedians=True)
35-
axs[0, 0].set_title('Custom violinplot 1', fontsize=fs)
35+
axs[0, 0].set_title('Custom violin 1', fontsize=fs)
3636

3737
axs[0, 1].violinplot(data, pos, points=40, widths=0.5,
3838
showmeans=True, showextrema=True, showmedians=True,
3939
bw_method='silverman')
40-
axs[0, 1].set_title('Custom violinplot 2', fontsize=fs)
40+
axs[0, 1].set_title('Custom violin 2', fontsize=fs)
4141

4242
axs[0, 2].violinplot(data, pos, points=60, widths=0.7, showmeans=True,
4343
showextrema=True, showmedians=True, bw_method=0.5)
44-
axs[0, 2].set_title('Custom violinplot 3', fontsize=fs)
44+
axs[0, 2].set_title('Custom violin 3', fontsize=fs)
4545

4646
axs[0, 3].violinplot(data, pos, points=60, widths=0.7, showmeans=True,
4747
showextrema=True, showmedians=True, bw_method=0.5,
4848
quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]])
49-
axs[0, 3].set_title('Custom violinplot 4', fontsize=fs)
49+
axs[0, 3].set_title('Custom violin 4', fontsize=fs)
5050

5151
axs[0, 4].violinplot(data[-1:], pos[-1:], points=60, widths=0.7,
5252
showmeans=True, showextrema=True, showmedians=True,
5353
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5)
54-
axs[0, 4].set_title('Custom violinplot 5', fontsize=fs)
54+
axs[0, 4].set_title('Custom violin 5', fontsize=fs)
55+
56+
axs[0, 5].violinplot(data[-1:], pos[-1:], points=60, widths=0.7,
57+
showmeans=True, showextrema=True, showmedians=True,
58+
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='low')
59+
60+
axs[0, 5].violinplot(data[-1:], pos[-1:], points=60, widths=0.7,
61+
showmeans=True, showextrema=True, showmedians=True,
62+
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high')
63+
axs[0, 5].set_title('Custom violin 6', fontsize=fs)
5564

5665
axs[1, 0].violinplot(data, pos, points=80, vert=False, widths=0.7,
5766
showmeans=True, showextrema=True, showmedians=True)
58-
axs[1, 0].set_title('Custom violinplot 6', fontsize=fs)
67+
axs[1, 0].set_title('Custom violin 7', fontsize=fs)
5968

6069
axs[1, 1].violinplot(data, pos, points=100, vert=False, widths=0.9,
6170
showmeans=True, showextrema=True, showmedians=True,
6271
bw_method='silverman')
63-
axs[1, 1].set_title('Custom violinplot 7', fontsize=fs)
72+
axs[1, 1].set_title('Custom violin 8', fontsize=fs)
6473

6574
axs[1, 2].violinplot(data, pos, points=200, vert=False, widths=1.1,
6675
showmeans=True, showextrema=True, showmedians=True,
6776
bw_method=0.5)
68-
axs[1, 2].set_title('Custom violinplot 8', fontsize=fs)
77+
axs[1, 2].set_title('Custom violin 9', fontsize=fs)
6978

7079
axs[1, 3].violinplot(data, pos, points=200, vert=False, widths=1.1,
7180
showmeans=True, showextrema=True, showmedians=True,
7281
quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]],
7382
bw_method=0.5)
74-
axs[1, 3].set_title('Custom violinplot 9', fontsize=fs)
83+
axs[1, 3].set_title('Custom violin 10', fontsize=fs)
7584

7685
axs[1, 4].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1,
7786
showmeans=True, showextrema=True, showmedians=True,
7887
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5)
79-
axs[1, 4].set_title('Custom violinplot 10', fontsize=fs)
88+
axs[1, 4].set_title('Custom violin 11', fontsize=fs)
89+
90+
axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1,
91+
showmeans=True, showextrema=True, showmedians=True,
92+
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='low')
93+
94+
axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1,
95+
showmeans=True, showextrema=True, showmedians=True,
96+
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high')
97+
axs[1, 5].set_title('Custom violin 12', fontsize=fs)
8098

8199

82100
for ax in axs.flat:

lib/matplotlib/axes/_axes.py

+28-9
Original file line numberDiff line numberDiff line change
@@ -8205,7 +8205,7 @@ def matshow(self, Z, **kwargs):
82058205
@_preprocess_data(replace_names=["dataset"])
82068206
def violinplot(self, dataset, positions=None, vert=True, widths=0.5,
82078207
showmeans=False, showextrema=True, showmedians=False,
8208-
quantiles=None, points=100, bw_method=None):
8208+
quantiles=None, points=100, bw_method=None, side='both'):
82098209
"""
82108210
Make a violin plot.
82118211
@@ -8258,6 +8258,10 @@ def violinplot(self, dataset, positions=None, vert=True, widths=0.5,
82588258
its only parameter and return a scalar. If None (default), 'scott'
82598259
is used.
82608260
8261+
side : {'both', 'low', 'high'}, default: 'both'
8262+
'both' plots standard violins. 'low'/'high' only
8263+
plots the side below/above the positions value.
8264+
82618265
data : indexable object, optional
82628266
DATA_PARAMETER_PLACEHOLDER
82638267
@@ -8309,10 +8313,10 @@ def _kde_method(X, coords):
83098313
quantiles=quantiles)
83108314
return self.violin(vpstats, positions=positions, vert=vert,
83118315
widths=widths, showmeans=showmeans,
8312-
showextrema=showextrema, showmedians=showmedians)
8316+
showextrema=showextrema, showmedians=showmedians, side=side)
83138317

83148318
def violin(self, vpstats, positions=None, vert=True, widths=0.5,
8315-
showmeans=False, showextrema=True, showmedians=False):
8319+
showmeans=False, showextrema=True, showmedians=False, side='both'):
83168320
"""
83178321
Draw a violin plot from pre-computed statistics.
83188322
@@ -8368,6 +8372,10 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
83688372
showmedians : bool, default: False
83698373
If true, will toggle rendering of the medians.
83708374
8375+
side : {'both', 'low', 'high'}, default: 'both'
8376+
'both' plots standard violins. 'low'/'high' only
8377+
plots the side below/above the positions value.
8378+
83718379
Returns
83728380
-------
83738381
dict
@@ -8430,8 +8438,13 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
84308438
elif len(widths) != N:
84318439
raise ValueError(datashape_message.format("widths"))
84328440

8441+
# Validate side
8442+
_api.check_in_list(["both", "low", "high"], side=side)
8443+
84338444
# Calculate ranges for statistics lines (shape (2, N)).
8434-
line_ends = [[-0.25], [0.25]] * np.array(widths) + positions
8445+
line_ends = [[-0.25 if side in ['both', 'low'] else 0],
8446+
[0.25 if side in ['both', 'high'] else 0]] \
8447+
* np.array(widths) + positions
84358448

84368449
# Colors.
84378450
if mpl.rcParams['_internal.classic_mode']:
@@ -8443,20 +8456,26 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
84438456
# Check whether we are rendering vertically or horizontally
84448457
if vert:
84458458
fill = self.fill_betweenx
8446-
perp_lines = functools.partial(self.hlines, colors=linecolor)
8447-
par_lines = functools.partial(self.vlines, colors=linecolor)
8459+
perp_lines = functools.partial(self.hlines, colors=linecolor,
8460+
capstyle='projecting')
8461+
par_lines = functools.partial(self.vlines, colors=linecolor,
8462+
capstyle='projecting')
84488463
else:
84498464
fill = self.fill_between
8450-
perp_lines = functools.partial(self.vlines, colors=linecolor)
8451-
par_lines = functools.partial(self.hlines, colors=linecolor)
8465+
perp_lines = functools.partial(self.vlines, colors=linecolor,
8466+
capstyle='projecting')
8467+
par_lines = functools.partial(self.hlines, colors=linecolor,
8468+
capstyle='projecting')
84528469

84538470
# Render violins
84548471
bodies = []
84558472
for stats, pos, width in zip(vpstats, positions, widths):
84568473
# The 0.5 factor reflects the fact that we plot from v-p to v+p.
84578474
vals = np.array(stats['vals'])
84588475
vals = 0.5 * width * vals / vals.max()
8459-
bodies += [fill(stats['coords'], -vals + pos, vals + pos,
8476+
bodies += [fill(stats['coords'],
8477+
-vals + pos if side in ['both', 'low'] else pos,
8478+
vals + pos if side in ['both', 'high'] else pos,
84608479
facecolor=fillcolor, alpha=0.3)]
84618480
means.append(stats['mean'])
84628481
mins.append(stats['min'])

lib/matplotlib/axes/_axes.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ class Axes(_AxesBase):
743743
| float
744744
| Callable[[GaussianKDE], float]
745745
| None = ...,
746+
side: Literal["both", "low", "high"] = ...,
746747
*,
747748
data=...,
748749
) -> dict[str, Collection]: ...
@@ -755,6 +756,7 @@ class Axes(_AxesBase):
755756
showmeans: bool = ...,
756757
showextrema: bool = ...,
757758
showmedians: bool = ...,
759+
side: Literal["both", "low", "high"] = ...,
758760
) -> dict[str, Collection]: ...
759761

760762
table = mtable.table

lib/matplotlib/pyplot.py

+2
Original file line numberDiff line numberDiff line change
@@ -4075,6 +4075,7 @@ def violinplot(
40754075
| float
40764076
| Callable[[GaussianKDE], float]
40774077
| None = None,
4078+
side: Literal["both", "low", "high"] = "both",
40784079
*,
40794080
data=None,
40804081
) -> dict[str, Collection]:
@@ -4089,6 +4090,7 @@ def violinplot(
40894090
quantiles=quantiles,
40904091
points=points,
40914092
bw_method=bw_method,
4093+
side=side,
40924094
**({"data": data} if data is not None else {}),
40934095
)
40944096

lib/matplotlib/tests/test_axes.py

+15
Original file line numberDiff line numberDiff line change
@@ -3794,6 +3794,21 @@ def test_horiz_violinplot_custompoints_200():
37943794
showextrema=False, showmedians=False, points=200)
37953795

37963796

3797+
@image_comparison(['violinplot_sides.png'], remove_text=True, style='mpl20')
3798+
def test_violinplot_sides():
3799+
ax = plt.axes()
3800+
np.random.seed(19680801)
3801+
data = [np.random.normal(size=100)]
3802+
# Check horizontal violinplot
3803+
for pos, side in zip([0, -0.5, 0.5], ['both', 'low', 'high']):
3804+
ax.violinplot(data, positions=[pos], vert=False, showmeans=False,
3805+
showextrema=True, showmedians=True, side=side)
3806+
# Check vertical violinplot
3807+
for pos, side in zip([4, 3.5, 4.5], ['both', 'low', 'high']):
3808+
ax.violinplot(data, positions=[pos], vert=True, showmeans=False,
3809+
showextrema=True, showmedians=True, side=side)
3810+
3811+
37973812
def test_violinplot_bad_positions():
37983813
ax = plt.axes()
37993814
# First 9 digits of frac(sqrt(47))

0 commit comments

Comments
 (0)