Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions doc/api/next_api_changes/behavior/29152_REC.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Return value of ``pie``
~~~~~~~~~~~~~~~~~~~~~~~
Previously, if no *labels* were passed to `~.Axes.pie`, and *labeldistance* was
not ``None``, empty text labels would be added to the axes and returned. This
is no longer the case. To continue creating empty labels, either pass an empty
string with the new *wedge_labels* parameter ``wedge_labels=''`` or, for
compatibility with older Matplotlib versions, pass an empty string for each wedge
via the *labels* parameter, i.e. ``labels=[''] * number_of_wedges``. Note the
latter option will stop working at Matplotlib 3.16.
10 changes: 10 additions & 0 deletions doc/api/next_api_changes/deprecations/29152_REC.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
``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 longer
term, use the new *wedge_labels* parameter of `~.Axes.pie` instead of *labels*.
18 changes: 18 additions & 0 deletions doc/release/next_whats_new/pie_wedge_labels.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
New *wedge_labels* parameter for pie
------------------------------------

`~.Axes.pie` now accepts a *wedge_labels* parameter which may be used to
annotate the wedges of the pie chart. It can take

* a list of strings, similar to the existing *labels* parameter
* a format string in analogy to the existing *autopct* parameter except that it
uses the `str.format` method, and it can handle absolute values as well as
fractions/percentages

To add multiple labels per wedge, *wedge_labels* can take a sequence of any combination
of the above two options.

*wedge_labels* have accompanying *wedge_label_distance* and *rotate_wedge_labels*
parameters, to customise the position and rotation of the labels.

For examples, see :doc:`/gallery/pie_and_polar_charts/pie_features`.
7 changes: 4 additions & 3 deletions galleries/examples/misc/svg_filter_pie.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@

# 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.
pies = ax.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%')
pies = ax.pie(fracs, explode=explode,
wedge_labels=[labels, '{frac:.1%}'], wedge_label_distance=[1.1, 0.6])

for w in pies[0]:
for w, label in zip(pies[0], 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")
Expand Down
5 changes: 3 additions & 2 deletions galleries/examples/pie_and_polar_charts/bar_of_pie.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
explode = [0.1, 0, 0]
# rotate so that first wedge is split by the x-axis
angle = -180 * overall_ratios[0]
wedges, *_ = ax1.pie(overall_ratios, autopct='%1.1f%%', startangle=angle,
labels=labels, explode=explode)
wedges, *_ = ax1.pie(
overall_ratios, startangle=angle, explode=explode,
wedge_labels=[labels, '{frac:.1%}'], wedge_label_distance=[1.1, 0.6])

# bar chart parameters
age_ratios = [.33, .54, .07, .06]
Expand Down
24 changes: 8 additions & 16 deletions galleries/examples/pie_and_polar_charts/pie_and_donut_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
# Now it's time for the pie. Starting with a pie recipe, we create the data
# and a list of labels from it.
#
# We can provide a function to the ``autopct`` argument, which will expand
# automatic percentage labeling by showing absolute values; we calculate
# the latter back from relative data and the known sum of all values.
# We can provide a format string to the *wedge_labels* parameter, to
# automatically label each ingredient's wedge with its weight in grams and
# percentages.
#
# We then create the pie and store the returned objects for later. The first
# returned element of the returned tuple is a list of the wedges. Those are
Expand All @@ -31,32 +31,24 @@
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
fig, ax = plt.subplots(figsize=(6, 3))

recipe = ["375 g flour",
"75 g sugar",
"250 g butter",
"300 g berries"]

data = [float(x.split()[0]) for x in recipe]
data = [int(x.split()[0]) for x in recipe]
ingredients = [x.split()[-1] for x in recipe]


def func(pct, allvals):
absolute = int(np.round(pct/100.*np.sum(allvals)))
return f"{pct:.1f}%\n({absolute:d} g)"


wedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data),
textprops=dict(color="w"))
wedges, texts = ax.pie(data, wedge_labels='{frac:.1%}\n({absval:d}g)',
textprops=dict(color="w", size=8, weight="bold"))

ax.legend(wedges, ingredients,
title="Ingredients",
loc="center left",
bbox_to_anchor=(1, 0, 0.5, 1))

plt.setp(autotexts, size=8, weight="bold")

ax.set_title("Matplotlib bakery: A pie")

plt.show()
Expand Down Expand Up @@ -97,7 +89,7 @@ def func(pct, allvals):

data = [225, 90, 50, 60, 100, 5]

wedges, texts = ax.pie(data, wedgeprops=dict(width=0.5), startangle=-40)
wedges, _ = ax.pie(data, wedgeprops=dict(width=0.5), startangle=-40)

bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
kw = dict(arrowprops=dict(arrowstyle="-"),
Expand Down
75 changes: 50 additions & 25 deletions galleries/examples/pie_and_polar_charts/pie_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,75 @@
# ------------
#
# 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
# addition to the customizations shown here, each wedge can be customized using
# 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%}')


# %%
#
# Multiple labels
# ---------------
#
# Pass both a list of labels and a format string to *wedge_labels*, with a
# corresponding *wedge_label_distance* for each.

fig, ax = plt.subplots()
ax.pie(sizes, wedge_labels=[labels, '{frac:.1%}'], wedge_label_distance=[1.1, 0.6])

# %%
# By default, the label values are obtained from the percent size of the slice.
#
# Color slices
# ------------
#
# Pass a list of colors to *colors* to set the color of each slice.

fig, ax = plt.subplots()
ax.pie(sizes, labels=labels,
ax.pie(sizes, wedge_labels=labels, wedge_label_distance=1.1,
colors=['olivedrab', 'rosybrown', 'gray', 'saddlebrown'])

# %%
Expand All @@ -58,22 +93,10 @@
# 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, wedge_labels=labels, wedge_label_distance=1.1,
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
# ---------------------------------
Expand All @@ -89,8 +112,8 @@
explode = (0, 0.1, 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=[labels, '{frac:.1%}'],
wedge_label_distance=[1.1, 0.6], shadow=True, startangle=90)
plt.show()

# %%
Expand All @@ -107,7 +130,8 @@

fig, ax = plt.subplots()

ax.pie(sizes, labels=labels, autopct='%.0f%%',
ax.pie(sizes, wedge_labels=[labels, '{frac:.1%}'],
wedge_label_distance=[1.1, 0.6],
textprops={'size': 'small'}, radius=0.5)
plt.show()

Expand All @@ -119,7 +143,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%%',
ax.pie(sizes, explode=explode, wedge_labels=[labels, '{frac:.1%}'],
wedge_label_distance=[1.1, 0.6],
shadow={'ox': -0.04, 'edgecolor': 'none', 'shade': 0.9}, startangle=90)
plt.show()

Expand Down
14 changes: 12 additions & 2 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1429,7 +1429,8 @@ def _add_data_doc(docstring, replace_names):
return docstring.replace(' DATA_PARAMETER_PLACEHOLDER', data_doc)


def _preprocess_data(func=None, *, replace_names=None, label_namer=None):
def _preprocess_data(func=None, *, replace_names=None, replace_names_multi=None,
label_namer=None):
"""
A decorator to add a 'data' kwarg to a function.

Expand All @@ -1454,6 +1455,11 @@ def func(ax, *args, **kwargs): ...
replace_names : list of str or None, default: None
The list of parameter names for which lookup into *data* should be
attempted. If None, replacement is attempted for all arguments.
replace_names_multi : list of str or None, default: None
As for *replace_names*, but if a sequence is passed, a lookup into *data*
will be attempted for each element of the sequence. Currently only
supported for parameters named in the function signature (not those passed via
*args or **kwargs).
label_namer : str, default: None
If set e.g. to "namer" (which must be a kwarg in the function's
signature -- not as ``**kwargs``), if the *namer* argument passed in is
Expand All @@ -1471,7 +1477,8 @@ def func(foo, label=None): ...
if func is None: # Return the actual decorator.
return functools.partial(
_preprocess_data,
replace_names=replace_names, label_namer=label_namer)
replace_names=replace_names, replace_names_multi=replace_names_multi,
label_namer=label_namer)

sig = inspect.signature(func)
varargs_name = None
Expand Down Expand Up @@ -1523,6 +1530,9 @@ def inner(ax, *args, data=None, **kwargs):
else:
if replace_names is None or k in replace_names:
bound.arguments[k] = _replacer(data, v)
if (replace_names_multi is not None and k in replace_names_multi and
not cbook.is_scalar_or_string(v)):
bound.arguments[k] = [_replacer(data, vi) for vi in v]

new_args = bound.args
new_kwargs = bound.kwargs
Expand Down
Loading
Loading