What is the correct way to replace matplotlib tick labels with computed values?
Asked Answered
A

1

10

I have a figure with a log axis

enter image description here

and I would like to relabel the axis ticks with logs of the values, rather than the values themselves

enter image description here

The way I've accomplished this is with

plt.axes().set_xticklabels([math.log10(x) for x in plt.axes().get_xticks()])

but I wonder if there isn't a less convoluted way to do this.

What is the correct idiom for systematically relabeling ticks on matplotlib plots with values computed from the original tick values?

Alliaceous answered 19/12, 2013 at 21:58 Comment(1)
If you don't want to use Formatter, you could just use plt.xticks(x_ticks, x_ticklabels) to put the pre-defined x_ticklabels at each corresponding tick of pre-defined x_ticks.Danelaw
R
9

Look into the Formatter classes. Unless you are putting text on your ticks you should almost never directly use set_xticklabels or set_yticklabels. This completely de-couples your tick labels from you data. If you adjust the view limits, the tick labels will remain the same.

In your case, a formatter already exists for this:

fig, ax = plt.subplots()
ax.loglog(np.logspace(0, 5), np.logspace(0, 5)**2)
ax.xaxis.set_major_formatter(matplotlib.ticker.LogFormatterExponent())

matplotlib.ticker.LogFormatterExponent doc

In general you can use FuncFormatter. For an example of how to use FuncFomatter see matplotlib: change yaxis tick labels which one of many examples floating around SO.

A concise example for what you want, lifting exactly from JoeKington in the comments,:

ax.xaxis.set_major_formatter(
   FuncFormatter(lambda x, pos: '{:0.1f}'.format(log10(x))))
Recitative answered 19/12, 2013 at 22:13 Comment(9)
On a side note, LogFormatterExponent has a few quirks (e.g. switching to scientific notation for negative values). It's a bit nicer to use FuncFormatter in this case. For the OP's reference, FuncFormatter expects a function that takes two arguments: the value and its position. For example: FuncFormatter(lambda x, pos: '{:0.1f}'.format(log10(x))).Overpowering
That formats the negative exponents in scientific notation rather than as integers. Also (more importantly) I'm curious about the idiom for generally relabeling ticks on matplotlib plots with values computed from the original tick values, not just the case where there's a Formatter.Alliaceous
@JoeKington huh, interesting. I discovered LogFormatterExponent while answering this question. You know the reason for that behavior for negatives?Recitative
@tcaswell - I'm not sure. I didn't realize it until after you posted your answer. Just glancing at the code, it looks unintentional... Probably worth considering a bug and fixing (though I guess it technically breaks backward compatibility). I may send in a pull request tonight, if that's alright.Overpowering
That sounds reasonable to me. I suspect no-one uses this code path, the formatter classes are not well publicized.Recitative
Meanwhile: what's the best general approach (where, for example, a Formatter does not exit for the task). I'm happy with answers that don't work in interactive mode too.Alliaceous
FuncFormatter which lets you change the number to a string any way you want.Recitative
Thanks, I see the edit. Accepted. Should I be noticing that this seems slower than my original hack?Alliaceous
How much slower? Don't know the exact code paths but I work would not guess there should be a huge difference.Recitative

© 2022 - 2024 — McMap. All rights reserved.