Skip to content

Commit

Permalink
add violin orientation param
Browse files Browse the repository at this point in the history
  • Loading branch information
saranti committed Apr 14, 2024
1 parent 9955a7c commit e0a39ce
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 24 deletions.
7 changes: 7 additions & 0 deletions doc/api/next_api_changes/deprecations/27998-TS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
``violinplot`` and ``violin`` *vert* parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The parameter *vert: bool* has been deprecated on `~.Axes.violinplot` and
`~.Axes.violin`.
It will be replaced by *orientation: {"vertical", "horizontal"}* for API
consistency.
21 changes: 21 additions & 0 deletions doc/users/next_whats_new/violinplot_orientation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
``violinplot`` and ``violin`` orientation parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Violinplots have a new parameter *orientation: {"vertical", "horizontal"}*
to change the orientation of the plot. This will replace the deprecated
*vert: bool* parameter.


.. plot::
:include-source: true
:alt: Example of creating 4 horizontal violinplots.

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()
np.random.seed(19680801)
all_data = [np.random.normal(0, std, 100) for std in range(6, 10)]

ax.violinplot(all_data, orientation='horizontal')
plt.show()
20 changes: 10 additions & 10 deletions galleries/examples/statistics/violinplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,37 +62,37 @@
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high')
axs[0, 5].set_title('Custom violin 6', fontsize=fs)

axs[1, 0].violinplot(data, pos, points=80, vert=False, widths=0.7,
axs[1, 0].violinplot(data, pos, points=80, orientation='horizontal', widths=0.7,
showmeans=True, showextrema=True, showmedians=True)
axs[1, 0].set_title('Custom violin 7', fontsize=fs)

axs[1, 1].violinplot(data, pos, points=100, vert=False, widths=0.9,
axs[1, 1].violinplot(data, pos, points=100, orientation='horizontal', widths=0.9,
showmeans=True, showextrema=True, showmedians=True,
bw_method='silverman')
axs[1, 1].set_title('Custom violin 8', fontsize=fs)

axs[1, 2].violinplot(data, pos, points=200, vert=False, widths=1.1,
axs[1, 2].violinplot(data, pos, points=200, orientation='horizontal', widths=1.1,
showmeans=True, showextrema=True, showmedians=True,
bw_method=0.5)
axs[1, 2].set_title('Custom violin 9', fontsize=fs)

axs[1, 3].violinplot(data, pos, points=200, vert=False, widths=1.1,
axs[1, 3].violinplot(data, pos, points=200, orientation='horizontal', widths=1.1,
showmeans=True, showextrema=True, showmedians=True,
quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]],
bw_method=0.5)
axs[1, 3].set_title('Custom violin 10', fontsize=fs)

axs[1, 4].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1,
showmeans=True, showextrema=True, showmedians=True,
axs[1, 4].violinplot(data[-1:], pos[-1:], points=200, orientation='horizontal',
widths=1.1, showmeans=True, showextrema=True, showmedians=True,
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5)
axs[1, 4].set_title('Custom violin 11', fontsize=fs)

axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1,
showmeans=True, showextrema=True, showmedians=True,
axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, orientation='horizontal',
widths=1.1, showmeans=True, showextrema=True, showmedians=True,
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='low')

axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1,
showmeans=True, showextrema=True, showmedians=True,
axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, orientation='horizontal',
widths=1.1, showmeans=True, showextrema=True, showmedians=True,
quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high')
axs[1, 5].set_title('Custom violin 12', fontsize=fs)

Expand Down
67 changes: 56 additions & 11 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8350,9 +8350,10 @@ def matshow(self, Z, **kwargs):

@_api.make_keyword_only("3.9", "vert")
@_preprocess_data(replace_names=["dataset"])
def violinplot(self, dataset, positions=None, vert=True, widths=0.5,
def violinplot(self, dataset, positions=None, vert=None, widths=0.5,
showmeans=False, showextrema=True, showmedians=False,
quantiles=None, points=100, bw_method=None, side='both'):
quantiles=None, points=100, bw_method=None, side='both',
orientation=None):
"""
Make a violin plot.
Expand All @@ -8371,8 +8372,14 @@ def violinplot(self, dataset, positions=None, vert=True, widths=0.5,
vertical violins (or y-axis for horizontal violins).
vert : bool, default: True.
If true, creates a vertical violin plot.
Otherwise, creates a horizontal violin plot.
.. deprecated:: 3.10
Use *orientation* instead.
If this is given during the deprecation period, it overrides
the *orientation* parameter.
If True, plots the violins vertically.
If False, plots the violins horizontally.
widths : float or array-like, default: 0.5
The maximum width of each violin in units of the *positions* axis.
Expand Down Expand Up @@ -8407,6 +8414,12 @@ def violinplot(self, dataset, positions=None, vert=True, widths=0.5,
'both' plots standard violins. 'low'/'high' only
plots the side below/above the positions value.
orientation : {'vertical', 'horizontal'}, default: 'vertical'
If 'horizontal', plots the violins horizontally.
Otherwise, plots the violins vertically.
.. versionadded:: 3.10
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
Expand Down Expand Up @@ -8457,12 +8470,14 @@ def _kde_method(X, coords):
vpstats = cbook.violin_stats(dataset, _kde_method, points=points,
quantiles=quantiles)
return self.violin(vpstats, positions=positions, vert=vert,
widths=widths, showmeans=showmeans,
showextrema=showextrema, showmedians=showmedians, side=side)
orientation=orientation, widths=widths,
showmeans=showmeans, showextrema=showextrema,
showmedians=showmedians, side=side)

@_api.make_keyword_only("3.9", "vert")
def violin(self, vpstats, positions=None, vert=True, widths=0.5,
showmeans=False, showextrema=True, showmedians=False, side='both'):
def violin(self, vpstats, positions=None, vert=None, widths=0.5,
showmeans=False, showextrema=True, showmedians=False, side='both',
orientation=None):
"""
Draw a violin plot from pre-computed statistics.
Expand Down Expand Up @@ -8501,8 +8516,14 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
vertical violins (or y-axis for horizontal violins).
vert : bool, default: True.
If true, plots the violins vertically.
Otherwise, plots the violins horizontally.
.. deprecated:: 3.10
Use *orientation* instead.
If this is given during the deprecation period, it overrides
the *orientation* parameter.
If True, plots the violins vertically.
If False, plots the violins horizontally.
widths : float or array-like, default: 0.5
The maximum width of each violin in units of the *positions* axis.
Expand All @@ -8522,6 +8543,12 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
'both' plots standard violins. 'low'/'high' only
plots the side below/above the positions value.
orientation : {'vertical', 'horizontal'}, default: 'vertical'
If 'horizontal', plots the violins horizontally.
Otherwise, plots the violins vertically.
.. versionadded:: 3.10
Returns
-------
dict
Expand Down Expand Up @@ -8572,6 +8599,24 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
datashape_message = ("List of violinplot statistics and `{0}` "
"values must have the same length")

if vert is not None:
_api.warn_deprecated(
"3.10",
name="vert: bool",
alternative="orientation: {'vertical', 'horizontal'}"
)

# vert and orientation parameters are linked until vert's
# deprecation period expires. If both are selected,
# vert takes precedence.
if vert or vert is None and orientation is None:
orientation = 'vertical'
elif vert is False:
orientation = 'horizontal'

if orientation is not None:
_api.check_in_list(['horizontal', 'vertical'], orientation=orientation)

# Validate positions
if positions is None:
positions = range(1, N + 1)
Expand Down Expand Up @@ -8600,7 +8645,7 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
fillcolor = linecolor = self._get_lines.get_next_color()

# Check whether we are rendering vertically or horizontally
if vert:
if orientation == 'vertical':
fill = self.fill_betweenx
if side in ['low', 'high']:
perp_lines = functools.partial(self.hlines, colors=linecolor,
Expand Down
6 changes: 4 additions & 2 deletions lib/matplotlib/axes/_axes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ class Axes(_AxesBase):
dataset: ArrayLike | Sequence[ArrayLike],
positions: ArrayLike | None = ...,
*,
vert: bool = ...,
vert: bool | None = ...,
widths: float | ArrayLike = ...,
showmeans: bool = ...,
showextrema: bool = ...,
Expand All @@ -751,19 +751,21 @@ class Axes(_AxesBase):
| Callable[[GaussianKDE], float]
| None = ...,
side: Literal["both", "low", "high"] = ...,
orientation: Literal["vertical", "horizontal"] | None = ...,
data=...,
) -> dict[str, Collection]: ...
def violin(
self,
vpstats: Sequence[dict[str, Any]],
positions: ArrayLike | None = ...,
*,
vert: bool = ...,
vert: bool | None = ...,
widths: float | ArrayLike = ...,
showmeans: bool = ...,
showextrema: bool = ...,
showmedians: bool = ...,
side: Literal["both", "low", "high"] = ...,
orientation: Literal["vertical", "horizontal"] | None = ...,
) -> dict[str, Collection]: ...

table = mtable.table
Expand Down
4 changes: 3 additions & 1 deletion lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4142,7 +4142,7 @@ def triplot(*args, **kwargs):
def violinplot(
dataset: ArrayLike | Sequence[ArrayLike],
positions: ArrayLike | None = None,
vert: bool = True,
vert: bool | None = None,
widths: float | ArrayLike = 0.5,
showmeans: bool = False,
showextrema: bool = True,
Expand All @@ -4154,6 +4154,7 @@ def violinplot(
| Callable[[GaussianKDE], float]
| None = None,
side: Literal["both", "low", "high"] = "both",
orientation: Literal["vertical", "horizontal"] | None = None,
*,
data=None,
) -> dict[str, Collection]:
Expand All @@ -4169,6 +4170,7 @@ def violinplot(
points=points,
bw_method=bw_method,
side=side,
orientation=orientation,
**({"data": data} if data is not None else {}),
)

Expand Down
37 changes: 37 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9034,3 +9034,40 @@ def test_latex_pie_percent(fig_test, fig_ref):

ax1 = fig_ref.subplots()
ax1.pie(data, autopct=r"%1.0f\%%", textprops={'usetex': True})


@check_figures_equal(extensions=['png'])
def test_violinplot_orientation(fig_test, fig_ref):
# Test the `orientation : {'vertical', 'horizontal'}`
# parameter and deprecation of `vert: bool`.
fig, axs = plt.subplots(nrows=1, ncols=3)
np.random.seed(19680801)
all_data = [np.random.normal(0, std, 100) for std in range(6, 10)]

axs[0].violinplot(all_data) # Default vertical plot.
# xticks and yticks should be at their default position.
assert all(axs[0].get_xticks() == np.array(
[0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5]))
assert all(axs[0].get_yticks() == np.array(
[-30., -20., -10., 0., 10., 20., 30.]))

# Horizontal plot using new `orientation` keyword.
axs[1].violinplot(all_data, orientation='horizontal')
# xticks and yticks should be swapped.
assert all(axs[1].get_xticks() == np.array(
[-30., -20., -10., 0., 10., 20., 30.]))
assert all(axs[1].get_yticks() == np.array(
[0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5]))

plt.close()

# Deprecation of `vert: bool` keyword
with pytest.warns(mpl.MatplotlibDeprecationWarning,
match='vert: bool was deprecated in Matplotlib 3.10'):
# Compare images between a figure that
# uses vert and one that uses orientation.
ax_ref = fig_ref.subplots()
ax_ref.violinplot(all_data, vert=False)

ax_test = fig_test.subplots()
ax_test.violinplot(all_data, orientation='horizontal')

0 comments on commit e0a39ce

Please sign in to comment.