How to turn off autoscaling in matplotlib.pyplot - python

I am using matplotlib.pyplot in python to plot my data. The problem is the image it generates seems to be autoscaled. How can I turn this off so that when I plot something at (0,0) it will be placed fixed in the center?

You want the autoscale function:
from matplotlib import pyplot as plt
# Set the limits of the plot
plt.xlim(-1, 1)
plt.ylim(-1, 1)
# Don't mess with the limits!
plt.autoscale(False)
# Plot anything you want
plt.plot([0, 1])

You can use xlim() and ylim() to set the limits. If you know your data goes from, say -10 to 20 on X and -50 to 30 on Y, you can do:
plt.xlim((-20, 20))
plt.ylim((-50, 50))
to make 0,0 centered.
If your data is dynamic, you could try allowing the autoscale at first, but then set the limits to be inclusive:
xlim = plt.xlim()
max_xlim = max(map(abs, xlim))
plt.xlim((-max_xlim, max_xlim))
ylim = plt.ylim()
max_ylim = max(map(abs, ylim))
plt.ylim((-max_ylim, max_ylim))

If you want to keep temporarily turn off the auto-scaling to make sure that the scale stays as it was at some point before drawing the last piece of the figure, this might become handy and seems to work better then plt.autoscale(False) as it actually preserves limits as they would have been without the last scatter:
from contextlib import contextmanager
#contextmanager
def autoscale_turned_off(ax=None):
ax = ax or plt.gca()
lims = [ax.get_xlim(), ax.get_ylim()]
yield
ax.set_xlim(*lims[0])
ax.set_ylim(*lims[1])
plt.scatter([0, 1], [0, 1])
with autoscale_turned_off():
plt.scatter([-1, 2], [-1, 2])
plt.show()
plt.scatter([0, 1], [0, 1])
plt.scatter([-1, 2], [-1, 2])
plt.show()

Related

matplotlib subplots: how to freeze x and y axis?

Good evening
matplotlib changes the scaling of the diagram when drawing with e.g. hist() or plot(), which is usually great.
Is it possible to freeze the x and y axes in a subplot after drawing, so that further drawing commands do not change them anymore? For example:
fig, (plt1, plt2) = plt.subplots(2, 1, figsize=(20, 10))
plt1.hist(…)
plt1.plot(…)
# How can this get done?:
plt1.Freeze X- and Y-Axis
# Those commands no longer changes the x- and y-axis
plt1.plot(…)
plt1.plot(…)
Thanks a lot, kind regards,
Thomas
Matplotlib has an autoscale() function that you can turn on or off for individual axis objects and their individual x- and y-axes:
from matplotlib import pyplot as plt
fig, (ax1, ax2) = plt.subplots(2)
#standard is that both x- and y-axis are autoscaled
ax1.plot([1, 3, 5], [2, 5, 1], label="autoscale on")
#rendering the current output
fig.draw_without_rendering()
#turning off autoscale for the x-axis of the upper panel
#the y-axis will still be autoscaled for all following artists
ax1.autoscale(False, axis="x")
ax1.plot([-1, 7], [-2, 4], label="autoscale off")
ax1.legend()
#other axis objects are not influenced
ax2.plot([-2, 4], [3, 1])
plt.show()
Sample output:
Use plt.xlim and plt.ylim to get the current limits after plotting the initial plots, then use those values to set the limits after plotting the additional plots:
import matplotlib.pyplot as plt
# initial data
x = [1, 2, 3, 4, 5]
y = [2, 4, 8, 16, 32]
plt.plot(x, y)
# Save the current limits here
xlims = plt.xlim()
ylims = plt.ylim()
# additional data (will change the limits)
new_x = [-10, 100]
new_y = [2, 2]
plt.plot(new_x, new_y)
# Then set the old limits as the current limits here
plt.xlim(xlims)
plt.ylim(ylims)
plt.show()
Output figure (note how the x-axis limits are ~ [1, 5] even though the orange line is defined in the range [-10, 100]) :
To freeze x-axis specify the domain on the plot function:
import matplotlib.pyplot as plt
fig, (plt1, plt2) = plt.subplots(2, 1, figsize=(20, 10))
# range(min, max, step)
n = range(0, 10, 1) # domain [min, max] = [0, 9]
# make sure your functions has equal length
f = [i * 2 for i in n]
g = [i ** 2 for i in n]
# keep x-axis scale the same by specifying x-axis on the plot function.
plt1.plot(n, f) # funtion (f) range depends on it's value [min, max]
plt1.plot(n, g) # funtion (g) range depends on it's value [min, max]
# range of (f) and (g) impacts the scaling of y-axis
See matplotlib.pyplot for hist function parameters.
The answer of #jfaccioni is almost perfect (thanks a lot!), but it does not work with matplotlib subplots (as asked) because Python, as unfortunately so often, does not have uniform attributes and methods (not even in the same module), and so the matplotlib interface to a plot and a subplot is different.
In this example, this code works with a plot but not with a subplot:
# this works for plots:
xlims = plt.xlim()
# and this must be used for subplots :-(
xlims = plt1.get_xlim()
therefore, this code works with subplots:
import matplotlib.pyplot as plt
fig, (plt1, plt2) = plt.subplots(2, 1, figsize=(20, 10))
# initial data
x = [1, 2, 3, 4, 5]
y = [2, 4, 8, 16, 32]
plt1.plot(x, y)
# Save the current limits here
xlims = plt1.get_xlim()
ylims = plt1.get_ylim()
# additional data (will change the limits)
new_x = [-10, 100]
new_y = [2, 2]
plt1.plot(new_x, new_y)
# Then set the old limits as the current limits here
plt1.set_xlim(xlims)
plt1.set_ylim(ylims)
plt.show()
btw: Freezing the x- and y axes can even be done by 2 lines because once again, python unfortunately has inconsistent attributes:
# Freeze the x- and y axes:
plt1.set_xlim(plt1.get_xlim())
plt1.set_ylim(plt1.get_ylim())
It does not make sense at all to set xlim to the value it already has.
But because Python matplotlib misuses the xlim/ylim attribute and sets the current plot size (and not the limits!), therefore this code works not as expected.
It helps to solve the task in question, but those concepts makes using matplotlib hard and reading matplotlib code is annoying because one must know hidden / unexpected internal behaviors.

Equally and centered distribute data point in matplotlib [duplicate]

I need help with setting the limits of y-axis on matplotlib. Here is the code that I tried, unsuccessfully.
import matplotlib.pyplot as plt
plt.figure(1, figsize = (8.5,11))
plt.suptitle('plot title')
ax = []
aPlot = plt.subplot(321, axisbg = 'w', title = "Year 1")
ax.append(aPlot)
plt.plot(paramValues,plotDataPrice[0], color = '#340B8C',
marker = 'o', ms = 5, mfc = '#EB1717')
plt.xticks(paramValues)
plt.ylabel('Average Price')
plt.xlabel('Mark-up')
plt.grid(True)
plt.ylim((25,250))
With the data I have for this plot, I get y-axis limits of 20 and 200. However, I want the limits 20 and 250.
Get current axis via plt.gca(), and then set its limits:
ax = plt.gca()
ax.set_xlim([xmin, xmax])
ax.set_ylim([ymin, ymax])
One thing you can do is to set your axis range by yourself by using matplotlib.pyplot.axis.
matplotlib.pyplot.axis
from matplotlib import pyplot as plt
plt.axis([0, 10, 0, 20])
0,10 is for x axis range.
0,20 is for y axis range.
or you can also use matplotlib.pyplot.xlim or matplotlib.pyplot.ylim
matplotlib.pyplot.ylim
plt.ylim(-2, 2)
plt.xlim(0,10)
Another workaround is to get the plot's axes and reassign changing only the y-values:
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,25,250))
You can instantiate an object from matplotlib.pyplot.axes and call the set_ylim() on it. It would be something like this:
import matplotlib.pyplot as plt
axes = plt.axes()
axes.set_ylim([0, 1])
Just for fine tuning. If you want to set only one of the boundaries of the axis and let the other boundary unchanged, you can choose one or more of the following statements
plt.xlim(right=xmax) #xmax is your value
plt.xlim(left=xmin) #xmin is your value
plt.ylim(top=ymax) #ymax is your value
plt.ylim(bottom=ymin) #ymin is your value
Take a look at the documentation for xlim and for ylim
This worked at least in matplotlib version 2.2.2:
plt.axis([None, None, 0, 100])
Probably this is a nice way to set up for example xmin and ymax only, etc.
To add to #Hima's answer, if you want to modify a current x or y limit you could use the following.
import numpy as np # you probably alredy do this so no extra overhead
fig, axes = plt.subplot()
axes.plot(data[:,0], data[:,1])
xlim = axes.get_xlim()
# example of how to zoomout by a factor of 0.1
factor = 0.1
new_xlim = (xlim[0] + xlim[1])/2 + np.array((-0.5, 0.5)) * (xlim[1] - xlim[0]) * (1 + factor)
axes.set_xlim(new_xlim)
I find this particularly useful when I want to zoom out or zoom in just a little from the default plot settings.
This should work. Your code works for me, like for Tamás and Manoj Govindan. It looks like you could try to update Matplotlib. If you can't update Matplotlib (for instance if you have insufficient administrative rights), maybe using a different backend with matplotlib.use() could help.

Draw arrows on matplotlib figure directly

I created a plot containing multiple subplots on a grid. The plots differ in two parameters, so I would like it to look like they ordered in a coordinate system.
I managed to plot lines using matplotlib.lines.Line2D() next to the subplots directly on the figure.
But I would prefer to have an arrow instead of a line to make it more clear.
(I can add the specific parameter values using fig.text().)
I'd like the blue lines to be arrows
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from itertools import product
fig = plt.figure()
plotGrid = mpl.gridspec.GridSpec(2, 2)
x = np.linspace(0,10,10000)
y = [j* np.sin(x + i) for i,j in product(range(2), range(1,3))]
for i in range(4):
ax = plt.Subplot(fig, plotGrid[i])
for sp in ax.spines.values():
sp.set_visible(False)
ax.plot(x,y[i], color = 'r')
ax.set_xticks([])
ax.set_yticks([])
fig.add_subplot(ax)
all_axes = fig.get_axes()
#I would like these lines to be arrows
blcorPosn = 0.08 #bottom corner position
l1 = mpl.lines.Line2D([blcorPosn,blcorPosn], [1, blcorPosn],
transform=fig.transFigure, fig)
l2 = mpl.lines.Line2D([blcorPosn, 1], [blcorPosn, blcorPosn],
transform=fig.transFigure, fig)
fig.lines.extend([l1, l2])
I'm not sure if this is the way to go. But I spend like a day on this by now and the only way I see so far to draw arrows is drawing them directly on an axes but thats not an option for me as far as I can see.
Also this is my first post here so advice on how to ask questions is highly appreciated.
Thanks
You can replace the Line2D along each axis with a slightly modified call to FancyArrow patch. The main difference is that that origin and destination x,y coords are replaced with origin x,y and a x,y distance to draw. The values are also passed as parameters directly, not as lists:
l1 = mpl.patches.FancyArrow(blcorPosn, blcorPosn, 1, 0,
transform=fig.transFigure, figure=fig)
l2 = mpl.patches.FancyArrow(blcorPosn, blcorPosn, 0, 1,
transform=fig.transFigure, figure=fig)
The FancyArrow patch accepts a few other parameters to allow you to customise the appearance of the arrow including width (for line width), head_width and head_length.

How to set the axis scale and ticklabels using matplotlib object oriented API

I would need some help with plotting in Matplotlib.pyplot under Python2.7!
I want to generate a plot with the following x-axis:
x-axis as it should be
I got so far by using myaxis.set_xticks([0,0.5,1,2,4,6,8]) and it looks good, but if I want to create **an logarithmic x-axis* **, then my axis labels look like this!
wrong x-axis labels
What can I do to have both a log-scaled x-axis and integer formated labels (not logarithmic values as labels either!). Please read the note regarding to the log-scale!!!
While browsing Stackoverflow I found the following similar question, but nothing of the suggestions worked for me and I do not know what I did wrong.
Matplotlib: show labels for minor ticks also
Thanks!
Note: This plot is called Madau-Plot (see:adsabs[dot]harvard[dot]edu Madau (1998) DOI=10.1086/305523). It is common to plot it log-scales and show the z=0.0 value although the axis is log-scaled axis and log10(0)=Error. I definitely want to point out here that this is common use in my field but should not be applied one to one to any other plots. So actually the plot is made with a trick! You plot (1+z) [1,1.5,2,3,5,7,9]] and then translate the x-axis to the pure z-values 0.0 < z 8.0! So what I need to find is how to set xticks to the "translated" values ([0,0.5,1,2,4,6,8])
What if you plotted your datapoint corresponding to x=0 somewhere else, like at x=0.25, then relabel it. For example,
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
plot_vals = [0.25, 0.5, 1, 2, 4, 6, 8]
label_vals = [0, 0.5, 1, 2, 4, 6, 8]
ax.plot(plot_vals, plot_vals, 'k-o')
ax.set_xscale('log')
ax.set_xticks(plot_vals)
ax.set_xticklabels(label_vals) # relabeling the ticklabels
This yields what I think is what you want.
You can turn off minor ticks by doing something like:
ax.tick_params(axis='x', which='minor', bottom='off', top='off')
Edit: Given the edit to the op, this can be done easily by:
import matplotlib.pyplot as plt
original_values = [0, 0.5, 1, 2, 4, 6, 8]
# if using numpy:
# import numpy as np
# plot_values = np.array(original_values) + 1
# if using pure python
plot_values = [i + 1 for i in original_values]
fig, ax = plt.subplots()
ax.plot(plot_values, plot_values, 'k-o') #substitute actual plotting here
ax.set_xscale('log')
ax.set_xticks(plot_values)
ax.set_xticklabels(original_values)
which yields:

How to set the y-axis limit

I need help with setting the limits of y-axis on matplotlib. Here is the code that I tried, unsuccessfully.
import matplotlib.pyplot as plt
plt.figure(1, figsize = (8.5,11))
plt.suptitle('plot title')
ax = []
aPlot = plt.subplot(321, axisbg = 'w', title = "Year 1")
ax.append(aPlot)
plt.plot(paramValues,plotDataPrice[0], color = '#340B8C',
marker = 'o', ms = 5, mfc = '#EB1717')
plt.xticks(paramValues)
plt.ylabel('Average Price')
plt.xlabel('Mark-up')
plt.grid(True)
plt.ylim((25,250))
With the data I have for this plot, I get y-axis limits of 20 and 200. However, I want the limits 20 and 250.
Get current axis via plt.gca(), and then set its limits:
ax = plt.gca()
ax.set_xlim([xmin, xmax])
ax.set_ylim([ymin, ymax])
One thing you can do is to set your axis range by yourself by using matplotlib.pyplot.axis.
matplotlib.pyplot.axis
from matplotlib import pyplot as plt
plt.axis([0, 10, 0, 20])
0,10 is for x axis range.
0,20 is for y axis range.
or you can also use matplotlib.pyplot.xlim or matplotlib.pyplot.ylim
matplotlib.pyplot.ylim
plt.ylim(-2, 2)
plt.xlim(0,10)
Another workaround is to get the plot's axes and reassign changing only the y-values:
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,25,250))
You can instantiate an object from matplotlib.pyplot.axes and call the set_ylim() on it. It would be something like this:
import matplotlib.pyplot as plt
axes = plt.axes()
axes.set_ylim([0, 1])
Just for fine tuning. If you want to set only one of the boundaries of the axis and let the other boundary unchanged, you can choose one or more of the following statements
plt.xlim(right=xmax) #xmax is your value
plt.xlim(left=xmin) #xmin is your value
plt.ylim(top=ymax) #ymax is your value
plt.ylim(bottom=ymin) #ymin is your value
Take a look at the documentation for xlim and for ylim
This worked at least in matplotlib version 2.2.2:
plt.axis([None, None, 0, 100])
Probably this is a nice way to set up for example xmin and ymax only, etc.
To add to #Hima's answer, if you want to modify a current x or y limit you could use the following.
import numpy as np # you probably alredy do this so no extra overhead
fig, axes = plt.subplot()
axes.plot(data[:,0], data[:,1])
xlim = axes.get_xlim()
# example of how to zoomout by a factor of 0.1
factor = 0.1
new_xlim = (xlim[0] + xlim[1])/2 + np.array((-0.5, 0.5)) * (xlim[1] - xlim[0]) * (1 + factor)
axes.set_xlim(new_xlim)
I find this particularly useful when I want to zoom out or zoom in just a little from the default plot settings.
This should work. Your code works for me, like for Tamás and Manoj Govindan. It looks like you could try to update Matplotlib. If you can't update Matplotlib (for instance if you have insufficient administrative rights), maybe using a different backend with matplotlib.use() could help.

Categories