Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new orientation parameter to Violinplot and deprecate vert #27998

Merged
merged 4 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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: 53 additions & 14 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,
showmeans=False, showextrema=True, showmedians=False,
quantiles=None, points=100, bw_method=None, side='both'):
def violinplot(self, dataset, positions=None, vert=None,
orientation='vertical', widths=0.5, showmeans=False,
showextrema=True, showmedians=False, quantiles=None,
points=100, bw_method=None, side='both',):
"""
Make a violin plot.

Expand All @@ -8370,9 +8371,21 @@ def violinplot(self, dataset, positions=None, vert=True, widths=0.5,
The positions of the violins; i.e. coordinates on the x-axis for
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.
vert : bool, optional
.. 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.

orientation : {'vertical', 'horizontal'}, default: 'vertical'
If 'horizontal', plots the violins horizontally.
Otherwise, plots the violins vertically.

.. versionadded:: 3.10

widths : float or array-like, default: 0.5
The maximum width of each violin in units of the *positions* axis.
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,
orientation='vertical', widths=0.5, showmeans=False,
showextrema=True, showmedians=False, side='both'):
"""
Draw a violin plot from pre-computed statistics.

Expand Down Expand Up @@ -8500,9 +8515,21 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
The positions of the violins; i.e. coordinates on the x-axis for
vertical violins (or y-axis for horizontal violins).

vert : bool, default: True.
If true, plots the violins vertically.
Otherwise, plots the violins horizontally.
vert : bool, optional
.. 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.

orientation : {'vertical', 'horizontal'}, default: 'vertical'
If 'horizontal', plots the violins horizontally.
Otherwise, plots the violins vertically.

.. versionadded:: 3.10

widths : float or array-like, default: 0.5
The maximum width of each violin in units of the *positions* axis.
Expand Down Expand Up @@ -8572,6 +8599,18 @@ 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")

# vert and orientation parameters are linked until vert's
# deprecation period expires. If both are selected,
# vert takes precedence.
if vert is not None:
_api.warn_deprecated(
"3.10",
name="vert: bool",
alternative="orientation: {'vertical', 'horizontal'}"
)
orientation = 'vertical' if vert else 'horizontal'
_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 +8639,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':
saranti marked this conversation as resolved.
Show resolved Hide resolved
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,8 @@ class Axes(_AxesBase):
dataset: ArrayLike | Sequence[ArrayLike],
positions: ArrayLike | None = ...,
*,
vert: bool = ...,
vert: bool | None = ...,
orientation: Literal["vertical", "horizontal"] = ...,
widths: float | ArrayLike = ...,
showmeans: bool = ...,
showextrema: bool = ...,
Expand All @@ -758,7 +759,8 @@ class Axes(_AxesBase):
vpstats: Sequence[dict[str, Any]],
positions: ArrayLike | None = ...,
*,
vert: bool = ...,
vert: bool | None = ...,
orientation: Literal["vertical", "horizontal"] = ...,
widths: float | ArrayLike = ...,
showmeans: bool = ...,
showextrema: bool = ...,
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,8 @@ def triplot(*args, **kwargs):
def violinplot(
dataset: ArrayLike | Sequence[ArrayLike],
positions: ArrayLike | None = None,
vert: bool = True,
vert: bool | None = None,
orientation: Literal["vertical", "horizontal"] = "vertical",
widths: float | ArrayLike = 0.5,
showmeans: bool = False,
showextrema: bool = True,
Expand All @@ -4161,6 +4162,7 @@ def violinplot(
dataset,
positions=positions,
vert=vert,
orientation=orientation,
widths=widths,
showmeans=showmeans,
showextrema=showextrema,
Expand Down
55 changes: 46 additions & 9 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3747,7 +3747,7 @@ def test_horiz_violinplot_baseline():
# First 9 digits of frac(sqrt(19))
np.random.seed(358898943)
data = [np.random.normal(size=100) for _ in range(4)]
ax.violinplot(data, positions=range(4), vert=False, showmeans=False,
ax.violinplot(data, positions=range(4), orientation='horizontal', showmeans=False,
showextrema=False, showmedians=False)


Expand All @@ -3757,7 +3757,7 @@ def test_horiz_violinplot_showmedians():
# First 9 digits of frac(sqrt(23))
np.random.seed(795831523)
data = [np.random.normal(size=100) for _ in range(4)]
ax.violinplot(data, positions=range(4), vert=False, showmeans=False,
ax.violinplot(data, positions=range(4), orientation='horizontal', showmeans=False,
showextrema=False, showmedians=True)


Expand All @@ -3767,7 +3767,7 @@ def test_horiz_violinplot_showmeans():
# First 9 digits of frac(sqrt(29))
np.random.seed(385164807)
data = [np.random.normal(size=100) for _ in range(4)]
ax.violinplot(data, positions=range(4), vert=False, showmeans=True,
ax.violinplot(data, positions=range(4), orientation='horizontal', showmeans=True,
showextrema=False, showmedians=False)


Expand All @@ -3777,7 +3777,7 @@ def test_horiz_violinplot_showextrema():
# First 9 digits of frac(sqrt(31))
np.random.seed(567764362)
data = [np.random.normal(size=100) for _ in range(4)]
ax.violinplot(data, positions=range(4), vert=False, showmeans=False,
ax.violinplot(data, positions=range(4), orientation='horizontal', showmeans=False,
showextrema=True, showmedians=False)


Expand All @@ -3787,7 +3787,7 @@ def test_horiz_violinplot_showall():
# First 9 digits of frac(sqrt(37))
np.random.seed(82762530)
data = [np.random.normal(size=100) for _ in range(4)]
ax.violinplot(data, positions=range(4), vert=False, showmeans=True,
ax.violinplot(data, positions=range(4), orientation='horizontal', showmeans=True,
showextrema=True, showmedians=True,
quantiles=[[0.1, 0.9], [0.2, 0.8], [0.3, 0.7], [0.4, 0.6]])

Expand All @@ -3798,7 +3798,7 @@ def test_horiz_violinplot_custompoints_10():
# First 9 digits of frac(sqrt(41))
np.random.seed(403124237)
data = [np.random.normal(size=100) for _ in range(4)]
ax.violinplot(data, positions=range(4), vert=False, showmeans=False,
ax.violinplot(data, positions=range(4), orientation='horizontal', showmeans=False,
showextrema=False, showmedians=False, points=10)


Expand All @@ -3808,7 +3808,7 @@ def test_horiz_violinplot_custompoints_200():
# First 9 digits of frac(sqrt(43))
np.random.seed(557438524)
data = [np.random.normal(size=100) for _ in range(4)]
ax.violinplot(data, positions=range(4), vert=False, showmeans=False,
ax.violinplot(data, positions=range(4), orientation='horizontal', showmeans=False,
showextrema=False, showmedians=False, points=200)


Expand All @@ -3819,11 +3819,11 @@ def test_violinplot_sides():
data = [np.random.normal(size=100)]
# Check horizontal violinplot
for pos, side in zip([0, -0.5, 0.5], ['both', 'low', 'high']):
ax.violinplot(data, positions=[pos], vert=False, showmeans=False,
ax.violinplot(data, positions=[pos], orientation='horizontal', showmeans=False,
showextrema=True, showmedians=True, side=side)
# Check vertical violinplot
for pos, side in zip([4, 3.5, 4.5], ['both', 'low', 'high']):
ax.violinplot(data, positions=[pos], vert=True, showmeans=False,
ax.violinplot(data, positions=[pos], orientation='vertical', showmeans=False,
showextrema=True, showmedians=True, side=side)


Expand Down 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')