13

I am trying to fix the axes to scientific notation of two different sets of data where one is [1-9]x1e-3 and the other is [1-9]x1e-4. I would like to set both axes to be 10^-4 and have the one digits after decimal (e.g. %.1e). Here is a simple version that I have tried to play around with: I would like the numbers on the axes to be at least 1 and I want both powers to be the same.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(1,9,9)
y1 = x*10**(-4)
y2 = x*10**(-3)

fig, ax = plt.subplots(2,1,sharex=True)

ax[0].plot(x,y1)
ax[0].ticklabel_format(axis='y', style='sci', scilimits=(-4,-4))
ax[0].yaxis.major.formatter._useMathText = True
ax[1].plot(x,y2)
ax[1].ticklabel_format(axis='y', style='sci', scilimits=(-4,-4))
ax[1].yaxis.major.formatter._useMathText = True

plt.show()

enter image description here

Gregory
  • 341
  • 1
  • 2
  • 10

1 Answers1

19

You can subclass matplotlib.ticker.ScalarFormatter and fix the orderOfMagnitude attribute to the number you like (in this case -4).
In the same way you can fix the format to be used.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker

class OOMFormatter(matplotlib.ticker.ScalarFormatter):
    def __init__(self, order=0, fformat="%1.1f", offset=True, mathText=True):
        self.oom = order
        self.fformat = fformat
        matplotlib.ticker.ScalarFormatter.__init__(self,useOffset=offset,useMathText=mathText)
    def _set_order_of_magnitude(self):
        self.orderOfMagnitude = self.oom
    def _set_format(self, vmin=None, vmax=None):
        self.format = self.fformat
        if self._useMathText:
            self.format = r'$\mathdefault{%s}$' % self.format


x = np.linspace(1,9,9)
y1 = x*10**(-4)
y2 = x*10**(-3)

fig, ax = plt.subplots(2,1,sharex=True)

ax[0].plot(x,y1)
ax[1].plot(x,y2)

for axe in ax:
    axe.yaxis.set_major_formatter(OOMFormatter(-4, "%1.1f"))
    axe.ticklabel_format(axis='y', style='sci', scilimits=(-4,-4))

plt.show()

While this may seem complicated at first sight the only thing it really does is overwrite the private methods _set_orderOfMagnitude and _set_format and thereby prevent them from doing some sophisticated stuff in the background that we don't want. Because in the end, all we need is that, independent of what happens internally, self.orderOfMagnitude is always -4 and self.format is always "%1.1f".

enter image description here

Note: In matplotlib < 3.1 the class needed to look like

class OOMFormatter(matplotlib.ticker.ScalarFormatter):
        def __init__(self, order=0, fformat="%1.1f", offset=True, mathText=True):
            self.oom = order
            self.fformat = fformat
            matplotlib.ticker.ScalarFormatter.__init__(self,useOffset=offset,useMathText=mathText)
        def _set_orderOfMagnitude(self, nothing=None):
            self.orderOfMagnitude = self.oom
        def _set_format(self, vmin=None, vmax=None):
            self.format = self.fformat
            if self._useMathText:
                self.format = '$%s$' % matplotlib.ticker._mathdefault(self.format)
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • This works! I just was wondering if you could explain what each definition in the class is doing and does it need to be expanded upon to fix the number of sig figs, for example if I want one after the decimal (e.g. 1.835 e-3 -> 18.3 e-4) @ImportanceOfBeingErnest – Gregory Mar 07 '17 at 21:21
  • Thanks! Accepted the answer – Gregory Mar 07 '17 at 22:26
  • late I know, but this function does not work if matplotlib version is <2.0.0. Is there a work around? I don't have the ability to install a later version of matplotlib on the computer I ssh into. – Gregory Apr 04 '17 at 21:44
  • Fair enough. The error is the following: self.format = '$%s$' % ticker._mathdefault(self.format) AttributeError: 'module' object has no attribute '_mathdefault' – Gregory Apr 04 '17 at 21:59
  • Try to replace that line with `self.format = '$%s$' % self.format`. It could be that this mathdefault isn't even necessary. – ImportanceOfBeingErnest Apr 04 '17 at 22:12
  • Awesome! Thanks again, really appreciate you coming back to look at it. Cheers – Gregory Apr 04 '17 at 22:18
  • Hi. What happens if I want to fix the order of magnitude for an offset? My data has points like `12.001, 12.002` so the plot adds a `+1.2e1`. How do I make that a simpler 12? – Enzo Dec 13 '18 at 16:25
  • 1
    in matplotlib 3.1.1 it looks like the helper signatures changed slightly, one changes name to snake case and neither takes args, so `def _set_order_of_magnitude(self): ...` and `def _set_format(self): ...` – patricksurry Jan 30 '20 at 22:36