5

I am currently generating different figures with a scientific notation for the y-axis leading to ticks like 2 or 6 on some plots, but 2.5 or 8.9 on some others. I would like to always have ticks with one decimal on the y-axis, even if it adds a zero. Here is an example

import matplotlib.pyplot as plt
import numpy as np

plt.plot(np.arange(1, 10), np.arange(1, 10)**5)
ax = plt.gca()
plt.ticklabel_format(axis='y', style='sci')
ax.yaxis.major.formatter.set_powerlimits((0,0))
plt.show()

Figure generated

What could I add to force to plot ticks 1.0, 2.0, etc. on the y-axis?

Mage d'Icare
  • 133
  • 1
  • 6

3 Answers3

5

The ScalarFormatter does not currently support custom formats for the ticks, such as setting numbers of decimals. However you can extend the class, so to force it to use a format that you specify. Here is an example:

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

class ScalarFormatterForceFormat(ScalarFormatter):
    def _set_format(self):  # Override function that finds format to use.
        self.format = "%1.1f"  # Give format here

plt.plot(np.arange(1, 10), np.arange(1, 10)**5)
ax = plt.gca()
yfmt = ScalarFormatterForceFormat()
yfmt.set_powerlimits((0,0))
gca().yaxis.set_major_formatter(yfmt)
plt.show()

Here is how it will look.

enter image description here

J. P. Petersen
  • 4,871
  • 4
  • 33
  • 33
  • 4
    I acutally needed to overload _set_format(self, vmin, vmax). Maybe Matplotlib 2? But I managed to make it work for me. Thanks! – Mage d'Icare Feb 10 '17 at 10:37
3

Since I did not manage to make the other answers work, probably because I am using subplots and axis, I create a function that simply goes around the problem of using the command plt.ticklabel_format(axis='y', style='sci').

The function takes the thicklabels of the y axis and converts the values to scientific notation. It calculates the value and the sign of the exponent and then modifies the plot accordingly, using ax.annotate to place the exponent a the top left corner. The function takes as the arguments the ax that needs to be modified, so the figure needs to be declared with an axis (ax), and the number decimal digits to be displayed. The only unknown is the textsize of the annotate, which may be automatized as well. I added a couple of different plots to show the case with negative numbers or decimals.

import matplotlib.pyplot as plt
import numpy as np


def scientific_notation_y(ax, decimals=1):
    v = np.asarray(ax.get_yticks().tolist())
    v_abs = np.abs(v)
    v_max = np.max(v_abs)
    exp = 0

    if v_max >= 10:
        sign = '+'
        while v_max >= 10:
            exp = exp + 1
            v_max = v_max / 10
        v = v / 10**exp
    elif v_max <= 1:
        sign = '-'
        while v_max <= 1:
            exp = exp + 1
            v_max = v_max * 10
        v = v * 10**exp
    v = np.around(v, decimals)
    ax.annotate(r'1e' + sign + str(exp), xycoords='axes fraction',
                xy=(0, 0), xytext=(0, 1.01), size=14)
    ax.set_yticklabels(v)
    return

fig, ax = plt.subplots()
ax.plot(np.arange(0, 10), np.arange(0, 10)**5)
scientific_notation_y(ax, 1)
plt.show()

fig, ax = plt.subplots()
ax.plot(np.arange(0, 10), -np.arange(0, 10)**5)
scientific_notation_y(ax, 1)
plt.show()

fig, ax = plt.subplots()
ax.plot(np.arange(0, 10), -np.arange(0, 10)/100)
scientific_notation_y(ax, 1)
plt.show()

enter image description here

Matteo
  • 93
  • 8
0

You can get the ticks and format it like you want.

plt.plot(np.arange(1, 10), np.arange(1, 10)**5)
ax = plt.gca()
plt.ticklabel_format(axis='y', style='sci')
ax.yaxis.major.formatter.set_powerlimits((0,0))

xx, locs = plt.xticks()
ll = ['%.1f' % a for a in xx]
plt.xticks(xx, ll)
plt.show()

formating ticks

Lucas
  • 6,869
  • 5
  • 29
  • 44