36

I am plotting some big numbers with matplotlib in a pyqt program using python 2.7. I have a y-axis that ranges from 1e+18 to 3e+18 (usually). I'd like to see each tick mark show values in scientific notation and with 2 decimal places. For example 2.35e+18 instead of just 2e+18 because values between 2e+18 and 3e+18 still read just 2e+18 for a few tickmarks. Here is an example of that problem.

import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
x = np.linspace(0, 300, 20)
y = np.linspace(0,300, 20)
y = y*1e16
ax.plot(x,y)  
ax.get_xaxis().set_major_formatter(plt.LogFormatter(10,  labelOnlyBase=False))
ax.get_yaxis().set_major_formatter(plt.LogFormatter(10,  labelOnlyBase=False))
plt.show()
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
tempneff
  • 365
  • 1
  • 3
  • 4
  • 1
    Similar: http://stackoverflow.com/questions/11577665/change-x-axes-scale-in-matplotlib – 0 _ Apr 21 '15 at 22:17
  • Also similar: https://stackoverflow.com/questions/28371674/prevent-scientific-notation-in-matplotlib-pyplot – cardamom May 05 '20 at 21:37

3 Answers3

68

This is really easy to do if you use the matplotlib.ticker.FormatStrFormatter as opposed to the LogFormatter. The following code will label everything with the format '%.2e':

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

fig = plt.figure()

ax = fig.add_subplot(111)

x = np.linspace(0, 300, 20)

y = np.linspace(0,300, 20)
y = y*1e16

ax.plot(x,y)

ax.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2e'))

plt.show()

Example plot

Ffisegydd
  • 51,807
  • 15
  • 147
  • 125
21

In order to get nicely formatted labels in scientific notation one may use the formatting capabilities of a ScalarFormatter which uses MathText (Latex) and apply it to the labels.

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

fig, ax = plt.subplots()

x = np.linspace(0, 300, 20)
y = np.linspace(0,300, 20)
y = y*1e16

ax.plot(x,y)

f = mticker.ScalarFormatter(useOffset=False, useMathText=True)
g = lambda x,pos : "${}$".format(f._formatSciNotation('%1.10e' % x))
plt.gca().yaxis.set_major_formatter(mticker.FuncFormatter(g))

plt.show()

enter image description here

While this may be useful in a lot of cases, it does not actually meet the requirements of the question. To have equal digits on all labels a more customized version can be used.

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

fig, ax = plt.subplots()

x = np.linspace(0, 300, 20)
y = np.linspace(0,300, 20)
y = y*1e16

ax.plot(x,y)

class MathTextSciFormatter(mticker.Formatter):
    def __init__(self, fmt="%1.2e"):
        self.fmt = fmt
    def __call__(self, x, pos=None):
        s = self.fmt % x
        decimal_point = '.'
        positive_sign = '+'
        tup = s.split('e')
        significand = tup[0].rstrip(decimal_point)
        sign = tup[1][0].replace(positive_sign, '')
        exponent = tup[1][1:].lstrip('0')
        if exponent:
            exponent = '10^{%s%s}' % (sign, exponent)
        if significand and exponent:
            s =  r'%s{\times}%s' % (significand, exponent)
        else:
            s =  r'%s%s' % (significand, exponent)
        return "${}$".format(s)

# Format with 2 decimal places
plt.gca().yaxis.set_major_formatter(MathTextSciFormatter("%1.2e"))

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
1

Since the recent reinstallation of the computer, the ScalarFormatter and _formatSciNotation method described in @ImportanceOfBeingEmest's no longer work. I receive the following error message.

g = lambda x,pos : "${}$".format(f._formatSciNotation('%1.10e' % x))
AttributeError: 'ScalarFormatter' object has no attribute '_formatSciNotation'

Following the suggestion of the following answer, I achieved my demand using LogFormatterSciNotation. https://stackoverflow.com/a/74403178/2494226

Example:

 from matplotlib.ticker import LogFormatterSciNotation
 plt.gca().set_xticks([1E2,3E2,1E3,3E3,1E4,3E4,1E5])
 plt.gca().xaxis.set_major_formatter(LogFormatterSciNotation(base=10,minor_thresholds=(10,10)))
HD189733b
  • 144
  • 7