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?

Infinite Recursion
  • 6,511
  • 28
  • 39
  • 51
orome
  • 45,163
  • 57
  • 202
  • 418
  • 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`. –  Oct 30 '17 at 07:00

1 Answers1

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))))
Community
  • 1
  • 1
tacaswell
  • 84,579
  • 22
  • 210
  • 199
  • 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)))`. – Joe Kington Dec 19 '13 at 22:19
  • 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`. – orome Dec 19 '13 at 22:23
  • @JoeKington huh, interesting. I discovered `LogFormatterExponent` while answering this question. You know the reason for that behavior for negatives? – tacaswell Dec 19 '13 at 22:29
  • @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. – Joe Kington Dec 19 '13 at 23:01
  • That sounds reasonable to me. I suspect no-one uses this code path, the formatter classes are not well publicized. – tacaswell Dec 19 '13 at 23:18
  • 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. – orome Dec 20 '13 at 00:24
  • FuncFormatter which lets you change the number to a string any way you want. – tacaswell Dec 20 '13 at 00:26
  • Thanks, I see the edit. Accepted. Should I be noticing that this seems slower than my original hack? – orome Dec 20 '13 at 13:38
  • How much slower? Don't know the exact code paths but I work would not guess there should be a huge difference. – tacaswell Dec 20 '13 at 14:50