diff --git a/doc/api/next_api_changes/deprecations/29152_REC.rst b/doc/api/next_api_changes/deprecations/29152_REC.rst new file mode 100644 index 000000000000..26ff0b036968 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/29152_REC.rst @@ -0,0 +1,11 @@ +``pie`` *labels* and *labeldistance* parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Currently the *labels* parameter of `~.Axes.pie` is used both for annotating the +pie wedges directly, and for automatic legend entries. For consistency +with other plotting methods, in future *labels* will only be used for the legend. + +The *labeldistance* parameter will therefore default to ``None`` from Matplotlib +3.14, when it will also be deprecated and then removed in Matplotlib 3.16. To +preserve the existing behavior for now, set ``labeldistance=1.1``. For the longer +term, use the new *wedge_labels* parameter of `~.Axes.pie` or the `~.Axes.pie_label` +method instead of *labels*. diff --git a/doc/release/next_whats_new/pie_wedge_labels.rst b/doc/release/next_whats_new/pie_wedge_labels.rst new file mode 100644 index 000000000000..07a7bcf07b95 --- /dev/null +++ b/doc/release/next_whats_new/pie_wedge_labels.rst @@ -0,0 +1,26 @@ +New *wedge_labels* parameter for pie +------------------------------------ + +`~.Axes.pie` now accepts a *wedge_labels* parameter as a shortcut to the +`~.Axes.pie_label` method. This may be used for simple annotation of the wedges +of the pie chart. It can take + +* a list of strings, similar to the existing *labels* parameter +* a format string similar to the existing *autopct* parameter except that it + uses the `str.format` method, and it can handle absolute values as well as + fractions/percentages + +*wedge_labels* has an accompanying *wedge_label_distance* parameter, to control +the distance of the labels from the center of the pie. + + +.. plot:: + :include-source: true + :alt: Two pie charts. The chart on the left has labels 'foo' and 'bar' outside the wedges. The chart on the right has labels '1' and '2' inside the wedges. + + import matplotlib.pyplot as plt + + fig, (ax1, ax2) = plt.subplots(ncols=2, layout='constrained') + + ax1.pie([1, 2], wedge_labels=['foo', 'bar'], wedge_label_distance=1.1) + ax2.pie([1, 2], wedge_labels='{absval:d}', wedge_label_distance=0.6) diff --git a/galleries/examples/misc/svg_filter_pie.py b/galleries/examples/misc/svg_filter_pie.py index f8ccc5bcb22b..d438fe77b8a6 100644 --- a/galleries/examples/misc/svg_filter_pie.py +++ b/galleries/examples/misc/svg_filter_pie.py @@ -28,11 +28,11 @@ # We want to draw the shadow for each pie, but we will not use "shadow" # option as it doesn't save the references to the shadow patches. -pie = ax.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%') +pie = ax.pie(fracs, explode=explode, wedge_labels=labels, wedge_label_distance=1.1) -for w in pie.wedges: +for w, label in zip(pie.wedges, labels): # set the id with the label. - w.set_gid(w.get_label()) + w.set_gid(label) # we don't want to draw the edge of the pie w.set_edgecolor("none") diff --git a/galleries/examples/pie_and_polar_charts/bar_of_pie.py b/galleries/examples/pie_and_polar_charts/bar_of_pie.py index 7c703976db2e..6e58bba5209d 100644 --- a/galleries/examples/pie_and_polar_charts/bar_of_pie.py +++ b/galleries/examples/pie_and_polar_charts/bar_of_pie.py @@ -25,8 +25,11 @@ explode = [0.1, 0, 0] # rotate so that first wedge is split by the x-axis angle = -180 * overall_ratios[0] -pie = ax1.pie(overall_ratios, autopct='%1.1f%%', startangle=angle, - labels=labels, explode=explode) +pie = ax1.pie(overall_ratios, startangle=angle, explode=explode) + +# label the wedges with our label strings and the ratios as percentages +ax1.pie_label(pie, labels, distance=1.1) +ax1.pie_label(pie, '{frac:.1%}', distance=0.6) # bar chart parameters age_ratios = [.33, .54, .07, .06] diff --git a/galleries/examples/pie_and_polar_charts/pie_features.py b/galleries/examples/pie_and_polar_charts/pie_features.py index 4c0eeaa4526e..8a586c43788a 100644 --- a/galleries/examples/pie_and_polar_charts/pie_features.py +++ b/galleries/examples/pie_and_polar_charts/pie_features.py @@ -15,15 +15,15 @@ # ------------ # # Plot a pie chart of animals and label the slices. To add -# labels, pass a list of labels to the *labels* parameter +# labels, pass a list of labels to the *wedge_labels* parameter. import matplotlib.pyplot as plt labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' -sizes = [15, 30, 45, 10] +sizes = [12, 24, 36, 8] fig, ax = plt.subplots() -ax.pie(sizes, labels=labels) +ax.pie(sizes, wedge_labels=labels) # %% # Each slice of the pie chart is a `.patches.Wedge` object; therefore in @@ -31,16 +31,44 @@ # the *wedgeprops* argument, as demonstrated in # :doc:`/gallery/pie_and_polar_charts/nested_pie`. # +# Controlling label positions +# --------------------------- +# If you want the labels outside the pie, set a *wedge_label_distance* greater than 1. +# This is the distance from the center of the pie as a fraction of its radius. + +fig, ax = plt.subplots() +ax.pie(sizes, wedge_labels=labels, wedge_label_distance=1.1) + +# %% +# # Auto-label slices # ----------------- # -# Pass a function or format string to *autopct* to label slices. +# Pass a format string to *wedge_labels* to label slices with their values... + +fig, ax = plt.subplots() +ax.pie(sizes, wedge_labels='{absval:.1f}') + +# %% +# +# ...or with their percentages... + +fig, ax = plt.subplots() +ax.pie(sizes, wedge_labels='{frac:.1%}') + +# %% +# +# ...or both. fig, ax = plt.subplots() -ax.pie(sizes, labels=labels, autopct='%1.1f%%') +ax.pie(sizes, wedge_labels='{absval:d}\n{frac:.1%}') + +# %% +# +# For more control over labels, or to add multiple sets, see +# :doc:`/gallery/pie_and_polar_charts/pie_label`. # %% -# By default, the label values are obtained from the percent size of the slice. # # Color slices # ------------ @@ -48,8 +76,7 @@ # Pass a list of colors to *colors* to set the color of each slice. fig, ax = plt.subplots() -ax.pie(sizes, labels=labels, - colors=['olivedrab', 'rosybrown', 'gray', 'saddlebrown']) +ax.pie(sizes, colors=['olivedrab', 'rosybrown', 'gray', 'saddlebrown']) # %% # Hatch slices @@ -58,22 +85,9 @@ # Pass a list of hatch patterns to *hatch* to set the pattern of each slice. fig, ax = plt.subplots() -ax.pie(sizes, labels=labels, hatch=['**O', 'oO', 'O.O', '.||.']) - -# %% -# Swap label and autopct text positions -# ------------------------------------- -# Use the *labeldistance* and *pctdistance* parameters to position the *labels* -# and *autopct* text respectively. - -fig, ax = plt.subplots() -ax.pie(sizes, labels=labels, autopct='%1.1f%%', - pctdistance=1.25, labeldistance=.6) +ax.pie(sizes, hatch=['**O', 'oO', 'O.O', '.||.']) # %% -# *labeldistance* and *pctdistance* are ratios of the radius; therefore they -# vary between ``0`` for the center of the pie and ``1`` for the edge of the -# pie, and can be set to greater than ``1`` to place text outside the pie. # # Explode, shade, and rotate slices # --------------------------------- @@ -86,11 +100,10 @@ # # This example orders the slices, separates (explodes) them, and rotates them. -explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') +explode = (0, 0.2, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') fig, ax = plt.subplots() -ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', - shadow=True, startangle=90) +ax.pie(sizes, explode=explode, wedge_labels='{frac:.1%}', shadow=True, startangle=90) plt.show() # %% @@ -107,8 +120,7 @@ fig, ax = plt.subplots() -ax.pie(sizes, labels=labels, autopct='%.0f%%', - textprops={'size': 'small'}, radius=0.5) +ax.pie(sizes, wedge_labels='{frac:.1%}', textprops={'size': 'small'}, radius=0.5) plt.show() # %% @@ -119,8 +131,8 @@ # the `.Shadow` patch. This can be used to modify the default shadow. fig, ax = plt.subplots() -ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', - shadow={'ox': -0.04, 'edgecolor': 'none', 'shade': 0.9}, startangle=90) +ax.pie(sizes, explode=explode, shadow={'ox': -0.04, 'edgecolor': 'none', 'shade': 0.9}, + startangle=90) plt.show() # %% diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 7fa360220664..6215c26b4e80 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3514,13 +3514,13 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, self.add_container(stem_container) return stem_container - @_api.make_keyword_only("3.10", "explode") - @_preprocess_data(replace_names=["x", "explode", "labels", "colors"]) - def pie(self, x, explode=None, labels=None, colors=None, - autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, - startangle=0, radius=1, counterclock=True, - wedgeprops=None, textprops=None, center=(0, 0), - frame=False, rotatelabels=False, *, normalize=True, hatch=None): + @_preprocess_data(replace_names=["x", "explode", "labels", "colors", + "wedge_labels"]) + def pie(self, x, *, explode=None, labels=None, colors=None, wedge_labels=None, + wedge_label_distance=0.6, autopct=None, pctdistance=0.6, shadow=False, + labeldistance=False, startangle=0, radius=1, counterclock=True, + wedgeprops=None, textprops=None, center=(0, 0), frame=False, + rotatelabels=False, normalize=True, hatch=None): """ Plot a pie chart. @@ -3540,7 +3540,13 @@ def pie(self, x, explode=None, labels=None, colors=None, of the radius with which to offset each wedge. labels : list, default: None - A sequence of strings providing the labels for each wedge + A sequence of strings providing the legend labels for each wedge. + + .. deprecated:: 3.12 + In future these labels will not appear on the wedges but only + be made available for the legend (see *labeldistance* below). + To place labels on the wedges, use *wedge_labels* or the + `pie_label` method. colors : :mpltype:`color` or list of :mpltype:`color`, default: None A sequence of colors through which the pie chart will cycle. If @@ -3553,12 +3559,35 @@ def pie(self, x, explode=None, labels=None, colors=None, .. versionadded:: 3.7 + wedge_labels : str or list of str, optional + A sequence of strings providing the labels for each wedge, or a format + string with ``absval`` and/or ``frac`` placeholders. For example, to label + each wedge with its value and the percentage in brackets:: + + wedge_labels="{absval:d} ({frac:.0%})" + + For more control or to add multiple sets of labels, use `pie_label` + instead. + + .. versionadded:: 3.12 + + wedge_label_distance : float, default: 0.6 + The radial position of the wedge labels, relative to the pie radius. + Values > 1 are outside the wedge and values < 1 are inside the wedge. + + .. versionadded:: 3.12 + autopct : None or str or callable, default: None If not *None*, *autopct* is a string or function used to label the wedges with their numeric value. The label will be placed inside the wedge. If *autopct* is a format string, the label will be ``fmt % pct``. If *autopct* is a function, then it will be called. + .. admonition:: Discouraged + + Consider using the *wedge_labels* parameter or `pie_label` + method instead. + pctdistance : float, default: 0.6 The relative distance along the radius at which the text generated by *autopct* is drawn. To draw the text outside the pie, @@ -3571,6 +3600,11 @@ def pie(self, x, explode=None, labels=None, colors=None, If set to ``None``, labels are not drawn but are still stored for use in `.legend`. + .. deprecated:: 3.12 + From v3.14 *labeldistance* will default to ``None`` and will + later be removed altogether. Use *wedge_labels* and + *wedge_label_distance* or the `pie_label` method instead. + shadow : bool or dict, default: False If bool, whether to draw a shadow beneath the pie. If dict, draw a shadow passing the properties in the dict to `.Shadow`. @@ -3654,6 +3688,26 @@ def pie(self, x, explode=None, labels=None, colors=None, fracs = x if labels is None: labels = [''] * len(x) + else: + if wedge_labels is not None and labeldistance is not None: + raise ValueError( + 'wedge_labels is a replacement for labels when annotating the ' + 'wedges, so the two should not be used together. To add multiple' + 'sets of labels, use the pie_label method.' + ) + if labeldistance is False: + # NB: when the labeldistance default changes, both labeldistance and + # rotatelabels should be deprecated for removal. + msg = ( + "From %(removal)s labeldistance will default to None, so that the " + "strings provided in the labels parameter are only available for " + "the legend. Later labeldistance will be removed completely. To " + "preserve existing behavior for now, pass labeldistance=1.1. " + "Consider using the wedge_labels parameter or the pie_label method " + "instead of the labels parameter." + ) + _api.warn_deprecated("3.12", message=msg) + labeldistance = 1.1 if explode is None: explode = [0] * len(x) if len(x) != len(labels): @@ -3711,11 +3765,16 @@ def get_next_color(): pc = PieContainer(slices, x, normalize) - if labeldistance is None: + if wedge_labels is not None: + self.pie_label(pc, wedge_labels, distance=wedge_label_distance, + textprops=textprops) + + elif labeldistance is None: # Insert an empty list of texts for backwards compatibility of the # return value. pc.add_texts([]) - else: + + if labeldistance is not None: # Add labels to the wedges. labels_textprops = { 'fontsize': mpl.rcParams['xtick.labelsize'], diff --git a/lib/matplotlib/axes/_axes.pyi b/lib/matplotlib/axes/_axes.pyi index 09587ab753a3..906ed8b06b2e 100644 --- a/lib/matplotlib/axes/_axes.pyi +++ b/lib/matplotlib/axes/_axes.pyi @@ -310,6 +310,8 @@ class Axes(_AxesBase): explode: ArrayLike | None = ..., labels: Sequence[str] | None = ..., colors: ColorType | Sequence[ColorType] | None = ..., + wedge_labels: str | Sequence | None = ..., + wedge_label_distance: float | Sequence = ..., autopct: str | Callable[[float], str] | None = ..., pctdistance: float = ..., shadow: bool = ..., diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index a20553d719db..51b90b328077 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -3939,13 +3939,16 @@ def phase_spectrum( @_copy_docstring_and_deprecators(Axes.pie) def pie( x: ArrayLike, + *, explode: ArrayLike | None = None, labels: Sequence[str] | None = None, colors: ColorType | Sequence[ColorType] | None = None, + wedge_labels: str | Sequence | None = None, + wedge_label_distance: float | Sequence = 0.6, autopct: str | Callable[[float], str] | None = None, pctdistance: float = 0.6, shadow: bool = False, - labeldistance: float | None = 1.1, + labeldistance: float | None = False, startangle: float = 0, radius: float = 1, counterclock: bool = True, @@ -3954,7 +3957,6 @@ def pie( center: tuple[float, float] = (0, 0), frame: bool = False, rotatelabels: bool = False, - *, normalize: bool = True, hatch: str | Sequence[str] | None = None, data=None, @@ -3964,6 +3966,8 @@ def pie( explode=explode, labels=labels, colors=colors, + wedge_labels=wedge_labels, + wedge_label_distance=wedge_label_distance, autopct=autopct, pctdistance=pctdistance, shadow=shadow, diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index fe121e12c9f1..a4102e78071a 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -6365,8 +6365,23 @@ def test_pie_default(): colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') fig1, ax1 = plt.subplots(figsize=(8, 6)) - ax1.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90) + ax1.pie(sizes, explode=explode, wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90) + + +@image_comparison(['pie_default.png'], tol=0.01) +def test_pie_default_legacy(): + # Same as above, but uses labels parameter. Remove after labeldistance + # parameter deprecation expires. + # The slices will be ordered and plotted counter-clockwise. + labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' + sizes = [15, 30, 45, 10] + colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] + explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') + fig1, ax1 = plt.subplots(figsize=(8, 6)) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + ax1.pie(sizes, explode=explode, labels=labels, colors=colors, + autopct='%1.1f%%', shadow=True, startangle=90) @image_comparison(['pie_linewidth_0', 'pie_linewidth_0', 'pie_linewidth_0'], @@ -6378,27 +6393,30 @@ def test_pie_linewidth_0(): colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') - plt.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, + plt.pie(sizes, explode=explode, wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, wedgeprops={'linewidth': 0}) # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') - # Reuse testcase from above for a labeled data test + # Reuse testcase from above for a labeled data test. Include legend labels + # to smoke test that they are correctly unpacked. data = {"l": labels, "s": sizes, "c": colors, "ex": explode} fig = plt.figure() ax = fig.gca() - ax.pie("s", explode="ex", labels="l", colors="c", + ax.pie("s", explode="ex", wedge_labels="l", colors="c", wedge_label_distance=1.1, autopct='%1.1f%%', shadow=True, startangle=90, - wedgeprops={'linewidth': 0}, data=data) + labels="l", labeldistance=None, wedgeprops={'linewidth': 0}, + data=data) ax.axis('equal') # And again to test the pyplot functions which should also be able to be # called with a data kwarg plt.figure() - plt.pie("s", explode="ex", labels="l", colors="c", + plt.pie("s", explode="ex", wedge_labels="l", colors="c", wedge_label_distance=1.1, autopct='%1.1f%%', shadow=True, startangle=90, - wedgeprops={'linewidth': 0}, data=data) + labels="l", labeldistance=None, wedgeprops={'linewidth': 0}, + data=data) plt.axis('equal') @@ -6410,8 +6428,8 @@ def test_pie_center_radius(): colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') - plt.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, + plt.pie(sizes, explode=explode, wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, wedgeprops={'linewidth': 0}, center=(1, 2), radius=1.5) plt.annotate("Center point", xy=(1, 2), xytext=(1, 1.3), @@ -6430,8 +6448,8 @@ def test_pie_linewidth_2(): colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') - plt.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, + plt.pie(sizes, explode=explode, wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, wedgeprops={'linewidth': 2}) # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') @@ -6445,8 +6463,8 @@ def test_pie_ccw_true(): colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') - plt.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, + plt.pie(sizes, explode=explode, wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, counterclock=True) # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') @@ -6461,35 +6479,53 @@ def test_pie_frame_grid(): # only "explode" the 2nd slice (i.e. 'Hogs') explode = (0, 0.1, 0, 0) - plt.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, + plt.pie(sizes, explode=explode, wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, wedgeprops={'linewidth': 0}, frame=True, center=(2, 2)) - plt.pie(sizes[::-1], explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, + plt.pie(sizes[::-1], explode=explode, wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, wedgeprops={'linewidth': 0}, frame=True, center=(5, 2)) - plt.pie(sizes, explode=explode[::-1], labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, + plt.pie(sizes, explode=explode[::-1], wedge_labels=labels, wedge_label_distance=1.1, + colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, wedgeprops={'linewidth': 0}, frame=True, center=(3, 5)) # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') +@image_comparison(['pie_rotatelabels_true.png'], style='mpl20', tol=0.009) +def test_pie_label_rotate(): + # The slices will be ordered and plotted counter-clockwise. + labels = 'Hogwarts', 'Frogs', 'Dogs', 'Logs' + sizes = [15, 30, 45, 10] + colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] + explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') + + pie = plt.pie(sizes, explode=explode, wedge_labels='{frac:.1%}', colors=colors, + shadow=True, startangle=90) + plt.pie_label(pie, labels, distance=1.1, rotate=True) + # Set aspect ratio to be equal so that pie is drawn as a circle. + plt.axis('equal') + + @image_comparison(['pie_rotatelabels_true.png'], style='mpl20', tol=0.009) def test_pie_rotatelabels_true(): + # As above but using legacy labels and rotatelabels parameters. Remove + # when the labeldistance parameter deprecation expires. # The slices will be ordered and plotted counter-clockwise. labels = 'Hogwarts', 'Frogs', 'Dogs', 'Logs' sizes = [15, 30, 45, 10] colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') - plt.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, - rotatelabels=True) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + plt.pie(sizes, explode=explode, labels=labels, colors=colors, + autopct='%1.1f%%', shadow=True, startangle=90, + rotatelabels=True) # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') @@ -6501,7 +6537,7 @@ def test_pie_nolabel_but_legend(): colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral'] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') plt.pie(sizes, explode=explode, labels=labels, colors=colors, - autopct='%1.1f%%', shadow=True, startangle=90, labeldistance=None, + wedge_labels='{frac:.1%}', shadow=True, startangle=90, labeldistance=None, rotatelabels=True) plt.axis('equal') plt.ylim(-1.2, 1.2) @@ -6542,9 +6578,13 @@ def test_pie_textprops(): rotation_mode="anchor", size=12, color="red") - _, texts, autopct = plt.gca().pie(data, labels=labels, autopct='%.2f', - textprops=textprops) - for labels in [texts, autopct]: + fig, ax = plt.subplots() + + pie1 = ax.pie(data, wedge_labels=labels, autopct='%.2f', textprops=textprops) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + pie2 = ax.pie(data, labels=labels, textprops=textprops) + + for labels in pie1.texts + pie2.texts: for tx in labels: assert tx.get_ha() == textprops["horizontalalignment"] assert tx.get_va() == textprops["verticalalignment"] @@ -6572,7 +6612,7 @@ def test_pie_invalid_labels(): # Test ValueError raised when feeding short labels list to axes.pie fig, ax = plt.subplots() with pytest.raises(ValueError): - ax.pie([1, 2, 3], labels=["One", "Two"]) + ax.pie([1, 2, 3], labels=["One", "Two"], labeldistance=None) def test_pie_invalid_radius(): @@ -6582,6 +6622,13 @@ def test_pie_invalid_radius(): ax.pie([1, 2, 3], radius=-5) +def test_pie_wedge_labels_and_labels(): + fig, ax = plt.subplots() + with pytest.raises(ValueError, match='wedge_labels is a replacement for labels'): + ax.pie([1, 2], wedge_labels=['spam', 'eggs'], labels=['bacon', 'beans'], + labeldistance=1.2) + + def test_normalize_kwarg_pie(): fig, ax = plt.subplots() x = [0.3, 0.3, 0.1] @@ -10011,10 +10058,10 @@ def test_pie_non_finite_values(): df = [5, float('nan'), float('inf')] with pytest.raises(ValueError, match='Wedge sizes must be finite numbers'): - ax.pie(df, labels=['A', 'B', 'C']) + ax.pie(df) def test_pie_all_zeros(): fig, ax = plt.subplots() with pytest.raises(ValueError, match="All wedge sizes are zero"): - ax.pie([0, 0], labels=["A", "B"]) + ax.pie([0, 0]) diff --git a/lib/matplotlib/tests/test_container.py b/lib/matplotlib/tests/test_container.py index b7dfe1196685..d27ee1115171 100644 --- a/lib/matplotlib/tests/test_container.py +++ b/lib/matplotlib/tests/test_container.py @@ -57,10 +57,13 @@ def test_barcontainer_position_centers__bottoms__tops(): def test_piecontainer_remove(): fig, ax = plt.subplots() - pie = ax.pie([2, 3], labels=['foo', 'bar'], autopct="%1.0f%%") + pie = ax.pie([2, 3], wedge_labels=['foo', 'bar'], autopct="%1.0f%%") ax.pie_label(pie, ['baz', 'qux']) + assert len(ax.patches) == 2 - assert len(ax.texts) == 6 + # We have added 6 labels but pie also adds an empty Text artist to each + # wedge if labeldistance is not None and labels is not passed + assert len(ax.texts) == 8 pie.remove() assert not ax.patches