This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to remove relative shift in matplotlib axis
I'm plotting numbers with five digits (210.10, 210.25, 211.35, etc) against dates and I'd like to have the y-axis ticks show all digits ('214.20' rather than '0.20 + 2.14e2') and have not been able to figure this out. I've attempted to set the ticklabel format to plain, but it appears to have no effect.
plt.ticklabel_format(style='plain', axis='y')
Any hints on the obvious I'm missing?
The axis numbers are defined according to a given Formatter. Unfortunately (AFAIK), matplotlib does not expose a way to control the threshold to go from the numbers to a smaller number + offset. A brute force approach would be setting all the xtick strings:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(100, 100.1, 100)
y = np.arange(100)
fig = plt.figure()
plt.plot(x, y)
plt.show() # original problem
# setting the xticks to have 3 decimal places
xx, locs = plt.xticks()
ll = ['%.3f' % a for a in xx]
plt.xticks(xx, ll)
plt.show()
This is actually the same as setting a FixedFormatter with the strings:
from matplotlib.ticker import FixedFormatter
plt.gca().xaxis.set_major_formatter(FixedFormatter(ll))
However, the problem of this approach is that the labels are fixed. If you want to resize/pan the plot, you have to start over again. A more flexible approach is using the FuncFormatter:
def form3(x, pos):
""" This function returns a string with 3 decimal places, given the input x"""
return '%.3f' % x
from matplotlib.ticker import FuncFormatter
formatter = FuncFormatter(form3)
gca().xaxis.set_major_formatter(FuncFormatter(formatter))
And now you can move the plot and still maintain the same precision. But sometimes this is not ideal. One doesn't always want a fixed precision. One would like to preserve the default Formatter behaviour, just increase the threshold to when it starts adding an offset. There is no exposed mechanism for this, so what I end up doing is to change the source code. It's pretty easy, just change one character in one line in ticker.py. If you look at that github version, it's on line 497:
if np.absolute(ave_oom - range_oom) >= 3: # four sig-figs
I usually change it to:
if np.absolute(ave_oom - range_oom) >= 5: # four sig-figs
and find that it works fine for my uses. Change that file in your matplotlib installation, and then remember to restart python before it takes effect.
You can also just turn the offset off: (almost exact copy of How to remove relative shift in matplotlib axis)
import matlplotlib is plt
plt.plot([1000, 1001, 1002], [1, 2, 3])
plt.gca().get_xaxis().get_major_formatter().set_useOffset(False)
plt.draw()
This grabs the current axes, gets the x-axis axis object and then the major formatter object and sets useOffset to false (doc).
Related
I have a line chart based on a simple list of numbers. By default the x-axis is just the an increment of 1 for each value plotted. I would like to be a percentage instead but can't figure out how. So instead of having an x-axis from 0 to 5, it would go from 0% to 100% (but keeping reasonably spaced tick marks. Code below. Thanks!
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid.axislines import Subplot
data=[8,12,15,17,18,18.5]
fig=plt.figure(1,(7,4))
ax=Subplot(fig,111)
fig.add_subplot(ax)
plt.plot(data)
The code below will give you a simplified x-axis which is percentage based, it assumes that each of your values are spaces equally between 0% and 100%.
It creates a perc array which holds evenly-spaced percentages that can be used to plot with. It then adjusts the formatting for the x-axis so it includes a percentage sign using matplotlib.ticker.FormatStrFormatter. Unfortunately this uses the old-style string formatting, as opposed to the new style, the old style docs can be found here.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mtick
data = [8,12,15,17,18,18.5]
perc = np.linspace(0,100,len(data))
fig = plt.figure(1, (7,4))
ax = fig.add_subplot(1,1,1)
ax.plot(perc, data)
fmt = '%.0f%%' # Format you want the ticks, e.g. '40%'
xticks = mtick.FormatStrFormatter(fmt)
ax.xaxis.set_major_formatter(xticks)
plt.show()
This is a few months late, but I have created PR#6251 with matplotlib to add a new PercentFormatter class. With this class you can do as follows to set the axis:
import matplotlib.ticker as mtick
# Actual plotting code omitted
ax.xaxis.set_major_formatter(mtick.PercentFormatter(5.0))
This will display values from 0 to 5 on a scale of 0% to 100%. The formatter is similar in concept to what #Ffisegydd suggests doing except that it can take any arbitrary existing ticks into account.
PercentFormatter() accepts three arguments, max, decimals, and symbol. max allows you to set the value that corresponds to 100% on the axis (in your example, 5).
The other two parameters allow you to set the number of digits after the decimal point and the symbol. They default to None and '%', respectively. decimals=None will automatically set the number of decimal points based on how much of the axes you are showing.
Note that this formatter will use whatever ticks would normally be generated if you just plotted your data. It does not modify anything besides the strings that are output to the tick marks.
Update
PercentFormatter was accepted into Matplotlib in version 2.1.0.
Totally late in the day, but I wrote this and thought it could be of use:
def transformColToPercents(x, rnd, navalue):
# Returns a pandas series that can be put in a new dataframe column, where all values are scaled from 0-100%
# rnd = round(x)
# navalue = Nan== this
hv = x.max(axis=0)
lv = x.min(axis=0)
pp = pd.Series(((x-lv)*100)/(hv-lv)).round(rnd)
return pp.fillna(navalue)
df['new column'] = transformColToPercents(df['a'], 2, 0)
I am trying to plot a collection of tens of thousands of line segments in a matplotlib interactive plot in a Jupyter notebook. The problem I have is that
the x-values are datetimes (datetime64[ns], basically POSIX timestamps)
LineCollections can only be based on numbers
when leaving the x-axis of the plot to be numbers, when I zoom the plot, the x-axis nicely adjusts in scale to the zoom. However, the x-axis values are uninformative. When formatting the x-axis to informative datetime values, this information is lost when zooming.
Example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import collections as mc
import matplotlib.dates as mdates
%matplotlib nbagg # interactive plot in jupyter notebook
x = np.array([['2018-03-19T07:01:00.000073810', '2018-03-19T07:01:00.632164618'],
['2018-03-19T07:01:00.000073811', '2018-03-19T07:01:00.742295898'],
['2018-03-19T07:01:00.218747698', '2018-03-19T07:01:00.260067814'],
['2018-03-19T07:01:01.218747698', '2018-03-19T07:01:02.260067814'],
['2018-03-19T07:01:02.218747698', '2018-03-19T07:01:02.260067814'],
['2018-03-19T07:01:02.218747698', '2018-03-19T07:01:02.260067814']],
dtype='datetime64[ns]')
y = np.array([[12355.5, 12355.5],
[12363. , 12363. ],
[12362.5, 12362.5],
[12355.5, 12355.5],
[12363. , 12363. ],
[12362.5, 12362.5]])
fig, ax = plt.subplots()
segs = np.zeros((x.shape[0], x.shape[1], 2))
segs[:, :, 1] = y
segs[:, :, 0] = mdates.date2num(x)
lc = mc.LineCollection(segs)
ax.set_xlim(segs[:,:,0].min(), segs[:,:,0].max())
ax.set_ylim(segs[:,:,1].min()-1, segs[:,:,1].max()+1)
ax.add_collection(lc)
Now, zooming works fine -- the x-axis scale adjusts with the zoom -- but the x-axis values don't tell me anything useful, i.e. the precise time I'm currently looking at. To remedy this I tried to e.g. do:
ax.xaxis.set_major_locator(mdates.SecondLocator())
#ax.xaxis.set_minor_locator(mdates.MicrosecondLocator()) # this causes the plot not to display
Fmt = mdates.DateFormatter("%S")
ax.xaxis.set_major_formatter(Fmt)
Now clearly zooming doesn't work fine since matplotlib doesn't know how format the finer ticks. So if I zoom sufficiently -- which I need to do -- I basically have no ticks on the x-axis.
Is there a way to address this? One way I could think of is to be able to setup a callback that gets called when the plot zooms, and adjust the format of the x-axis. But as far as I could find, this is not possible.
It appears that the main problem is currently to get just any useful ticks and labels on your plot. The default way to do this would be
loc = mdates.AutoDateLocator()
fmt = mdates.AutoDateFormatter(loc)
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(fmt)
This would automatically choose useful tick locations for you and is correct down to some microseconds; below that, ticking may become inaccurate due to floating point restrictions.
Meaning, if you need customized or more accurate tick locations you will need to write your own locator and/or change the units of your data (e.g. to "seconds since midnight").
I am trying to plot a data and function with matplotlib 2.0 under python 2.7.
The x values of the function are evolving with time and the x is first decreasing to a certain value, than increasing again.
If the function is plotted against time, it shows function like this plot of data against time
I need the same x axis evolution for plotting against real x values. Unfortunately as the x values are the same for both parts before and after, both values are mixed together. This gives me the wrong data plot:
In this example it means I need the x-axis to start on value 2.4 and decrease to 1.0 than again increase to 2.4. I swear I found before that this is possible, but unfortunately I can't find a trace about that again.
A matplotlib axis is by default linearly increasing. More importantly, there must be an injective mapping of the number line to the axis units. So changing the data range is not really an option (at least when the aim is to keep things simple).
It would hence be good to keep the original numbers and only change the ticks and ticklabels on the axis. E.g. you could use a FuncFormatter to map the original numbers to
np.abs(x-tp)+tp
where tp would be the turning point.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
x = np.linspace(-10,20,151)
y = np.exp(-(x-5)**2/19.)
plt.plot(x,y)
tp = 5
fmt = lambda x,pos:"{:g}".format(np.abs(x-tp)+tp)
plt.gca().xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(fmt))
plt.show()
One option would be to use two axes, and plot your two timespans separately on each axes.
for instance, if you have the following data:
myX = np.linspace(1,2.4,100)
myY1 = -1*myX
myY2 = -0.5*myX-0.5
plt.plot(myX,myY, c='b')
plt.plot(myX,myY2, c='g')
you can instead create two subplots with a shared y-axis and no space between the two axes, plot each time span independently, and finally, adjust the limits of one of your x-axis to reverse the order of the points
fig, (ax1,ax2) = plt.subplots(1,2, gridspec_kw={'wspace':0}, sharey=True)
ax1.plot(myX,myY1, c='b')
ax2.plot(myX,myY2, c='g')
ax1.set_xlim((2.4,1))
ax2.set_xlim((1,2.4))
Is there a way how to force pyplot (matplotlib) to have ticks at values divisible by automatic interval of ticks?
I really like that pyplot can adjust interval of ticks automatically based on data so I don't have to care about it. But I would really like it does use values divisible by that interval.
For example if it decides that interval is 5, it should use values 5,10,15,20... and not 4,9,14,19 like in the example below. How can I easily fix it?
You can locate your ticks anywhere you want using matplotlib.ticker.Locator classes. Specifically in your case I guess you'd like to use MultipleLocator. Just add in your program
from matplotlib.ticker import MultipleLocator
ax = plt.gca()
ax.get_xaxis().set_major_locator(MultipleLocator(base=5))
and you'll be all set.
UPDATE:
To get the base, you can check the default AutoLocator tick positions (after the call to plt.plot) and get the difference between any of them lying next to each other:
ticks = ax.get_xticks()
base = ticks[1] - ticks[0]
I am trying to change the value of the ticks on the x-axis an imshow plot using the following code:
import matplotlib.pyplot as plt
import numpy as np
def scale_xaxis(number):
return(number+1001)
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto')
ax.autoscale(False)
xticks = ax.get_xticks()
ax.xaxis.set_ticklabels(scale_xaxis(xticks))
plt.savefig("test.png")
Resulting image http://ubuntuone.com/2Y5ujtlEkEnrlTcVUxvWLU
However the x-ticks overlap and have "non-round" values. Is there some way for matplotlib to automatically do this? Either by using set_ticklabels or some other way?
Also look into using extent (doc) to let matplotlib do all the thinking about how to put in the tick labels and add in an arbitrary shift:
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto',extent=[10000,10010,0,1])
If you definitely want do to it my hand, you might be better off setting the formatter and locator of the axis to get what you want (doc).
import matplotlib.pyplot as plt
import numpy as np
def scale_xaxis(number):
return(number+1001)
def my_form(x,pos):
return '%d'%scale_xaxis(x)
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto')
ax.autoscale(False)
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(int(2)))
ax.xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(my_form))
The locator needs to be set to make sure that ticks don't get put at non-integer locations which are then forcible cast to integers by the formatter (which would leave them in the wrong place)
related questions:
matplotlib: format axis offset-values to whole numbers or specific number
removing leading 0 from matplotlib tick label formatting
There are several ways to do this.
You can:
Pass in an array of ints instead of an array of floats
Pass in an array of formatted strings
Use a custom tick formatter
The last option is overkill for something this simple.
As an example of the first option, you'd change your scale_xaxis function to be something like this:
def scale_xaxis(numbers):
return numbers.astype(int) + 1001
Note that what you're getting out of ax.get_xticks is a numpy array instead of a single value. Thus, we need to do number.astype(int) instead of int(number).
Alternately, we could return a series of formatted strings. set_xticklabels actually expects a sequence of strings:
def scale_xaxis(numbers):
return ['{:0.0f}'.format(item + 1001) for item in numbers]
Using a custom tick formatter is overkill here, so I'll leave it out for the moment. It's quite handy in the right situation, though.