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

Skip to content

Commit 97fea92

Browse files
committed
ENH: add wedge_labels parameter for pie
1 parent 671c032 commit 97fea92

File tree

12 files changed

+496
-148
lines changed

12 files changed

+496
-148
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Return value of ``pie``
2+
~~~~~~~~~~~~~~~~~~~~~~~
3+
Previously, if no *labels* were passed to `~.Axes.pie`, and *labeldistance* was
4+
not ``None``, empty text labels would be added to the axes and returned. This
5+
is no longer the case. To continue creating empty labels, either pass an empty
6+
string with the new *wedge_labels* parameter ``wedge_labels=''`` or, for
7+
compatibility with older Matplotlib versions, pass an empty string for each wedge
8+
via the *labels* parameter, i.e. ``labels=[''] * number_of_wedges``. Note the
9+
latter option will stop working at Matplotlib 3.16.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
``pie`` *labels* and *labeldistance* parameters
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
Currently the *labels* parameter of `~.Axes.pie` is used both for annotating the
4+
pie wedges directly, and for automatic legend entries. For consistency
5+
with other plotting methods, in future *labels* will only be used for the legend.
6+
7+
The *labeldistance* parameter will therefore default to ``None`` from Matplotlib
8+
3.14, when it will also be deprecated and then removed in Matplotlib 3.16. To
9+
preserve the existing behavior for now, set ``labeldistance=1.1``. For longer
10+
term, use the new *wedge_labels* parameter of `~.Axes.pie` instead of *labels*.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
New *wedge_labels* parameter for pie
2+
------------------------------------
3+
4+
`~.Axes.pie` now accepts a *wedge_labels* parameter which may be used to
5+
annotate the wedges of the pie chart. It can take
6+
7+
* a list of strings, similar to the existing *labels* parameter
8+
* a format string in analogy to the existing *autopct* parameter except that it
9+
uses the `str.format` method, and it can handle absolute values as well as
10+
fractions/percentages
11+
12+
To add multiple labels per wedge, *wedge_labels* can take a sequence of any combination
13+
of the above two options.
14+
15+
*wedge_labels* have accompanying *wedge_label_distance* and *rotate_wedge_labels*
16+
parameters, to customise the position and rotation of the labels.
17+
18+
For examples, see :doc:`/gallery/pie_and_polar_charts/pie_features`.

galleries/examples/misc/svg_filter_pie.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@
2828

2929
# We want to draw the shadow for each pie, but we will not use "shadow"
3030
# option as it doesn't save the references to the shadow patches.
31-
pies = ax.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%')
31+
pies = ax.pie(fracs, explode=explode,
32+
wedge_labels=[labels, '{frac:.1%}'], wedge_label_distance=[1.1, 0.6])
3233

33-
for w in pies[0]:
34+
for w, label in zip(pies[0], labels):
3435
# set the id with the label.
35-
w.set_gid(w.get_label())
36+
w.set_gid(label)
3637

3738
# we don't want to draw the edge of the pie
3839
w.set_edgecolor("none")

galleries/examples/pie_and_polar_charts/bar_of_pie.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
explode = [0.1, 0, 0]
2626
# rotate so that first wedge is split by the x-axis
2727
angle = -180 * overall_ratios[0]
28-
wedges, *_ = ax1.pie(overall_ratios, autopct='%1.1f%%', startangle=angle,
29-
labels=labels, explode=explode)
28+
wedges, *_ = ax1.pie(
29+
overall_ratios, startangle=angle, explode=explode,
30+
wedge_labels=[labels, '{frac:.1%}'], wedge_label_distance=[1.1, 0.6])
3031

3132
# bar chart parameters
3233
age_ratios = [.33, .54, .07, .06]

galleries/examples/pie_and_polar_charts/pie_and_donut_labels.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
# Now it's time for the pie. Starting with a pie recipe, we create the data
1616
# and a list of labels from it.
1717
#
18-
# We can provide a function to the ``autopct`` argument, which will expand
19-
# automatic percentage labeling by showing absolute values; we calculate
20-
# the latter back from relative data and the known sum of all values.
18+
# We can provide a format string to the *wedge_labels* parameter, to
19+
# automatically label each ingredient's wedge with its weight in grams and
20+
# percentages.
2121
#
2222
# We then create the pie and store the returned objects for later. The first
2323
# returned element of the returned tuple is a list of the wedges. Those are
@@ -31,32 +31,24 @@
3131
import matplotlib.pyplot as plt
3232
import numpy as np
3333

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

3636
recipe = ["375 g flour",
3737
"75 g sugar",
3838
"250 g butter",
3939
"300 g berries"]
4040

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

44-
45-
def func(pct, allvals):
46-
absolute = int(np.round(pct/100.*np.sum(allvals)))
47-
return f"{pct:.1f}%\n({absolute:d} g)"
48-
49-
50-
wedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data),
51-
textprops=dict(color="w"))
44+
wedges, texts = ax.pie(data, wedge_labels='{frac:.1%}\n({absval:d}g)',
45+
textprops=dict(color="w", size=8, weight="bold"))
5246

5347
ax.legend(wedges, ingredients,
5448
title="Ingredients",
5549
loc="center left",
5650
bbox_to_anchor=(1, 0, 0.5, 1))
5751

58-
plt.setp(autotexts, size=8, weight="bold")
59-
6052
ax.set_title("Matplotlib bakery: A pie")
6153

6254
plt.show()
@@ -97,7 +89,7 @@ def func(pct, allvals):
9789

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

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

10294
bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
10395
kw = dict(arrowprops=dict(arrowstyle="-"),

galleries/examples/pie_and_polar_charts/pie_features.py

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,75 @@
1515
# ------------
1616
#
1717
# Plot a pie chart of animals and label the slices. To add
18-
# labels, pass a list of labels to the *labels* parameter
18+
# labels, pass a list of labels to the *wedge_labels* parameter.
1919

2020
import matplotlib.pyplot as plt
2121

2222
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
23-
sizes = [15, 30, 45, 10]
23+
sizes = [12, 24, 36, 8]
2424

2525
fig, ax = plt.subplots()
26-
ax.pie(sizes, labels=labels)
26+
ax.pie(sizes, wedge_labels=labels)
2727

2828
# %%
2929
# Each slice of the pie chart is a `.patches.Wedge` object; therefore in
3030
# addition to the customizations shown here, each wedge can be customized using
3131
# the *wedgeprops* argument, as demonstrated in
3232
# :doc:`/gallery/pie_and_polar_charts/nested_pie`.
3333
#
34+
# Controlling label positions
35+
# ---------------------------
36+
# If you want the labels outside the pie, set a *wedge_label_distance* greater than 1.
37+
# This is the distance from the center of the pie as a fraction of its radius.
38+
39+
fig, ax = plt.subplots()
40+
ax.pie(sizes, wedge_labels=labels, wedge_label_distance=1.1)
41+
42+
# %%
43+
#
3444
# Auto-label slices
3545
# -----------------
3646
#
37-
# Pass a function or format string to *autopct* to label slices.
47+
# Pass a format string to *wedge_labels* to label slices with their values
48+
49+
fig, ax = plt.subplots()
50+
ax.pie(sizes, wedge_labels='{absval:.1f}')
51+
52+
# %%
53+
#
54+
# or with their percentages
55+
56+
fig, ax = plt.subplots()
57+
ax.pie(sizes, wedge_labels='{frac:.1%}')
58+
59+
# %%
60+
#
61+
# or both.
3862

3963
fig, ax = plt.subplots()
40-
ax.pie(sizes, labels=labels, autopct='%1.1f%%')
64+
ax.pie(sizes, wedge_labels='{absval:d}\n{frac:.1%}')
65+
66+
67+
# %%
68+
#
69+
# Multiple labels
70+
# ---------------
71+
#
72+
# Pass both a list of labels and a format string to *wedge_labels*, with a
73+
# corresponding *wedge_label_distance* for each.
74+
75+
fig, ax = plt.subplots()
76+
ax.pie(sizes, wedge_labels=[labels, '{frac:.1%}'], wedge_label_distance=[1.1, 0.6])
4177

4278
# %%
43-
# By default, the label values are obtained from the percent size of the slice.
4479
#
4580
# Color slices
4681
# ------------
4782
#
4883
# Pass a list of colors to *colors* to set the color of each slice.
4984

5085
fig, ax = plt.subplots()
51-
ax.pie(sizes, labels=labels,
86+
ax.pie(sizes, wedge_labels=labels, wedge_label_distance=1.1,
5287
colors=['olivedrab', 'rosybrown', 'gray', 'saddlebrown'])
5388

5489
# %%
@@ -58,22 +93,10 @@
5893
# Pass a list of hatch patterns to *hatch* to set the pattern of each slice.
5994

6095
fig, ax = plt.subplots()
61-
ax.pie(sizes, labels=labels, hatch=['**O', 'oO', 'O.O', '.||.'])
62-
63-
# %%
64-
# Swap label and autopct text positions
65-
# -------------------------------------
66-
# Use the *labeldistance* and *pctdistance* parameters to position the *labels*
67-
# and *autopct* text respectively.
68-
69-
fig, ax = plt.subplots()
70-
ax.pie(sizes, labels=labels, autopct='%1.1f%%',
71-
pctdistance=1.25, labeldistance=.6)
96+
ax.pie(sizes, wedge_labels=labels, wedge_label_distance=1.1,
97+
hatch=['**O', 'oO', 'O.O', '.||.'])
7298

7399
# %%
74-
# *labeldistance* and *pctdistance* are ratios of the radius; therefore they
75-
# vary between ``0`` for the center of the pie and ``1`` for the edge of the
76-
# pie, and can be set to greater than ``1`` to place text outside the pie.
77100
#
78101
# Explode, shade, and rotate slices
79102
# ---------------------------------
@@ -89,8 +112,8 @@
89112
explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')
90113

91114
fig, ax = plt.subplots()
92-
ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
93-
shadow=True, startangle=90)
115+
ax.pie(sizes, explode=explode, wedge_labels=[labels, '{frac:.1%}'],
116+
wedge_label_distance=[1.1, 0.6], shadow=True, startangle=90)
94117
plt.show()
95118

96119
# %%
@@ -107,7 +130,8 @@
107130

108131
fig, ax = plt.subplots()
109132

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

@@ -119,7 +143,8 @@
119143
# the `.Shadow` patch. This can be used to modify the default shadow.
120144

121145
fig, ax = plt.subplots()
122-
ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
146+
ax.pie(sizes, explode=explode, wedge_labels=[labels, '{frac:.1%}'],
147+
wedge_label_distance=[1.1, 0.6],
123148
shadow={'ox': -0.04, 'edgecolor': 'none', 'shade': 0.9}, startangle=90)
124149
plt.show()
125150

lib/matplotlib/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,7 +1429,8 @@ def _add_data_doc(docstring, replace_names):
14291429
return docstring.replace(' DATA_PARAMETER_PLACEHOLDER', data_doc)
14301430

14311431

1432-
def _preprocess_data(func=None, *, replace_names=None, label_namer=None):
1432+
def _preprocess_data(func=None, *, replace_names=None, replace_names_multi=None,
1433+
label_namer=None):
14331434
"""
14341435
A decorator to add a 'data' kwarg to a function.
14351436
@@ -1454,6 +1455,11 @@ def func(ax, *args, **kwargs): ...
14541455
replace_names : list of str or None, default: None
14551456
The list of parameter names for which lookup into *data* should be
14561457
attempted. If None, replacement is attempted for all arguments.
1458+
replace_names_multi : list of str or None, default: None
1459+
As for *replace_names*, but if a sequence is passed, a lookup into *data*
1460+
will be attempted for each element of the sequence. Currently only
1461+
supported for parameters named in the function signature (not those passed via
1462+
*args or **kwargs).
14571463
label_namer : str, default: None
14581464
If set e.g. to "namer" (which must be a kwarg in the function's
14591465
signature -- not as ``**kwargs``), if the *namer* argument passed in is
@@ -1471,7 +1477,8 @@ def func(foo, label=None): ...
14711477
if func is None: # Return the actual decorator.
14721478
return functools.partial(
14731479
_preprocess_data,
1474-
replace_names=replace_names, label_namer=label_namer)
1480+
replace_names=replace_names, replace_names_multi=replace_names_multi,
1481+
label_namer=label_namer)
14751482

14761483
sig = inspect.signature(func)
14771484
varargs_name = None
@@ -1523,6 +1530,9 @@ def inner(ax, *args, data=None, **kwargs):
15231530
else:
15241531
if replace_names is None or k in replace_names:
15251532
bound.arguments[k] = _replacer(data, v)
1533+
if (replace_names_multi is not None and k in replace_names_multi and
1534+
not cbook.is_scalar_or_string(v)):
1535+
bound.arguments[k] = [_replacer(data, vi) for vi in v]
15261536

15271537
new_args = bound.args
15281538
new_kwargs = bound.kwargs

0 commit comments

Comments
 (0)