Offset secondary axis in matplotlib - python

I'm trying to bring together to different plot settings in matplotlib. I found nice examples for each of them in the matplotlib example gallery/documentation and stack but I couldn't find anything on my specific problem.
So what I know so far is, how to add one or more axes with offset y-axis for plotting different data with respect to the same x-axis, by using ax.twinx(). The third y-axis is called parasite axis in the example Parasite axis demo. However, if you want to add an additional axis which is just a scaled version of the existing one, you can use ax.secondary_yaxis(), as shown in the Secondary axis demo. There is no additional data to be plotted.
What I could not achieve so far is a secondary y-axis which is offset from the original one. This can be very helpful to make plots more readable across scientific communities. For instance, while some scientists use frequency as reference for the electromagnetic spectrum, others use the wavelength or the wavenumber. Afsar [1] used a very convenient axis labeling which includes all the three variables in the same plot:
I would like to the something similar, just on the y-axis instead of the x-axis. Is there a way to offset the secondary axis from the primary axis? I tried a few parameters but couldn't figure it out.
Thank you for any help!
[1] Afsar, Mohammed Nurul. “Precision Millimeter-Wave Measurements of Complex Refractive Index, Complex Dielectric Permittivity, and Loss Tangent of Common Polymers.” IEEE Transactions on Instrumentation and Measurement IM–36, no. 2 (June 1987): 530–36. https://doi.org/10.1109/TIM.1987.6312733.
[1]:

A complete example. The third-to-last line is the relevant one.
import matplotlib.pyplot as plt
import numpy as np
import datetime
dates = [datetime.datetime(2018, 1, 1) + datetime.timedelta(hours=k * 6)
for k in range(240)]
temperature = np.random.randn(len(dates)) * 4 + 6.7
fig, ax = plt.subplots(constrained_layout=True)
ax.plot(dates, temperature)
ax.set_ylabel(r'$T\ [^oC]$')
plt.xticks(rotation=70)
def date2yday(x):
"""Convert matplotlib datenum to days since 2018-01-01."""
y = x - mdates.date2num(datetime.datetime(2018, 1, 1))
return y
def yday2date(x):
"""Return a matplotlib datenum for *x* days after 2018-01-01."""
y = x + mdates.date2num(datetime.datetime(2018, 1, 1))
return y
secax_x = ax.secondary_xaxis('top', functions=(date2yday, yday2date))
secax_x.set_xlabel('yday [2018]')
def celsius_to_fahrenheit(x):
return x * 1.8 + 32
def fahrenheit_to_celsius(x):
return (x - 32) / 1.8
secax_y = ax.secondary_yaxis(
'right', functions=(celsius_to_fahrenheit, fahrenheit_to_celsius))
secax_y.set_ylabel(r'$T\ [^oF]$')
def celsius_to_anomaly(x):
return (x - np.mean(temperature))
def anomaly_to_celsius(x):
return (x + np.mean(temperature))
# document use of a float for the position:
secax_y2 = ax.secondary_yaxis(
1.2, functions=(celsius_to_anomaly, anomaly_to_celsius))
secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$')
plt.show()

Here is another approach, although maybe it's more of a hack:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
#FuncFormatter
def twin1_formatter(x, pos):
return f'{x/np.pi*180:.0f}'
#FuncFormatter
def twin2_formatter(x, pos):
return f'{x/np.pi:.1f} $\pi$'
data = np.arange(0, 2*np.pi, 0.1)
fig, ax = plt.subplots()
twin1 = ax.twiny()
twin1.spines['top'].set_position(('axes', 1.2))
twin1.set_xlabel('Degrees')
twin1.xaxis.set_major_formatter(FuncFormatter(twin1_formatter))
twin2 = ax.twiny()
twin2.set_xlabel('Pies')
twin2.xaxis.set_major_formatter(FuncFormatter(twin2_formatter))
twin2.xaxis.set_ticks(np.array([0, 1/2, 1, 3/2, 2])*np.pi)
ax.plot(data, np.sin(data))
ax.set_xlabel('Radians')
twin1.set_xlim(ax.get_xlim())
twin2.set_xlim(ax.get_xlim())
fig.show()

Related

Matplotlib + pandas change xtick label frequency when using period[Q-DEC] [duplicate]

I am trying to fix how python plots my data.
Say:
x = [0,5,9,10,15]
y = [0,1,2,3,4]
matplotlib.pyplot.plot(x,y)
matplotlib.pyplot.show()
The x axis' ticks are plotted in intervals of 5. Is there a way to make it show intervals of 1?
You could explicitly set where you want to tick marks with plt.xticks:
plt.xticks(np.arange(min(x), max(x)+1, 1.0))
For example,
import numpy as np
import matplotlib.pyplot as plt
x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(np.arange(min(x), max(x)+1, 1.0))
plt.show()
(np.arange was used rather than Python's range function just in case min(x) and max(x) are floats instead of ints.)
The plt.plot (or ax.plot) function will automatically set default x and y limits. If you wish to keep those limits, and just change the stepsize of the tick marks, then you could use ax.get_xlim() to discover what limits Matplotlib has already set.
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, stepsize))
The default tick formatter should do a decent job rounding the tick values to a sensible number of significant digits. However, if you wish to have more control over the format, you can define your own formatter. For example,
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
Here's a runnable example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, 0.712123))
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
plt.show()
Another approach is to set the axis locator:
import matplotlib.ticker as plticker
loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)
There are several different types of locator depending upon your needs.
Here is a full example:
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)
plt.show()
I like this solution (from the Matplotlib Plotting Cookbook):
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = [0,5,9,10,15]
y = [0,1,2,3,4]
tick_spacing = 1
fig, ax = plt.subplots(1,1)
ax.plot(x,y)
ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
plt.show()
This solution give you explicit control of the tick spacing via the number given to ticker.MultipleLocater(), allows automatic limit determination, and is easy to read later.
In case anyone is interested in a general one-liner, simply get the current ticks and use it to set the new ticks by sampling every other tick.
ax.set_xticks(ax.get_xticks()[::2])
if you just want to set the spacing a simple one liner with minimal boilerplate:
plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1))
also works easily for minor ticks:
plt.gca().xaxis.set_minor_locator(plt.MultipleLocator(1))
a bit of a mouthfull, but pretty compact
This is a bit hacky, but by far the cleanest/easiest to understand example that I've found to do this. It's from an answer on SO here:
Cleanest way to hide every nth tick label in matplotlib colorbar?
for label in ax.get_xticklabels()[::2]:
label.set_visible(False)
Then you can loop over the labels setting them to visible or not depending on the density you want.
edit: note that sometimes matplotlib sets labels == '', so it might look like a label is not present, when in fact it is and just isn't displaying anything. To make sure you're looping through actual visible labels, you could try:
visible_labels = [lab for lab in ax.get_xticklabels() if lab.get_visible() is True and lab.get_text() != '']
plt.setp(visible_labels[::2], visible=False)
This is an old topic, but I stumble over this every now and then and made this function. It's very convenient:
import matplotlib.pyplot as pp
import numpy as np
def resadjust(ax, xres=None, yres=None):
"""
Send in an axis and I fix the resolution as desired.
"""
if xres:
start, stop = ax.get_xlim()
ticks = np.arange(start, stop + xres, xres)
ax.set_xticks(ticks)
if yres:
start, stop = ax.get_ylim()
ticks = np.arange(start, stop + yres, yres)
ax.set_yticks(ticks)
One caveat of controlling the ticks like this is that one does no longer enjoy the interactive automagic updating of max scale after an added line. Then do
gca().set_ylim(top=new_top) # for example
and run the resadjust function again.
I developed an inelegant solution. Consider that we have the X axis and also a list of labels for each point in X.
Example:
import matplotlib.pyplot as plt
x = [0,1,2,3,4,5]
y = [10,20,15,18,7,19]
xlabels = ['jan','feb','mar','apr','may','jun']
Let's say that I want to show ticks labels only for 'feb' and 'jun'
xlabelsnew = []
for i in xlabels:
if i not in ['feb','jun']:
i = ' '
xlabelsnew.append(i)
else:
xlabelsnew.append(i)
Good, now we have a fake list of labels. First, we plotted the original version.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabels,rotation=45)
plt.show()
Now, the modified version.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabelsnew,rotation=45)
plt.show()
Pure Python Implementation
Below's a pure python implementation of the desired functionality that handles any numeric series (int or float) with positive, negative, or mixed values and allows for the user to specify the desired step size:
import math
def computeTicks (x, step = 5):
"""
Computes domain with given step encompassing series x
# params
x - Required - A list-like object of integers or floats
step - Optional - Tick frequency
"""
xMax, xMin = math.ceil(max(x)), math.floor(min(x))
dMax, dMin = xMax + abs((xMax % step) - step) + (step if (xMax % step != 0) else 0), xMin - abs((xMin % step))
return range(dMin, dMax, step)
Sample Output
# Negative to Positive
series = [-2, 18, 24, 29, 43]
print(list(computeTicks(series)))
[-5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45]
# Negative to 0
series = [-30, -14, -10, -9, -3, 0]
print(list(computeTicks(series)))
[-30, -25, -20, -15, -10, -5, 0]
# 0 to Positive
series = [19, 23, 24, 27]
print(list(computeTicks(series)))
[15, 20, 25, 30]
# Floats
series = [1.8, 12.0, 21.2]
print(list(computeTicks(series)))
[0, 5, 10, 15, 20, 25]
# Step – 100
series = [118.3, 293.2, 768.1]
print(list(computeTicks(series, step = 100)))
[100, 200, 300, 400, 500, 600, 700, 800]
Sample Usage
import matplotlib.pyplot as plt
x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(computeTicks(x))
plt.show()
Notice the x-axis has integer values all evenly spaced by 5, whereas the y-axis has a different interval (the matplotlib default behavior, because the ticks weren't specified).
Generalisable one liner, with only Numpy imported:
ax.set_xticks(np.arange(min(x),max(x),1))
Set in the context of the question:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = [0,5,9,10,15]
y = [0,1,2,3,4]
ax.plot(x,y)
ax.set_xticks(np.arange(min(x),max(x),1))
plt.show()
How it works:
fig, ax = plt.subplots() gives the ax object which contains the axes.
np.arange(min(x),max(x),1) gives an array of interval 1 from the min of x to the max of x. This is the new x ticks that we want.
ax.set_xticks() changes the ticks on the ax object.
xmarks=[i for i in range(1,length+1,1)]
plt.xticks(xmarks)
This worked for me
if you want ticks between [1,5] (1 and 5 inclusive) then replace
length = 5
Since None of the above solutions worked for my usecase, here I provide a solution using None (pun!) which can be adapted to a wide variety of scenarios.
Here is a sample piece of code that produces cluttered ticks on both X and Y axes.
# Note the super cluttered ticks on both X and Y axis.
# inputs
x = np.arange(1, 101)
y = x * np.log(x)
fig = plt.figure() # create figure
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.set_xticks(x) # set xtick values
ax.set_yticks(y) # set ytick values
plt.show()
Now, we clean up the clutter with a new plot that shows only a sparse set of values on both x and y axes as ticks.
# inputs
x = np.arange(1, 101)
y = x * np.log(x)
fig = plt.figure() # create figure
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.set_xticks(x)
ax.set_yticks(y)
# which values need to be shown?
# here, we show every third value from `x` and `y`
show_every = 3
sparse_xticks = [None] * x.shape[0]
sparse_xticks[::show_every] = x[::show_every]
sparse_yticks = [None] * y.shape[0]
sparse_yticks[::show_every] = y[::show_every]
ax.set_xticklabels(sparse_xticks, fontsize=6) # set sparse xtick values
ax.set_yticklabels(sparse_yticks, fontsize=6) # set sparse ytick values
plt.show()
Depending on the usecase, one can adapt the above code simply by changing show_every and using that for sampling tick values for X or Y or both the axes.
If this stepsize based solution doesn't fit, then one can also populate the values of sparse_xticks or sparse_yticks at irregular intervals, if that is what is desired.
You can loop through labels and show or hide those you want:
for i, label in enumerate(ax.get_xticklabels()):
if i % interval != 0:
label.set_visible(False)

Polar coordinates, datetimes

I am trying to plot some time dependent data in a polar coordinates system. The problem is that I got such an output that I have no clue where to start: times, locators, … not “correctly” drawn.
I need some major ticks each 2 hours, minor tick every hour, and a day label in correspondence with a day transition. In normal coordinates it seems ok but when switching to polar it seems more complicated and this is very confusing. I miss smt but don’t know what.
I have tried with
p_locator = mpolar.ThetaLocator(mdates.AutoDateLocator(minticks=24, maxticks=24))
p_formatter = mpolar.ThetaFormatter()
or with
p_locator = mdates.AutoDateLocator()
p_formatter = mdates.DateFormatter("%H:%M")
but no success. I think I missed how matplotlib works internally with datetimeobjects. Not just the ticks, locator and co. are "wrong" but the data don't even fit the full circle (-> should I apply a scale transformation?)
I would really appreciate some help to understand the mechanism behind it.
Update the axis with
ax.set_xticks(time_ticks)
ax.xaxis.set_major_formatter(p_formatter)
ax.xaxis.set_major_locator(p_locator)
Here a code sample
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
import matplotlib.projections.polar as mpolar
import matplotlib.ticker as mticker
import datetime
import random
N_MAJOR_TICKS = 12 # amount of major ticks
IS_MULTI_DAYS = True
def random_daily_hours(n_times, base_date=datetime.datetime.today()): # ordered list of day-hours
secs = sorted([random.randint(0, 24*60*60 - 1) for _ in range(n_times)])
return [base_date + datetime.timedelta(seconds=s) for s in secs]
def uniform_daily_hours(n, base_date=datetime.datetime.today()): # return floats! matplolib format!!!
return [base_date + datetime.timedelta(days=1)*i/n for i in range(n)]
# time ticks
time_ticks = uniform_daily_hours(N_MAJOR_TICKS)
# random values
random.seed(10) # fixing random state for reproducibility
x = random_daily_hours(15)
y = [random.random() for _ in range(len(x))]
# multi days - append a consecutive day
if IS_MULTI_DAYS:
day = datetime.timedelta(days=1)
x += random_daily_hours(15, base_date=datetime.datetime.today() + 1 * day)
y = [random.random() for _ in range(len(x))]
ax = plt.subplot(projection='polar')
# fix scale & orientation
ax.set_rticks([0.5, 1, 1.5, 2]) # set values radial ticks
ax.set_rlabel_position(120) # set location radial scale
ax.set_theta_zero_location('N') # set polar reference direction
ax.set_theta_direction(-1) # set default orientation - clockwise
# attempt 1
ax.set_xticks(np.linspace(0, 2 * np.pi, N_MAJOR_TICKS, endpoint=False))
ax.set_xticklabels(time_ticks)
ax.plot(x, y, '-')
ax.grid(True)
plt.show()

Is there a way to properly invert the y-axis in Python's 3DAxes plot_surface without it interfering with the x-axis?

I'm using 3DAxes in Python to plot a surface plot. Im trying to manipulate the y-axis to go from [25 - 0] instead of going from [0-25] in the plot itself. So far I've tried to just use ax.invert_yaxis(), but my problem is that this changes the x-axis to have the same numerical values as the y-axis. My suspiscion is that this is because I use np.meshgrid earlier in the code.
Does anyone know how to invert just the y-axis?
I have this:
I want to change so the y-axis goes from 25 to 0, and naturally the surfaceplot will change as well then.
But when I use invert_yaxis I get this:
Which is wrong cause the x-axis is changed in numerically values as well...
CODE:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
Tcm_0 = 17.9 #K
def t(T):
return T/Tcm_0
my_0 = 1.25663706212e-6
Hcm_0 = 29.7 / my_0 #A/m
def Hc2(T):
return Hcm_0 * (1 - np.power(t(T),1.52))
def h(H, T):
return H/Hc2(T)
C_1 = 350e3 #AT/mm^2
def Jc(H, T):
return (C_1/(my_0*H))*(1 - np.power(t(T),1.52))*(1 - np.power(t(T),2))*np.power(h(H,T),0.5)*np.power(1-h(H,T),2)
fig = plt.figure()
ax = plt.axes(projection='3d')
T = np.linspace(0,17,100)
B = np.linspace(0.1,28,100)
H = B/my_0
T, H = np.meshgrid(T,H)
J = Jc(H,T)
B = H*my_0
surface = ax.plot_surface(T,B,J, cmap='winter', linewidth=0)
ax.set_xbound(0, Tcm_0)
ax.set_xlabel('Temperature [K]')
ax.set_ybound(0, Hcm_0*my_0)
ax.invert_yaxis() <- This us what I dont get to work.
plt.show()
You can do it by manually setting the limits for the axis, no need to invert anything.
ax.set_ylim(25, 0)

Generate a heatmap in MatPlotLib using a scatter data set

My question is almost exactly similar to this one. However, I'm not satisfied with the answers, because I want to generate an actual heatmap, without explicitely binning the data.
To be precise, I would like to display the function that is the result of a convolution between the scatter data and a custom kernel, such as 1/x^2.
How should I implement this with matplotlib?
EDIT: Basically, what I have done is this. The result is here. I'd like to keep everything, the axis, the title, the labels and so on. Basically just change the plot to be like I described, while re-implementing as little as possible.
Convert your time series data into a numeric format with matplotlib.dats.date2num. Lay down a rectangular grid that spans your x and y ranges and do your convolution on that plot. Make a pseudo-color plot of your convolution and then reformat the x labels to be dates.
The label formatting is a little messy, but reasonably well documented. You just need to replace AutoDateFormatter with DateFormatter and an appropriate formatting string.
You'll need to tweak the constants in the convolution for your data.
import numpy as np
import datetime as dt
import pylab as plt
import matplotlib.dates as dates
t0 = dt.date.today()
t1 = t0+dt.timedelta(days=10)
times = np.linspace(dates.date2num(t0), dates.date2num(t1), 10)
dt = times[-1]-times[0]
price = 100 - (times-times.mean())**2
dp = price.max() - price.min()
volume = np.linspace(1, 100, 10)
tgrid = np.linspace(times.min(), times.max(), 100)
pgrid = np.linspace(70, 110, 100)
tgrid, pgrid = np.meshgrid(tgrid, pgrid)
heat = np.zeros_like(tgrid)
for t,p,v in zip(times, price, volume):
delt = (t-tgrid)**2
delp = (p-pgrid)**2
heat += v/( delt + delp*1.e-2 + 5.e-1 )**2
fig = plt.figure()
ax = fig.add_subplot(111)
ax.pcolormesh(tgrid, pgrid, heat, cmap='gist_heat_r')
plt.scatter(times, price, volume, marker='x')
locator = dates.DayLocator()
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(dates.AutoDateFormatter(locator))
fig.autofmt_xdate()
plt.show()

matplotlib: format axis offset-values to whole numbers or specific number

I have a matplotlib figure which I am plotting data that is always referred to as nanoseconds (1e-9). On the y-axis, if I have data that is tens of nanoseconds, ie. 44e-9, the value on the axis shows as 4.4 with a +1e-8 as an offset. Is there anyway to force the axis to show 44 with a +1e-9 offset?
The same goes for my x-axis where the axis is showing +5.54478e4, where I would rather it show an offset of +55447 (whole number, no decimal - the value here is in days).
I've tried a couple things like this:
p = axes.plot(x,y)
p.ticklabel_format(style='plain')
for the x-axis, but this doesn't work, though I'm probably using it incorrectly or misinterpreting something from the docs, can someone point me in the correct direction?
Thanks,
Jonathan
I tried doing something with formatters but haven't found any solution yet...:
myyfmt = ScalarFormatter(useOffset=True)
myyfmt._set_offset(1e9)
axes.get_yaxis().set_major_formatter(myyfmt)
and
myxfmt = ScalarFormatter(useOffset=True)
myxfmt.set_portlimits((-9,5))
axes.get_xaxis().set_major_formatter(myxfmt)
On a side note, I'm actually confused as to where the 'offset number' object actually resides...is it part of the major/minor ticks?
I had exactly the same problem, and these lines fixed the problem:
from matplotlib.ticker import ScalarFormatter
y_formatter = ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
A much easier solution is to simply customize the tick labels. Take this example:
from pylab import *
# Generate some random data...
x = linspace(55478, 55486, 100)
y = random(100) - 0.5
y = cumsum(y)
y -= y.min()
y *= 1e-8
# plot
plot(x,y)
# xticks
locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs))
# ytikcs
locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
ylabel('microseconds (1E-9)')
show()
Notice how in the y-axis case, I multiplied the values by 1e9 then mentioned that constant in the y-label
EDIT
Another option is to fake the exponent multiplier by manually adding its text to the top of the plot:
locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
text(0.0, 1.01, '1e-9', fontsize=10, transform = gca().transAxes)
EDIT2
Also you can format the x-axis offset value in the same manner:
locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs-min(locs)))
text(0.92, -0.07, "+%g" % min(locs), fontsize=10, transform = gca().transAxes)
You have to subclass ScalarFormatter to do what you need... _set_offset just adds a constant, you want to set ScalarFormatter.orderOfMagnitude. Unfortunately, manually setting orderOfMagnitude won't do anything, as it's reset when the ScalarFormatter instance is called to format the axis tick labels. It shouldn't be this complicated, but I can't find an easier way to do exactly what you want... Here's an example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, FormatStrFormatter
class FixedOrderFormatter(ScalarFormatter):
"""Formats axis ticks using scientific notation with a constant order of
magnitude"""
def __init__(self, order_of_mag=0, useOffset=True, useMathText=False):
self._order_of_mag = order_of_mag
ScalarFormatter.__init__(self, useOffset=useOffset,
useMathText=useMathText)
def _set_orderOfMagnitude(self, range):
"""Over-riding this to avoid having orderOfMagnitude reset elsewhere"""
self.orderOfMagnitude = self._order_of_mag
# Generate some random data...
x = np.linspace(55478, 55486, 100)
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8
# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')
# Force the y-axis ticks to use 1e-9 as a base exponent
ax.yaxis.set_major_formatter(FixedOrderFormatter(-9))
# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FormatStrFormatter('%0.0f'))
plt.show()
Which yields something like:
Whereas, the default formatting would look like:
Hope that helps a bit!
Edit: For what it's worth, I don't know where the offset label resides either... It would be slightly easier to just manually set it, but I couldn't figure out how to do so... I get the feeling that there has to be an easier way than all of this. It works, though!
Similar to Amro's answer, you can use FuncFormatter
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
# Generate some random data...
x = np.linspace(55478, 55486, 100)
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8
# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')
# Force the y-axis ticks to use 1e-9 as a base exponent
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: ('%.1f')%(x*1e9)))
ax.set_ylabel('microseconds (1E-9)')
# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: '%.0f'%x))
plt.show()
As has been pointed out in the comments and in this answer, the offset may be switched off globally, by doing the following:
matplotlib.rcParams['axes.formatter.useoffset'] = False
Gonzalo's solution started working for me after having added set_scientific(False):
ax=gca()
fmt=matplotlib.ticker.ScalarFormatter(useOffset=False)
fmt.set_scientific(False)
ax.xaxis.set_major_formatter(fmt)
I think that a more elegant way is to use the ticker formatter. Here is an example for both xaxis and yaxis:
from pylab import *
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
majorLocator = MultipleLocator(20)
xFormatter = FormatStrFormatter('%d')
yFormatter = FormatStrFormatter('%.2f')
minorLocator = MultipleLocator(5)
t = arange(0.0, 100.0, 0.1)
s = sin(0.1*pi*t)*exp(-t*0.01)
ax = subplot(111)
plot(t,s)
ax.xaxis.set_major_locator(majorLocator)
ax.xaxis.set_major_formatter(xFormatter)
ax.yaxis.set_major_formatter(yFormatter)
#for the minor ticks, use no labels; default NullFormatter
ax.xaxis.set_minor_locator(minorLocator)
For the second part, without manually resetting all the ticks again, this was my solution:
class CustomScalarFormatter(ScalarFormatter):
def format_data(self, value):
if self._useLocale:
s = locale.format_string('%1.2g', (value,))
else:
s = '%1.2g' % value
s = self._formatSciNotation(s)
return self.fix_minus(s)
xmajorformatter = CustomScalarFormatter() # default useOffset=True
axes.get_xaxis().set_major_formatter(xmajorformatter)
obviously you can set the format string to whatever you want.

Categories