How can i make the plot to show in the x-axis scientific notation but only in multiples of 3? that is: 1e-3, 1e-6, 1e-9, etc.
The following example generates a scale in 1e-4, which is not multiple of 3.
My desire is a scale in either 10^-3, so x-axis [0:0.3] or a scale in 10^-6, so x-axis [0:300]
import numpy as np
import matplotlib.pyplot as plt
# time
T = 30e-6
N = 10
time = np.linspace(0, N*T, num=N)
plt.plot(time,np.sin(time))
ax = plt.gca()
ax.ticklabel_format(style='sci',axis='both',scilimits=(0,0))
You can create your own custom ScalarFormatter and set the order of magnitude value (which give the scale factor). For example,
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
# create a custom for which you can manually set the "order of magnitude"
class CustomScalerFormatter(ScalarFormatter):
def set_locs(self, locs):
# docstring inherited
self.locs = locs
if len(self.locs) > 0:
if self._useOffset:
self._compute_offset()
# comment out this line below from the original implementation,
# so that you can manually set the order of magnitude
#self._set_order_of_magnitude()
self._set_format()
# time
T = 30e-6
N = 10
time = np.linspace(0, N*T, num=N)
plt.plot(time, np.sin(time))
ax = plt.gca()
# create custom formatter for y-axis with 10^-3 scale
yformatter = CustomScalerFormatter()
yformatter.orderOfMagnitude = -3
# create custom formatter for y-axis with 10^-6 scale
xformatter = CustomScalerFormatter()
xformatter.orderOfMagnitude = -6
ax.yaxis.set_major_formatter(yformatter)
ax.xaxis.set_major_formatter(xformatter)

Two different methods can be used, depending on whether you want to keep the factor (order of magnitude) or not
One way could be to define your own formatter function
def myform(x, pos):
e=0
while x>1e3 or x<-1e3:
x/=1000
e+=3
while x>-1 and x<1 and x!=0:
x*=1000
e-=3
if e==0: return f'${x:.1f}$'
return f'${x:.0f}.10^{{{e}}}$'
ax.xaxis.set_major_formatter(myform)
Advantage of this is that you have a total control on how the label is formatted.
Drawback is that you can't have the order of magnitude factor (at least I don't know how to combine it, other that with the second method, but if you use the second method, then, there is no point in still using it). Also, you need to reinvent the wheel (to decide how to display a number)
The way I compute the exponent is of course naive. You may want to replace that with some log10, or the like. But I wanted to make it explicit. Plus, as naive as is it, the computation cost is still negligible before anything related to plotting anyway (it even with very large of very small numbers, it is still just a handful multiplication)
Other method is to still let the control of all that to ScalarFormatter (this is what you are doing now. ScalarFormatter is the default one in your case). But subsume the way it decides the order of magnitude factor
# Just a subclass of ScalarFormatter that does almost nothing different
class MyForm(ticker.ScalarFormatter):
def __init__(self, *arg, **kw):
ticker.ScalarFormatter.__init__(self, *arg, **kw)
# I "hardcode" in it the two options you set with `style=sci` and `scilimits`
self.set_scientific(True)
self.set_powerlimits((0,0))
# And the only thing that this scalarformatter does differently as the parent one is the way it chooses the order of magnitude
def _set_order_of_magnitude(self):
# Rather than rewriting one, I rely on the original one
super()._set_order_of_magnitude()
# But after it has done its computation (leading to -4 in your example)
# I force it to choose one that is multiple of 3, my removing the remainder
self.orderOfMagnitude -= self.orderOfMagnitude%3
ax.yaxis.set_major_formatter(MyForm())
Advantage is that you don't reinvent the wheel. That is the same scalar formatter you were using. Drawback is the one of not reinventing the wheel generally speaking: you are stuck with the choices made by those who invented it (for example, see how in the first version, I used latex to have nice power notation).
Following picture demonstrates it on your example, using the first one on x-axis and second one on yaxis

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
def myform(x, pos):
e=0
while x>1e3 or x<-1e3:
x/=1000
e+=3
while x>-1 and x<1 and x!=0:
x*=1000
e-=3
if e==0: return f'${x:.1f}$'
return f'${x:.0f}.10^{{{e}}}$'
class MyForm(ticker.ScalarFormatter):
def __init__(self, *args, **kw):
ticker.ScalarFormatter.__init__(self, *args, **kw)
self.set_scientific(True)
self.set_powerlimits((0,0))
#
def _set_order_of_magnitude(self):
super()._set_order_of_magnitude()
self.orderOfMagnitude -= self.orderOfMagnitude%3
T = 30e-6
N = 10
time = np.linspace(0, N*T, num=N)
plt.plot(time,np.sin(time))
ax = plt.gca()
ax.xaxis.set_major_formatter(myform)
ax.yaxis.set_major_formatter(MyForm())
plt.show()
(ok, the choice of myform for the function name and MyForm for the class name is not very good. But anyway, you are not expected to use both. And I am sure you'll come up with better names)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With