From 7c2ea8f91dd1da02f0064f83108503d253961ecb Mon Sep 17 00:00:00 2001 From: saranti Date: Sun, 10 Mar 2024 15:23:35 +1100 Subject: [PATCH 1/7] rename tick label --- .../deprecations/27901-TS.rst | 3 +++ lib/matplotlib/axes/_axes.py | 13 ++++++---- lib/matplotlib/axes/_axes.pyi | 2 +- lib/matplotlib/cbook.py | 17 +++++++----- lib/matplotlib/cbook.pyi | 2 +- lib/matplotlib/pyplot.py | 4 +-- lib/matplotlib/tests/test_legend.py | 26 +++++++++++-------- 7 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/27901-TS.rst diff --git a/doc/api/next_api_changes/deprecations/27901-TS.rst b/doc/api/next_api_changes/deprecations/27901-TS.rst new file mode 100644 index 000000000000..0f36c179d0a2 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/27901-TS.rst @@ -0,0 +1,3 @@ +``boxplot`` tick labels +~~~~~~~~~~~~~~~~~~~~~~~ +This has been renamed to *tick_labels* for clarity and consistency with`~.Axes.bar`. diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index cc310b7da0d7..e3c7113a796e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3789,12 +3789,13 @@ def apply_mask(arrays, mask): return errorbar_container # (l0, caplines, barcols) @_preprocess_data() + @_api.rename_parameter("3.9", "labels", "tick_labels") def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, meanline=None, showmeans=None, showcaps=None, showbox=None, showfliers=None, boxprops=None, - labels=None, flierprops=None, medianprops=None, + tick_labels=None, flierprops=None, medianprops=None, meanprops=None, capprops=None, whiskerprops=None, manage_ticks=True, autorange=False, zorder=None, capwidths=None): @@ -3908,9 +3909,11 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, If `False` produces boxes with the Line2D artist. Otherwise, boxes are drawn with Patch artists. - labels : sequence, optional - Labels for each dataset (one per dataset). These are used for - x-tick labels; *not* for legend entries. + tick_labels : sequence, optional + The tick labels of each boxplot. + Default: None (Use default numeric labels.) + + .. versionchanged:: 3.9 manage_ticks : bool, default: True If True, the tick locations and labels will be adjusted to match @@ -3994,7 +3997,7 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, bootstrap = mpl.rcParams['boxplot.bootstrap'] bxpstats = cbook.boxplot_stats(x, whis=whis, bootstrap=bootstrap, - labels=labels, autorange=autorange) + tick_labels=tick_labels, autorange=autorange) if notch is None: notch = mpl.rcParams['boxplot.notch'] if vert is None: diff --git a/lib/matplotlib/axes/_axes.pyi b/lib/matplotlib/axes/_axes.pyi index 97424419d234..18c10c1452ba 100644 --- a/lib/matplotlib/axes/_axes.pyi +++ b/lib/matplotlib/axes/_axes.pyi @@ -362,7 +362,7 @@ class Axes(_AxesBase): showbox: bool | None = ..., showfliers: bool | None = ..., boxprops: dict[str, Any] | None = ..., - labels: Sequence[str] | None = ..., + tick_labels: Sequence[str] | None = ..., flierprops: dict[str, Any] | None = ..., medianprops: dict[str, Any] | None = ..., meanprops: dict[str, Any] | None = ..., diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index ff6b2a15ec35..df9e29338c48 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1126,7 +1126,8 @@ def _broadcast_with_masks(*args, compress=False): return inputs -def boxplot_stats(X, whis=1.5, bootstrap=None, labels=None, +@_api.rename_parameter("3.9", "labels", "tick_labels") +def boxplot_stats(X, whis=1.5, bootstrap=None, tick_labels=None, autorange=False): r""" Return a list of dictionaries of statistics used to draw a series of box @@ -1161,10 +1162,12 @@ def boxplot_stats(X, whis=1.5, bootstrap=None, labels=None, Number of times the confidence intervals around the median should be bootstrapped (percentile method). - labels : array-like, optional + tick_labels : array-like, optional Labels for each dataset. Length must be compatible with dimensions of *X*. + .. versionchanged:: 3.9 + autorange : bool, optional (False) When `True` and the data are distributed such that the 25th and 75th percentiles are equal, ``whis`` is set to (0, 100) such that the @@ -1240,13 +1243,13 @@ def _compute_conf_interval(data, med, iqr, bootstrap): X = _reshape_2D(X, "X") ncols = len(X) - if labels is None: - labels = itertools.repeat(None) - elif len(labels) != ncols: - raise ValueError("Dimensions of labels and X must be compatible") + if tick_labels is None: + tick_labels = itertools.repeat(None) + elif len(tick_labels) != ncols: + raise ValueError("Dimensions of tick labels and X must be compatible") input_whis = whis - for ii, (x, label) in enumerate(zip(X, labels)): + for ii, (x, label) in enumerate(zip(X, tick_labels)): # empty dict stats = {} diff --git a/lib/matplotlib/cbook.pyi b/lib/matplotlib/cbook.pyi index c60ae0781af1..832a8003bdcf 100644 --- a/lib/matplotlib/cbook.pyi +++ b/lib/matplotlib/cbook.pyi @@ -135,7 +135,7 @@ def boxplot_stats( X: ArrayLike, whis: float | tuple[float, float] = ..., bootstrap: int | None = ..., - labels: ArrayLike | None = ..., + tick_labels: ArrayLike | None = ..., autorange: bool = ..., ) -> list[dict[str, Any]]: ... diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 6318b99539df..14c4941c7328 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2852,7 +2852,7 @@ def boxplot( showbox: bool | None = None, showfliers: bool | None = None, boxprops: dict[str, Any] | None = None, - labels: Sequence[str] | None = None, + tick_labels: Sequence[str] | None = None, flierprops: dict[str, Any] | None = None, medianprops: dict[str, Any] | None = None, meanprops: dict[str, Any] | None = None, @@ -2883,7 +2883,7 @@ def boxplot( showbox=showbox, showfliers=showfliers, boxprops=boxprops, - labels=labels, + tick_labels=tick_labels, flierprops=flierprops, medianprops=medianprops, meanprops=meanprops, diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 73892698882c..1f6ba164fcc5 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -1437,16 +1437,20 @@ def test_legend_text(): assert_allclose(leg_bboxes[1].bounds, leg_bboxes[0].bounds) -def test_boxplot_labels(): - # Test that boxplot(..., labels=) sets the tick labels but not legend entries - # This is not consistent with other plot types but is the current behavior. - fig, ax = plt.subplots() +def test_boxplot_tick_labels(): + # Test the renamed `tick_labels` parameter. + # Test for deprecation of old name `labels`. + np.random.seed(19680801) np.random.seed(19680801) data = np.random.random((10, 3)) - bp = ax.boxplot(data, labels=['A', 'B', 'C']) - # Check that labels set the tick labels ... - assert [l.get_text() for l in ax.get_xticklabels()] == ['A', 'B', 'C'] - # ... but not legend entries - handles, labels = ax.get_legend_handles_labels() - assert len(handles) == 0 - assert len(labels) == 0 + + fig, axs = plt.subplots(nrows=1, ncols=2, sharey=True) + # Should get deprecation warning for `labels` + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='has been renamed \'tick_labels\''): + axs[0].boxplot(data, labels=['A', 'B', 'C']) + assert [l.get_text() for l in axs[0].get_xticklabels()] == ['A', 'B', 'C'] + + # Test the new tick_labels parameter + axs[1].boxplot(data, tick_labels=['A', 'B', 'C']) + assert [l.get_text() for l in axs[1].get_xticklabels()] == ['A', 'B', 'C'] From e11978ed8aac0b7b2dd5dac442c51f115a293def Mon Sep 17 00:00:00 2001 From: saranti Date: Mon, 11 Mar 2024 13:58:01 +1100 Subject: [PATCH 2/7] fix docs --- doc/api/next_api_changes/deprecations/27901-TS.rst | 2 +- lib/matplotlib/axes/_axes.py | 6 ++++-- lib/matplotlib/cbook.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/api/next_api_changes/deprecations/27901-TS.rst b/doc/api/next_api_changes/deprecations/27901-TS.rst index 0f36c179d0a2..e31b77e2c6f8 100644 --- a/doc/api/next_api_changes/deprecations/27901-TS.rst +++ b/doc/api/next_api_changes/deprecations/27901-TS.rst @@ -1,3 +1,3 @@ ``boxplot`` tick labels ~~~~~~~~~~~~~~~~~~~~~~~ -This has been renamed to *tick_labels* for clarity and consistency with`~.Axes.bar`. +The parameter *labels* has been renamed to *tick_labels* for clarity and consistency with `~.Axes.bar`. diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index e3c7113a796e..736849dc67d3 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3909,9 +3909,11 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, If `False` produces boxes with the Line2D artist. Otherwise, boxes are drawn with Patch artists. - tick_labels : sequence, optional + tick_labels : list of str, optional The tick labels of each boxplot. - Default: None (Use default numeric labels.) + Ticks are always placed at the box *positions*. If *tick_labels* is given, + the ticks are labelled accordingly. Otherwise, they keep their numeric + values. .. versionchanged:: 3.9 diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index df9e29338c48..3867bd31a01d 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1246,7 +1246,7 @@ def _compute_conf_interval(data, med, iqr, bootstrap): if tick_labels is None: tick_labels = itertools.repeat(None) elif len(tick_labels) != ncols: - raise ValueError("Dimensions of tick labels and X must be compatible") + raise ValueError("Dimensions of tick_labels and X must be compatible") input_whis = whis for ii, (x, label) in enumerate(zip(X, tick_labels)): From 7ccfdf9cbf7723bafc5085cd901f087d6e4a5466 Mon Sep 17 00:00:00 2001 From: saranti Date: Mon, 11 Mar 2024 14:04:23 +1100 Subject: [PATCH 3/7] update label param on example docs --- galleries/examples/statistics/boxplot.py | 12 ++++++------ galleries/examples/statistics/boxplot_color.py | 2 +- galleries/examples/statistics/bxp.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/galleries/examples/statistics/boxplot.py b/galleries/examples/statistics/boxplot.py index 79b05386c5d9..ccdaab97d24a 100644 --- a/galleries/examples/statistics/boxplot.py +++ b/galleries/examples/statistics/boxplot.py @@ -28,23 +28,23 @@ # Demonstrate how to toggle the display of different elements: fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) -axs[0, 0].boxplot(data, labels=labels) +axs[0, 0].boxplot(data, tick_labels=labels) axs[0, 0].set_title('Default', fontsize=fs) -axs[0, 1].boxplot(data, labels=labels, showmeans=True) +axs[0, 1].boxplot(data, tick_labels=labels, showmeans=True) axs[0, 1].set_title('showmeans=True', fontsize=fs) -axs[0, 2].boxplot(data, labels=labels, showmeans=True, meanline=True) +axs[0, 2].boxplot(data, tick_labels=labels, showmeans=True, meanline=True) axs[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs) -axs[1, 0].boxplot(data, labels=labels, showbox=False, showcaps=False) +axs[1, 0].boxplot(data, tick_labels=labels, showbox=False, showcaps=False) tufte_title = 'Tufte Style \n(showbox=False,\nshowcaps=False)' axs[1, 0].set_title(tufte_title, fontsize=fs) -axs[1, 1].boxplot(data, labels=labels, notch=True, bootstrap=10000) +axs[1, 1].boxplot(data, tick_labels=labels, notch=True, bootstrap=10000) axs[1, 1].set_title('notch=True,\nbootstrap=10000', fontsize=fs) -axs[1, 2].boxplot(data, labels=labels, showfliers=False) +axs[1, 2].boxplot(data, tick_labels=labels, showfliers=False) axs[1, 2].set_title('showfliers=False', fontsize=fs) for ax in axs.flat: diff --git a/galleries/examples/statistics/boxplot_color.py b/galleries/examples/statistics/boxplot_color.py index 496844236323..3491253aaf3e 100644 --- a/galleries/examples/statistics/boxplot_color.py +++ b/galleries/examples/statistics/boxplot_color.py @@ -26,7 +26,7 @@ bplot = ax.boxplot(fruit_weights, patch_artist=True, # fill with color - labels=labels) # will be used to label x-ticks + tick_labels=labels) # will be used to label x-ticks # fill with colors for patch, color in zip(bplot['boxes'], colors): diff --git a/galleries/examples/statistics/bxp.py b/galleries/examples/statistics/bxp.py index d78747523325..f44193166ff1 100644 --- a/galleries/examples/statistics/bxp.py +++ b/galleries/examples/statistics/bxp.py @@ -25,7 +25,7 @@ labels = list('ABCD') # compute the boxplot stats -stats = cbook.boxplot_stats(data, labels=labels, bootstrap=10000) +stats = cbook.boxplot_stats(data, tick_labels=labels, bootstrap=10000) # %% # After we've computed the stats, we can go through and change anything. From f44b842cf0574abde0284a5fbb6ad1cf7bb0c0f1 Mon Sep 17 00:00:00 2001 From: saranti Date: Mon, 11 Mar 2024 22:27:22 +1100 Subject: [PATCH 4/7] fix failing tests --- lib/matplotlib/tests/test_cbook.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 1f4f96324e9e..45481578f58b 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -145,7 +145,7 @@ def test_results_whiskers_percentiles(self): def test_results_withlabels(self): labels = ['Test1', 2, 'Aardvark', 4] - results = cbook.boxplot_stats(self.data, labels=labels) + results = cbook.boxplot_stats(self.data, tick_labels=labels) for lab, res in zip(labels, results): assert res['label'] == lab @@ -156,7 +156,7 @@ def test_results_withlabels(self): def test_label_error(self): labels = [1, 2] with pytest.raises(ValueError): - cbook.boxplot_stats(self.data, labels=labels) + cbook.boxplot_stats(self.data, tick_labels=labels) def test_bad_dims(self): data = np.random.normal(size=(34, 34, 34)) From a864eead2da96cf4f0aca031a7d9abb54e21fd97 Mon Sep 17 00:00:00 2001 From: saranti Date: Wed, 13 Mar 2024 18:18:04 +1100 Subject: [PATCH 5/7] move legend, fix doc and remove double line --- lib/matplotlib/cbook.py | 2 ++ lib/matplotlib/tests/test_axes.py | 18 ++++++++++++++++++ lib/matplotlib/tests/test_legend.py | 19 ------------------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 3867bd31a01d..e3a98fa7c58c 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1167,6 +1167,8 @@ def boxplot_stats(X, whis=1.5, bootstrap=None, tick_labels=None, dimensions of *X*. .. versionchanged:: 3.9 + Renamed from *labels*, which is deprecated since 3.9 + and will be removed in 3.11. autorange : bool, optional (False) When `True` and the data are distributed such that the 25th and 75th diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 195b1986f028..4e659a4d841d 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -8966,3 +8966,21 @@ def test_axes_clear_behavior(fig_ref, fig_test, which): ax_ref.grid(True) ax_test.grid(True) + + +def test_boxplot_tick_labels(): + # Test the renamed `tick_labels` parameter. + # Test for deprecation of old name `labels`. + np.random.seed(19680801) + data = np.random.random((10, 3)) + + fig, axs = plt.subplots(nrows=1, ncols=2, sharey=True) + # Should get deprecation warning for `labels` + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='has been renamed \'tick_labels\''): + axs[0].boxplot(data, labels=['A', 'B', 'C']) + assert [l.get_text() for l in axs[0].get_xticklabels()] == ['A', 'B', 'C'] + + # Test the new tick_labels parameter + axs[1].boxplot(data, tick_labels=['A', 'B', 'C']) + assert [l.get_text() for l in axs[1].get_xticklabels()] == ['A', 'B', 'C'] diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 1f6ba164fcc5..3b3145006427 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -1435,22 +1435,3 @@ def test_legend_text(): leg_bboxes.append( leg.get_window_extent().transformed(ax.transAxes.inverted())) assert_allclose(leg_bboxes[1].bounds, leg_bboxes[0].bounds) - - -def test_boxplot_tick_labels(): - # Test the renamed `tick_labels` parameter. - # Test for deprecation of old name `labels`. - np.random.seed(19680801) - np.random.seed(19680801) - data = np.random.random((10, 3)) - - fig, axs = plt.subplots(nrows=1, ncols=2, sharey=True) - # Should get deprecation warning for `labels` - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match='has been renamed \'tick_labels\''): - axs[0].boxplot(data, labels=['A', 'B', 'C']) - assert [l.get_text() for l in axs[0].get_xticklabels()] == ['A', 'B', 'C'] - - # Test the new tick_labels parameter - axs[1].boxplot(data, tick_labels=['A', 'B', 'C']) - assert [l.get_text() for l in axs[1].get_xticklabels()] == ['A', 'B', 'C'] From d8a2e024338a3d68f69a3b08468a1a8b6deb0c57 Mon Sep 17 00:00:00 2001 From: saranti Date: Wed, 13 Mar 2024 19:22:04 +1100 Subject: [PATCH 6/7] fix docstring --- lib/matplotlib/cbook.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index e3a98fa7c58c..9e30d801f29b 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1167,8 +1167,8 @@ def boxplot_stats(X, whis=1.5, bootstrap=None, tick_labels=None, dimensions of *X*. .. versionchanged:: 3.9 - Renamed from *labels*, which is deprecated since 3.9 - and will be removed in 3.11. + Renamed from *labels*, which is deprecated since 3.9 + and will be removed in 3.11. autorange : bool, optional (False) When `True` and the data are distributed such that the 25th and 75th From 6fce3114081d0a895029732d9c70a9c590ecbb57 Mon Sep 17 00:00:00 2001 From: saranti Date: Wed, 13 Mar 2024 20:12:44 +1100 Subject: [PATCH 7/7] fix docstring on axes.py --- lib/matplotlib/axes/_axes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 736849dc67d3..e2b8d13b52a2 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3916,6 +3916,8 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, values. .. versionchanged:: 3.9 + Renamed from *labels*, which is deprecated since 3.9 + and will be removed in 3.11. manage_ticks : bool, default: True If True, the tick locations and labels will be adjusted to match