Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Add support for (sub-) panel labels to Axes#15771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
There's already |
lschr commentedNov 25, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I tried that in the beginning, but could find no way to align with the y axis label. Theoretically, one could apply the logic I implemented to the left title, but then the left title would be treated completely differently from the center and right titles. I would much rather prefer to add this new “panel label” (there may be a better name for it) to make it clear what it is intended for and that it will behave differently from the titles. Edit:
|
seems fair. |
I like the idea here. However I’d like to see a little more survey done on whether this is best practice. In particular I’m used to the a, b, c being inside the axes frame more often than not. I or aligned with the frame as@anntzer suggests. I find your examples above to be placed in a way that’s hard to read and looks strange to me so I’d like some argument that this is a good default. |
I am a biophysics guy and virtually all I ever see is letters in the very top left corner outside the frame.
The list is endless. However, this may be different in different fields of science. Maybe |
Agreed this is consistent with how Nature and other journals do things. I am 95% convinced this is a reasonable idea. I think we may need to think about the "align" parameter and how to make it more consistent w/ the rest of matplotlib's anchoring paradigm. I'm not super comfortable with that paradigm personally, and it may not do what you need here, but maybe others should chime in:@ImportanceOfBeingErnest ?@timhoffm ? Please do ping us if you do not get a lot of comments. This seems reasonable overall, so I think it should get some attention. |
I totally see how this feature is useful. While I personally never had problems putting those labels where needed, either using It is definitely true that there is no canonical place for those labels (even for Journals, there is in my experience no standarized position, every one wants them at a different place it seems), and hence a solution needs to be somehow flexible - but that necessarily also makes it complicated. Concerning the currently proposed solution I would mention that it would be nice if those labels do not slow down axes drawtime in case they are not set. Calculating a position for something that is not even shown and creating transform(s) for it seems unnecessary, and drawing the axes is pretty slow already. Also, the interplay of this current propasal with layout managers is not too clear to me. In general I do wonder if a flexible enough solution using |
timhoffm commentedDec 1, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I don't have time to look into this thoroughly at the moment. So just some thought, not a full review:
|
I am a bit concerned about adding the API surface for this. I would rather see this sort of thing as a well-scoped helper function than as a new method. Seehttps://gist.github.com/tacaswell/9643166 for a worked example of a (possibly too naive) function that does something similar (defaults to inside the frame) with |
Such a helper could be a method offigure rather than axes. It’s a bit annoying having to call this for each subplot, if the labeling is the same. |
I think that should be easy enough, like adding a check whether there is any text and whether it is
I'll have a look at that. What would be the advantage over setting the |
I basically copied the API and implementation from the axis labels.
I agree that this is not optimal. There are
There are reasons not to call For convenience however, an optional argument could be added to
I was not able to come up with such a thing, but matplotlib internals are quite new to me. The only solution I found was to modify |
It is quite easy to use |
It would be easy to add a method to defset_panellabels(labels,axs=None,**kwargs):ifaxsisNone:axs=self.get_axes()forax,labinzip(axs,labels):ax.set_panellabel(lab,**kwargs) |
https://matplotlib.org/tutorials/text/annotations.html#using-complex-coordinates-with-annotations has some examples of how to anchor annotations against other artists. I see the benefit of aligning the panel labels with a variety of other things, but in the scope of one axes and between the different axes, however that also brings a tremendous amount of complexity in how to specify it.... |
@tacaswell Thanks for the hint. I guess this is what@ImportanceOfBeingErnest meant by setting the bbox at draw time. As time permits I will give it a try. |
This still seems like a good idea in general, but the implementation can likely be both simplified and made more robust, pushing to 3.4.0. |
I have had no chance to work on this as I have spent most of my time writing my PhD thesis. But as soon as the thesis is finished (quite soon, I hope) I will come back to this. |
In scientific publications, sub-panels are often enumerated (a, b, c,…), identifying them for reference in the figure legend or main text.This adds support for such labels, plus various options for alignment.
Use Annotation instead of Text (makes padding easy), pass_get_panellabel_bbox (calculate bounding box for anchoring) method asxycoords argument.
Replace SubplotSpec.get_rows_columns by rowspan and colspan.
lschr commentedMay 18, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I reimplemented the code that calculates the label positions according to the suggestions by@ImportanceOfBeingErnest and@tacaswell (I think), which indeed seems like a nice improvement. |
lschr commentedMay 18, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Since there were some proponents of not putting this into the Axes class, I also tried something different. I derived a PanelLabel class from Annotation classPanelLabel(mtext.Annotation):def__init__(self,label,fontdict=None,h_align='axislabel',v_align='title',pad=0.,**kwargs):default= {'fontsize':rcParams['axes.panellabelsize'],'fontweight':rcParams['axes.panellabelweight'],'verticalalignment':'baseline','horizontalalignment':'left'}ifisinstance(pad,Number):pad= (pad,)*2super().__init__(label, (0.0,1.0),pad,xycoords=self._get_bbox,textcoords="offset points")self.update(default)iffontdictisnotNone:self.update(fontdict)self.update(kwargs)self._align= (h_align,v_align)self._align_x_grp= {self}self._align_y_grp= {self}self.set_clip_on(False)def_get_bbox(self,renderer):# Something very similar to Axes._get_panellabel_bbox ... which can be used like fig,ax=plt.subplots()pl=ax.add_artist(PanelLabel("a")) Furthermore, defalign_panellabels(pls):forplinpls:pl._align_x_grp=set()pl._align_y_grp=set()ss=pl.axes.get_subplotspec()row0=ss.rowspan.startcol0=ss.colspan.startforplcinpls:ssc=plc.axes.get_subplotspec()ifssc.colspan.start==col0:pl._align_x_grp.add(plc)ifssc.rowspan.start==row0:pl._align_y_grp.add(plc) allows for alignment of labels using align_panellabels(list_of_labels) which could probably be improved to take a list of Axes as its parameter (and then find the PanelLabel child in each Axes) and further to take a Figure as its parameter (where it could go through all associated Axes). I think, this would be a nice solution, although not really consistent with the rest of the Axes API, where pretty much everything is done via |
@timhoffm@tacaswell@ImportanceOfBeingErnest@jklymak Any feedback or suggestions on how to proceed? |
Sorry this fell off everyone's radar. I think this is still a reasonable idea. I was thinking of this having the same level as figure. i.e. |
anntzer commentedMay 17, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I'll close this because the feature request is being tracked at#20182, and also because I think a strategy using annotate() to get fixed offsets relative to axes corners (#25905, or the same example before that PR) would be much simpler than using groupers for alignment. Feel free to ping for reopen if you disagree. |
PR Summary
In scientific publications, sub-panels are often enumerated (a, b, c, …), identifying them for reference in the figure legend or main text.
This adds support for such labels, plus various options for alignment.
By default, labels are aligned with the left edge of the y axis label and with the baseline of the title.
Example:
However, alignment can be changed:
It is also possible to align the labels of multiple sub-panels, even if their titles and/or axis labels are not aligned:
Default font size and font weight are controlled by the
axes.panellabelsize
andaxes.panellabelweight
rcParams, respectively.Before I start polishing documentation, writing unit tests, etc. I would love some feedback!
PR Checklist