X Tutup
Skip to content

ENH: introduce PieContainer and pie_label method#30733

Merged
story645 merged 1 commit intomatplotlib:mainfrom
rcomer:pie_label
Nov 24, 2025
Merged

ENH: introduce PieContainer and pie_label method#30733
story645 merged 1 commit intomatplotlib:mainfrom
rcomer:pie_label

Conversation

@rcomer
Copy link
Member

@rcomer rcomer commented Nov 7, 2025

PR summary

This PR introduces a new pie_label method for adding labels to the wedges of existing pie charts, following discussion in #29152. Similarly to BarContainer, we introduce a PieContainer as the new return value of pie. This contains the Wedge patches and the numbers that pie_label requires for automatically labelling the wedges with their values and/or fractions.

Closes #19338

A few things I’m not sure about:

  • So far I have added very few new tests, since most of the pie_label functionality should be covered by existing pie tests that use the labels and/or autopct parameters. Is is better to duplicate this coverage? Addressed
  • Should PieContainer inherit from Container as I have currently done? It feels a bit wrong to me as I have also overwritten the __iter__ method to provide backwards compatibility on the return value of pie. So we do not get the standard Container behaviour that looping through it gives you the patches one by one. OTOH it is nice to get the remove method for free. Addressed
  • Should pie_label accept a function for the labels parameter, similar to the autopct parameter of pie? Initially I thought no, because I do not know of a use case and the only example I found in the docs was the bakery one, which can now use the format string. However, supporting a function would allow us to move more of the autopct handling into pie_label, and remove the duplicate handling for the "usetex" case.

PR checklist

@github-actions github-actions bot added topic: pyplot API Documentation: examples files in galleries/examples Documentation: API files in lib/ and doc/api labels Nov 7, 2025
rotate : bool, default: False
Rotate each label to the angle of the corresponding slice if true.

alignment : {'center', 'outer', 'auto'}, default: 'auto'
Copy link
Member Author

@rcomer rcomer Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'center' is how the autopct labels of pie are aligned and 'outer' is how the labels of pie are aligned. We had some discussion about the horizontal alignment at #29152 (comment) and #29152 (comment), but I did not notice the extra complication for vertical alignment with rotate until I was writing the example. autopct does not have an associated rotate option, but I can report that rotated labels that were centred horizontally but not centred vertically looked bad!

@rcomer rcomer marked this pull request as ready for review November 7, 2025 19:34
@timhoffm
Copy link
Member

timhoffm commented Nov 8, 2025

  • Should PieContainer inherit from Container as I have currently done?

Container falls short for our needs here. It is basically a tuple of artists with remove() propagation and a CallbackRegistry (which AFAICS we nowhere use). That simple tuple approach does not suit the more general case that we have not just one type sequence of Artists. In fact, only BarContainer really fits in. Already ErrorbarContainer and StemContainer have to work around the limitation using tuples of tuples, which is not a good API.

I think we need to come up with a more generic concept. For a start, I'd say make your own type (i.e. copy the remove method) and define whatever minimal API is reasonable. If we know what kind of API we want to have based on this example it's easier to design a more general ArtistCollection or CompoundArtist.

On a side-note we want this to not only be pure collections, but be a semantic entity, like StepPatch, i.e. there may be (not necessarily from the start) semantic functions, that non-trivially manipulate multiple properties of the artists; e.g. setting the pie values would need to recompute positions of all wedges and texts and update the text content.

Edit: The case is similar to grouped_bar(), where we currently also only have a provisional class with functionality and minimal guarantees.

@timhoffm
Copy link
Member

timhoffm commented Nov 8, 2025

  • So far I have added very few new tests, since most of the pie_label functionality should be covered by existing pie tests that use the labels and/or autopct parameters. Is is better to duplicate this coverage?

If writing from scratch, I would mostly test the functionality on pie_label() directly and only ensure basic propagation from pie() to pie_label(). Given there are already many tests on pie() that is good enough for a start. I would not duplicate but rather move coverage out of pie() tests and into pie_label() tests. But that is a nice-to-have. If done, I'd rather make this a separate cleanup PR.

@rcomer rcomer force-pushed the pie_label branch 2 times, most recently from 867bd4a to bed6636 Compare November 9, 2025 19:53
@rcomer
Copy link
Member Author

rcomer commented Nov 9, 2025

OK, PieContainer is now its own class with its own remove method.

@rcomer rcomer marked this pull request as draft November 10, 2025 10:40
@rcomer
Copy link
Member Author

rcomer commented Nov 10, 2025

For complete backwards compatibility, we need "texts" to be returned as an empty list when labeldistance is None. I think both my current and previous implementations broke that.

In draft for now while I think about how to address that. Suggestions of course welcome!

Edit: this is now addressed by the next push.

@rcomer rcomer marked this pull request as ready for review November 10, 2025 12:37
@rcomer
Copy link
Member Author

rcomer commented Nov 10, 2025

Apologies - just found some doc changes that I had missed before. None of the examples now unpack or index the return value.

super().__init__(lines, **kwargs)


class PieContainer:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure on the name. I'm tempted to call this just Pie on the background, that I envision this will become a compound semantic artist. The "Container" puts more focus on "aggregation of parts".

In that context, I'm pondering whether pie() could/should become essentially

def pie(self, *args, **kwargs):
    pie = Pie(*args, **kwargs)
    self.add_artist(pie)
    return pie

OTOH, full semantics may be more than we can easily achieve; e.g. assume a later pie.set_values(new_values) changes the number of wedges. How does that affect explicitly set lists of labels or wedge properties?

Recommendation: Let's stick with PieContainer for now, but make then name provisional. - It's not needed for the basic use case of

pie = ax.pie(...)
ax.pie_label(pie, ....)

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left some minor comments.

Overall a systematic and easy-to-review PR. Thanks for the effort.🚀

@rcomer
Copy link
Member Author

rcomer commented Nov 11, 2025

Thanks for all your reviewing work @timhoffm. Your latest suggestions seem good to me.

@rcomer
Copy link
Member Author

rcomer commented Nov 18, 2025

It just occurred to me that if someone is using pandas pie, they will not get the PieContainer returned but only the Axes. Should we add the PieContainer to the Axes.containers list to support this case?

@rcomer
Copy link
Member Author

rcomer commented Nov 18, 2025

To be in the containers list we need at least a get_label method. Container takes the get_label and set_label methods from Artist. Artist.set_label calls Artist.pchanged, and I am wondering if that is the reason Container has the unused callback machinery.

@story645
Copy link
Member

To be in the containers list we need at least a get_label method. Container takes the get_label and set_label methods from Artist

Does it make sense to have a container interface/ABC to spell out the membership requirements for the containers list?

@timhoffm
Copy link
Member

timhoffm commented Nov 19, 2025

We need to define what exactly a container is. My understanding from the label machinery is that it is an artist that is realized through multiple base artists, but rather as an implementation detail. ErrorbarContainer uses multiple Line2D, but is logically still a single entity that shows up as one legend entry. I haven’t checked, but suspect that tracking containers explicitly in Axes may be done so that we can identify the artists to label (but it could also be that this was done purely to have a place to attach the involved artists to the figure, since historically we tracked different types of artists like lines, patches, etc. in their own lists and we certainly want to mix the lines of BarContainer with regular standalone lines).

In contrast, Pie is a rather a concerted logical aggregation of multiple artists. A single label and legend entry does not make sense. I therefore believe that Pie is not a container as far as current container design goes.

How to move forward?

I wouldn’t jump extra hoops to support advanced plotting through the pandas plot API. It it’s a high-level API to ease plotting from dataframes, but you can always extract the column data and feed it to matplotlib if needed.

we should specify the concept behind the type of aggregation we do with Pie more clearly. And we need to define how that is represented in internal structures. Is it one artist that can provide multiple legend entries? Or is it rather a grouping where the individual parts are tracked as separate entities in the Axes? Currently, and historically, wedges are added as individual patches. As long as we do this, we can live without adding Pie to the Axes as a minimal starting point. Agreed it would be nice to be able to retrieve it back, but it’s not that we are losing functionality. It’s just a yet to be implemented feature which requires additional design and refactoring, and I propose to rather do this as a follow up.

@story645
Copy link
Member

A single label and legend entry does not make sense. I therefore believe that Pie is not a container as far as current container design goes

BarContainer also supports multiple legend entries and is basically the analogue to PieContainer.

@rcomer
Copy link
Member Author

rcomer commented Nov 19, 2025

OK, I am happy to just drop the latest commit here.

I was thinking that we need to support adding (at least) two sets of labels - once we deprecate labeldistance not None and discourage autopct in the next PR that means at least one set has to be added after the pie is created. However, I was forgetting that pandas will add one of those sets automatically based on what is in the data frame. So pandas could pass on a single set of wedge_labels through **kwargs and then add its automatic ones using the pie_label method.

@rcomer
Copy link
Member Author

rcomer commented Nov 19, 2025

I haven’t checked, but suspect that tracking containers explicitly in Axes may be done so that we can identify the artists to label

I can confirm that if you put something without a get_label method into the Axes.containers list, you get an error in legend.

@rcomer
Copy link
Member Author

rcomer commented Nov 19, 2025

OK, PR is now back to what @timhoffm already approved 😇

@timhoffm
Copy link
Member

timhoffm commented Nov 20, 2025

A single label and legend entry does not make sense. I therefore believe that Pie is not a container as far as current container design goes

BarContainer also supports multiple legend entries and is basically the analogue to PieContainer.

BarContainer dues not reasonably handle labels either. There's some low-level hack in bar() to make this work.

if not isinstance(label, str) and np.iterable(label):
bar_container_label = '_nolegend_'
patch_labels = label
else:
bar_container_label = label
patch_labels = ['_nolegend_'] * len(x)

@story645
Copy link
Member

BarContainer dues not reasonably handle labels either. There's some low-level hack in bar() to make this work.

Looks like it's doing an either use labels on individual artists or container level label. I think it's maybe a questionable API decision to make them mutually exclusive, but I don't think it's a hack.

@timhoffm
Copy link
Member

I think it's maybe a questionable API decision to make them mutually exclusive, but I don't think it's a hack.

Whatever you want to name it. The effect is that responsibility of label handling is distributed between the individual bars and the container. There is no easy way to change between the two approaches afterwards.

But anyway, this is of no concern for Pie, because you don't ever need a single legend entry for the set of all wedges, so Pie itself does not need the label capability.

Copy link
Member

@story645 story645 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit you can take or leave but mostly fun 'cause I learned some new things.

Comment on lines -3691 to -3692
for frac, label, expl in zip(x, labels, explode):
x, y = center
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤨 til that loops have local scoping starting in v3

Copy link
Member Author

@rcomer rcomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @story645

@rcomer rcomer added this to the v3.11.0 milestone Nov 22, 2025
@rcomer
Copy link
Member Author

rcomer commented Nov 24, 2025

Have I missed something I still need to do for this one?

@story645 story645 merged commit 50fea43 into matplotlib:main Nov 24, 2025
48 of 49 checks passed
@story645
Copy link
Member

Thanks @rcomer for the new labeling capabilities!

@rcomer rcomer deleted the pie_label branch November 24, 2025 22:29
andreas16700 added a commit to andreas16700/matplotlib that referenced this pull request Jan 28, 2026
…nd pie_label

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow option to display absolute values for pie chart

3 participants

X Tutup