Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e5db08a

Browse filesBrowse files
committed
ENH: add option to label pie charts with absolute values
1 parent 183b04f commit e5db08a
Copy full SHA for e5db08a

File tree

8 files changed

+132
-30
lines changed
Filter options

8 files changed

+132
-30
lines changed
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The *pctdistance* parameter of ``Axes.pie``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
... is deprecated. Please use *autodistance* instead.
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Label pie charts with absolute input values
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Pie charts may now be automatically labelled with the original input values
5+
using the new *absolutefmt* parameter in `~.Axes.pie`.
6+
7+
.. plot::
8+
:include-source: true
9+
:alt: Pie chart with each wedge labelled with its input value
10+
11+
import matplotlib.pyplot as plt
12+
13+
x = [4, 2, 1]
14+
15+
fig, ax = plt.subplots()
16+
ax.pie(x, absolutefmt='%d')
17+
18+
plt.show()

‎galleries/examples/pie_and_polar_charts/pie_and_donut_labels.py

Copy file name to clipboardExpand all lines: galleries/examples/pie_and_polar_charts/pie_and_donut_labels.py
+6-6Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,17 @@
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]
43+
total = np.sum(data)
4344

4445

45-
def func(pct, allvals):
46-
absolute = int(np.round(pct/100.*np.sum(allvals)))
47-
return f"{pct:.1f}%\n({absolute:d} g)"
46+
def func(value):
47+
fraction = value / total
48+
return f"{fraction:.1%}\n({value:d} g)"
4849

4950

50-
wedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data),
51-
textprops=dict(color="w"))
51+
wedges, texts, autotexts = ax.pie(data, absolutefmt=func, textprops=dict(color="w"))
5252

5353
ax.legend(wedges, ingredients,
5454
title="Ingredients",

‎galleries/examples/pie_and_polar_charts/pie_features.py

Copy file name to clipboardExpand all lines: galleries/examples/pie_and_polar_charts/pie_features.py
+11-5Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,19 @@
3434
# Auto-label slices
3535
# -----------------
3636
#
37-
# Pass a function or format string to *autopct* to label slices.
37+
# Pass a function or format string to *absolutefmt* to label slices with your
38+
# input values...
39+
40+
fig, ax = plt.subplots()
41+
ax.pie(sizes, labels=labels, absolutefmt='%d')
42+
43+
# %%
44+
# ...or to *autopct* to label slices with the percent sizes of the slice.
3845

3946
fig, ax = plt.subplots()
4047
ax.pie(sizes, labels=labels, autopct='%1.1f%%')
4148

4249
# %%
43-
# By default, the label values are obtained from the percent size of the slice.
4450
#
4551
# Color slices
4652
# ------------
@@ -63,15 +69,15 @@
6369
# %%
6470
# Swap label and autopct text positions
6571
# -------------------------------------
66-
# Use the *labeldistance* and *pctdistance* parameters to position the *labels*
72+
# Use the *labeldistance* and *autodistance* parameters to position the *labels*
6773
# and *autopct* text respectively.
6874

6975
fig, ax = plt.subplots()
7076
ax.pie(sizes, labels=labels, autopct='%1.1f%%',
71-
pctdistance=1.25, labeldistance=.6)
77+
autodistance=1.25, labeldistance=.6)
7278

7379
# %%
74-
# *labeldistance* and *pctdistance* are ratios of the radius; therefore they
80+
# *labeldistance* and *autodistance* are ratios of the radius; therefore they
7581
# vary between ``0`` for the center of the pie and ``1`` for the edge of the
7682
# pie, and can be set to greater than ``1`` to place text outside the pie.
7783
#

‎lib/matplotlib/axes/_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_axes.py
+53-15Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3154,13 +3154,13 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0,
31543154
self.add_container(stem_container)
31553155
return stem_container
31563156

3157-
@_api.make_keyword_only("3.9", "explode")
3157+
@_api.rename_parameter("3.11", "pctdistance", "autodistance")
31583158
@_preprocess_data(replace_names=["x", "explode", "labels", "colors"])
3159-
def pie(self, x, explode=None, labels=None, colors=None,
3160-
autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1,
3159+
def pie(self, x, *, explode=None, labels=None, colors=None, absolutefmt=None,
3160+
autopct=None, autodistance=0.6, shadow=False, labeldistance=1.1,
31613161
startangle=0, radius=1, counterclock=True,
31623162
wedgeprops=None, textprops=None, center=(0, 0),
3163-
frame=False, rotatelabels=False, *, normalize=True, hatch=None):
3163+
frame=False, rotatelabels=False, normalize=True, hatch=None):
31643164
"""
31653165
Plot a pie chart.
31663166
@@ -3193,18 +3193,38 @@ def pie(self, x, explode=None, labels=None, colors=None,
31933193
31943194
.. versionadded:: 3.7
31953195
3196+
absolutefmt : None or str or callable, default: None
3197+
If not *None*, *absolutefmt* is a string or function used to label
3198+
the wedges with their original numeric values. The label will be
3199+
placed according to *autodistance*. If *absolutefmt* is a format
3200+
string, the label will be ``fmt % number``. If *absolutefmt* is a
3201+
function, then it will be called. Cannot be used at the same time
3202+
as *autopct*.
3203+
3204+
.. versionadded:: 3.11
3205+
31963206
autopct : None or str or callable, default: None
31973207
If not *None*, *autopct* is a string or function used to label the
3198-
wedges with their numeric value. The label will be placed inside
3199-
the wedge. If *autopct* is a format string, the label will be
3200-
``fmt % pct``. If *autopct* is a function, then it will be called.
3208+
wedges with their percentage values. The label will be placed
3209+
according to *autodistance*. If *autopct* is a format string, the
3210+
label will be ``fmt % pct``. If *autopct* is a function, then it
3211+
will be called. Cannot be used at the same time as *absolutefmt*.
3212+
3213+
autodistance : float, default: 0.6
3214+
The relative distance along the radius at which the text
3215+
generated by *absolutefmt* or *autopct* is drawn. To draw the text
3216+
outside the pie, set *autodistance* > 1. This parameter is ignored
3217+
if both *absolutefmt* and *autopct* are ``None``.
32013218
32023219
pctdistance : float, default: 0.6
32033220
The relative distance along the radius at which the text
32043221
generated by *autopct* is drawn. To draw the text outside the pie,
32053222
set *pctdistance* > 1. This parameter is ignored if *autopct* is
32063223
``None``.
32073224
3225+
.. deprecated:: 3.11
3226+
Use *autodistance* instead.
3227+
32083228
labeldistance : float or None, default: 1.1
32093229
The relative distance along the radius at which the labels are
32103230
drawn. To draw the labels inside the pie, set *labeldistance* < 1.
@@ -3275,9 +3295,7 @@ def pie(self, x, explode=None, labels=None, colors=None,
32753295
The Axes aspect ratio can be controlled with `.Axes.set_aspect`.
32763296
"""
32773297
self.set_aspect('equal')
3278-
# The use of float32 is "historical", but can't be changed without
3279-
# regenerating the test baselines.
3280-
x = np.asarray(x, np.float32)
3298+
x = np.asarray(x)
32813299
if x.ndim > 1:
32823300
raise ValueError("x must be 1D")
32833301

@@ -3287,9 +3305,11 @@ def pie(self, x, explode=None, labels=None, colors=None,
32873305
sx = x.sum()
32883306

32893307
if normalize:
3290-
x = x / sx
3308+
fracs = x / sx
32913309
elif sx > 1:
32923310
raise ValueError('Cannot plot an unnormalized pie with sum(x) > 1')
3311+
else:
3312+
fracs = x
32933313
if labels is None:
32943314
labels = [''] * len(x)
32953315
if explode is None:
@@ -3324,7 +3344,7 @@ def get_next_color():
33243344
slices = []
33253345
autotexts = []
33263346

3327-
for frac, label, expl in zip(x, labels, explode):
3347+
for n, frac, label, expl in zip(x, fracs, labels, explode):
33283348
x, y = center
33293349
theta2 = (theta1 + frac) if counterclock else (theta1 - frac)
33303350
thetam = 2 * np.pi * 0.5 * (theta1 + theta2)
@@ -3369,15 +3389,33 @@ def get_next_color():
33693389
texts.append(t)
33703390

33713391
if autopct is not None:
3372-
xt = x + pctdistance * radius * math.cos(thetam)
3373-
yt = y + pctdistance * radius * math.sin(thetam)
3392+
if absolutefmt is not None:
3393+
raise ValueError('Only one of autopct and absolutefmt may be used.')
33743394
if isinstance(autopct, str):
33753395
s = autopct % (100. * frac)
33763396
elif callable(autopct):
33773397
s = autopct(100. * frac)
33783398
else:
33793399
raise TypeError(
33803400
'autopct must be callable or a format string')
3401+
autolabels = True
3402+
3403+
elif absolutefmt is not None:
3404+
if isinstance(absolutefmt, str):
3405+
s = absolutefmt % n
3406+
elif callable(absolutefmt):
3407+
s = absolutefmt(n)
3408+
else:
3409+
raise TypeError(
3410+
'absolutefmt must be callable or a format string')
3411+
autolabels = True
3412+
3413+
else:
3414+
autolabels = False
3415+
3416+
if autolabels:
3417+
xt = x + autodistance * radius * math.cos(thetam)
3418+
yt = y + autodistance * radius * math.sin(thetam)
33813419
if mpl._val_or_rc(textprops.get("usetex"), "text.usetex"):
33823420
# escape % (i.e. \%) if it is not already escaped
33833421
s = re.sub(r"([^\\])%", r"\1\\%", s)
@@ -3397,7 +3435,7 @@ def get_next_color():
33973435
xlim=(-1.25 + center[0], 1.25 + center[0]),
33983436
ylim=(-1.25 + center[1], 1.25 + center[1]))
33993437

3400-
if autopct is None:
3438+
if not autotexts:
34013439
return slices, texts
34023440
else:
34033441
return slices, texts, autotexts

‎lib/matplotlib/axes/_axes.pyi

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_axes.pyi
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,9 @@ class Axes(_AxesBase):
299299
explode: ArrayLike | None = ...,
300300
labels: Sequence[str] | None = ...,
301301
colors: ColorType | Sequence[ColorType] | None = ...,
302+
absolutefmt: str | Callable[[float], str] | None = ...,
302303
autopct: str | Callable[[float], str] | None = ...,
303-
pctdistance: float = ...,
304+
autodistance: float = ...,
304305
shadow: bool = ...,
305306
labeldistance: float | None = ...,
306307
startangle: float = ...,

‎lib/matplotlib/pyplot.py

Copy file name to clipboardExpand all lines: lib/matplotlib/pyplot.py
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3772,11 +3772,13 @@ def phase_spectrum(
37723772
@_copy_docstring_and_deprecators(Axes.pie)
37733773
def pie(
37743774
x: ArrayLike,
3775+
*,
37753776
explode: ArrayLike | None = None,
37763777
labels: Sequence[str] | None = None,
37773778
colors: ColorType | Sequence[ColorType] | None = None,
3779+
absolutefmt: str | Callable[[float], str] | None = None,
37783780
autopct: str | Callable[[float], str] | None = None,
3779-
pctdistance: float = 0.6,
3781+
autodistance: float = 0.6,
37803782
shadow: bool = False,
37813783
labeldistance: float | None = 1.1,
37823784
startangle: float = 0,
@@ -3787,7 +3789,6 @@ def pie(
37873789
center: tuple[float, float] = (0, 0),
37883790
frame: bool = False,
37893791
rotatelabels: bool = False,
3790-
*,
37913792
normalize: bool = True,
37923793
hatch: str | Sequence[str] | None = None,
37933794
data=None,
@@ -3797,8 +3798,9 @@ def pie(
37973798
explode=explode,
37983799
labels=labels,
37993800
colors=colors,
3801+
absolutefmt=absolutefmt,
38003802
autopct=autopct,
3801-
pctdistance=pctdistance,
3803+
autodistance=autodistance,
38023804
shadow=shadow,
38033805
labeldistance=labeldistance,
38043806
startangle=startangle,

‎lib/matplotlib/tests/test_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_axes.py
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5961,6 +5961,39 @@ def test_pie_default():
59615961
autopct='%1.1f%%', shadow=True, startangle=90)
59625962

59635963

5964+
def test_pie_abs():
5965+
labels = ['Frogs', 'Hogs', 'Dogs', 'Logs']
5966+
sizes = [15, 30, 45, 10]
5967+
fig1, ax1 = plt.subplots(figsize=(8, 6))
5968+
slices, texts, autotexts = ax1.pie(
5969+
sizes, labels=labels, absolutefmt='%d')
5970+
5971+
assert [txt.get_text() for txt in texts] == labels
5972+
assert [txt.get_text() for txt in autotexts] == [str(size) for size in sizes]
5973+
5974+
5975+
def test_pie_abs_func():
5976+
labels = ['Frogs', 'Hogs', 'Dogs', 'Logs']
5977+
sizes = [15, 30, 45, 10]
5978+
fig1, ax1 = plt.subplots(figsize=(8, 6))
5979+
5980+
def double(n):
5981+
return n * 2
5982+
5983+
slices, texts, autotexts = ax1.pie(
5984+
sizes, labels=labels, absolutefmt=double)
5985+
5986+
assert [txt.get_text() for txt in texts] == labels
5987+
assert [txt.get_text() for txt in autotexts] == [str(size * 2) for size in sizes]
5988+
5989+
5990+
def test_pie_both_number_formats():
5991+
sizes = [15, 30, 45, 10]
5992+
fig1, ax1 = plt.subplots(figsize=(8, 6))
5993+
with pytest.raises(ValueError, match='Only one of autopct and absolutefmt'):
5994+
ax1.pie(sizes, absolutefmt='%d', autopct='%1.1f%%')
5995+
5996+
59645997
@image_comparison(['pie_linewidth_0', 'pie_linewidth_0', 'pie_linewidth_0'],
59655998
extensions=['png'], style='mpl20', tol=0.01)
59665999
def test_pie_linewidth_0():

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.