Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Legend label size not mutable by rcparams #3

Closed
bradyrx opened this issue Jan 10, 2019 · 7 comments
Closed

Legend label size not mutable by rcparams #3

bradyrx opened this issue Jan 10, 2019 · 7 comments
Labels

Comments

@bradyrx
Copy link
Collaborator

bradyrx commented Jan 10, 2019

Calling ax.format(rc_kw={'legend.fontsize': int}) does not affect the font size of labels within the legend, and in fact overrides 'axes.labelsize', affecting the size of tick labels. However, other rcparams for the legend (e.g., legend.handlelength) do work. Because of this, I anticipate this is a proplot issue and not a matplotlib issue.

Sample code:

import numpy as np
import proplot as plot

def _set_aeshetics(ax):
    rc_kw = {'axes.labelsize': 8,
             'legend.fontsize': 20,
             'legend.handlelength': 6,
    }
    ax.format(rc_kw=rc_kw)

f, ax = plot.subplots(axwidth=6, aspect=5, bottomlegend=True)
x = np.random.rand(100,)
p = ax.plot(x, label='signal')
set_aeshetics(ax)
f.bottompanel.legend([p])

Note that 'legend.fontsize' doesn't affect the font size of labels within the legend, but actually blows up the tick labels to huge sizes. However, 'legend.handlelength' will stretch out the handle graphics.


Also, something internal to proplot blocks outside calls from managing this, e.g.:

plt.legend([p], prop={'size': 24})   
@lukelbd
Copy link
Collaborator

lukelbd commented Jan 11, 2019

Thanks for the report. I'll look into that legend issue specifically, you should definitely be able to override the settings manually.

As for your first issue: in the rcmod.py file, there is a dictionary called rcGlobals_children:

rcGlobals_children = {
   # Important ones
   'xcolor':     [],
    'ycolor':     [],
    'color':      ['axes.labelcolor', 'axes.edgecolor', 'axes.hatchcolor', 'map.color', 'map.hatchcolor', 'xtick.color', 'ytick.color'], # change the 'color' of an axes
    'facecolor':  ['axes.facecolor', 'map.facecolor'], # simple alias
    'facehatch':  ['axes.facehatch', 'map.facehatch'], # optionally apply background hatching
    'small':      ['font.size', 'xtick.labelsize', 'ytick.labelsize', 'axes.labelsize', 'legend.fontsize'], # the 'small' fonts
    'large':      ['abc.fontsize', 'figure.titlesize', 'axes.titlesize'], # the 'large' fonts
    'linewidth':  ['axes.linewidth', 'map.linewidth', 'hatch.linewidth', 'axes.hatchlw',
                   'map.hatchlw', 'xtick.major.width', 'ytick.major.width'], # gridline widths same as tick widths
                   # 'grid.linewidth', # should not be coupled, looks ugly
    'gridalpha':  ['grid.alpha',     'gridminor.alpha'],
    'gridcolor':  ['grid.color',     'gridminor.color'],
    'gridstyle':  ['grid.linestyle', 'gridminor.linestyle'],
    # Aliases
    'margin':         ['axes.xmargin', 'axes.ymargin'],
    'xmargin':        ['axes.xmargin'], # found I wanted to change these *a lot*
    'ymargin':        ['axes.ymargin'], # found I wanted to change these *a lot*
    'fontname':       ['font.family'], # specify family directly, so we can easily switch between serif/sans-serif; requires text.usetex = False; see below
    'abcweight':      ['abc.weight'],
    'titleweight':    ['axes.titleweight'],
    'suptitleweight': ['figure.titleweight'],
    # Less important ones
    'bottom':     ['xtick.major.bottom',  'xtick.minor.bottom'], # major and minor ticks should always be in the same place
    'top':        ['xtick.major.top',     'xtick.minor.top'],
    'left':       ['ytick.major.left',    'ytick.minor.left'],
    'right':      ['ytick.major.right',   'ytick.minor.right'],
    'ticklen' :   ['xtick.major.size',    'ytick.major.size'],
    'tickdir':    ['xtick.direction',     'ytick.direction'],
    'tickpad':    ['xtick.major.pad', 'xtick.minor.pad', 'ytick.major.pad', 'ytick.minor.pad'],
    }

The basic idea here is, the user could change a global property (the dictionary keys) to change any of the constituent ("children") properties all at once. This includes the key "small" for axis labels, tick labels, legend text, colorbar label text, and text manually added with ax.text(), and the key "large" for axes titles, "super" figure titles, and the "abc" labelling. (Note a few items in those dictionary lists, like abc.fontsize, are new rc items I made up, stored not in mpl.rcParams but in a separate custom dictionary.) This is based on my (possibly biased) opinion of which font sizes in a figure "should" be about the same.

The problem is, whenever you change a setting in one of these "property families", all the other settings get changed too. Since in your example dictionary, legend.fontsize comes after axes.labelsize, it's the one that gets applied to the entire family.

I think I'll disable this behavior, and make the "property syncing" only possible when the user accesses those global keywords "small", "large", etc. I suppose it is pretty unexpected behavior when you're just starting to use this repo (exhibit A: this issue).

Note the small and large sizes can be changed a number of ways: proplot.rc.small = <num>, proplot.rc['small'] = <num>, ax.format(small=<num>), or ax.format(rc_kw={'small':num}), and same for "large".

@bradyrx
Copy link
Collaborator Author

bradyrx commented Jan 14, 2019

@lukelbd Thanks for thinking about this. I've always found rcParams to be powerful but inconsistent. The best method for me has been to make my own style file, but then it's sort of a fixed style.

I like the set_aesthetics method, since it can just be called once. Do you have any other suggestions for how to go about doing this, or do you agree that a quick function call works well?

@bradyrx
Copy link
Collaborator Author

bradyrx commented Jan 16, 2019

@lukelbd, I gave the global approach a shot and still 'small' doesn't affect the legend.fontsize feature. It seems like a larger lock on legend mutability for some reason.


no formatting change
screen shot 2019-01-15 at 6 05 44 pm

ax.format(rc_kw={'small': 14})
screen shot 2019-01-15 at 6 04 57 pm

@lukelbd
Copy link
Collaborator

lukelbd commented Jan 16, 2019

Thanks for finding this. This was indeed a bug; latest push (27ea3dd) should fix it. Two important things in that commit:

  1. The function _get_alias was deleted. This was the thing that changed all the "sibling" properties whenever a child of a "global" property was set -- i.e., legend.fontsize affecting axes.labelsize.
  2. I deleted some experimental stuff with _setitem_mode, that I forgot was still in there. This was causing the bug you last mentioned.

This example demos the new (fixed) behavior:

import proplot as plot
import numpy as np
plot.nbsetup()
for size in (4,12):
    # plot.rc.small = size
    plot.rc['legend.fontsize'] = size
    f, ax = plot.subplots()
    for i in range(6):
        ax.plot(np.random.rand(10), label=f'line {i}')
    ax.legend()

@bradyrx
Copy link
Collaborator Author

bradyrx commented Jan 16, 2019

Great, thanks for that fix! This should work fine for now. Two other oddities to point your attention to:

  1. One has to run the plot.rc['legend.fontsize'] prior to calling the legend for it to take affect, while other rcParams are indifferent to this order. Perhaps it is just good practice anyways to do the rcParams first thing in a figure.
import proplot as plot
import numpy as np
plot.nbsetup()
x = np.random.rand(100,)
f, ax = plot.subplots(nrows=2, axwidth=6, aspect=5)
ax[0].plot(x, label='data')
ax[0].legend()
ax.format(rc_kw={'small': 14})
ax[1].plot(x, label='data')
ax[1].legend()
  1. Not all y tick labels seem to pick up on rcParam edits (see below figure rendered from above code). I've noticed this from time to time while making plots. More commonly it's just the upper end (like in the top plot), but for some reason a portion of y ticks remain small.

screen shot 2019-01-15 at 10 00 06 pm

@lukelbd
Copy link
Collaborator

lukelbd commented Jan 16, 2019

No problem!

On the first point: Yep, you're right. The rc settings should be applied before drawing the legend. Currently, format() only updates settings related to axes, axes components (e.g. spines, ticks, tick labels), and figures (e.g. the super title) after instantiation. Default settings related to objects -- e.g. lines, scatter plots, legends, etc. -- are left alone. It probably wouldn't make sense to implement, since updating font sizes after a legend is instantiated would probably mess up the alignment of legend rows/columns.

On the second point: That is another bug. I had been applying properties by iterating through the text objects generated by ax.<x|y>axis.get_ticklabels(). But I think new text objects are created when the axis is auto-scaled after plotting stuff. Now (7031858) I use axis.set_tick_params() for updating the label color/size, and only use axis.get_ticklabels() to update the font name (for some reason, it's impossible to set text properties other than color and size with axis.set_tick_params()). Note this means if you want to change the font name after instantiation, you will get the same bug as above (half the tick labels have different fonts)... but that's a pretty weird use case.

@bradyrx
Copy link
Collaborator Author

bradyrx commented Jan 16, 2019

Fixed it on my end! Thanks. Will keep you posted on other issues; doing plenty of plotting lately so giving you some rigorous testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants