How to add axis labels to imshow plots in python? - python

I copied from this website (and simplified) the following code to plot the result of a function with two variables using imshow.
from numpy import exp,arange
from pylab import meshgrid,cm,imshow,contour,clabel,colorbar,axis,title,show
# the function that I'm going to plot
def z_func(x,y):
return (x+y**2)
x = arange(-3.0,3.0,0.1)
y = arange(-3.0,3.0,0.1)
X,Y = meshgrid(x, y) # grid of point
Z = z_func(X, Y) # evaluation of the function on the grid
im = imshow(Z,cmap=cm.RdBu) # drawing the function
colorbar(im) # adding the colobar on the right
show()
How do I add axis labels (like 'x' and 'y' or 'var1 and 'var2') to the plot? In R I would use xlab = 'x' within (most of) the plotting function(s).
I tried im.ylabel('y') with the
AttributeError: 'AxesImage' object has no attribute 'ylabel'
Beside this, I only found how to remove the axis labels, but not how to add them.
Bonus question: how to have the ticks range from -3 to 3 and not from 0 to 60?

To specify axes labels:
matplotlib.pyplot.xlabel for the x axis
matplotlib.pyplot.ylabel for the y axis
Regarding your bonus question, consider extent kwarg. (Thanks to #Jona).
Moreover, consider absolute imports as recommended by PEP 8 -- Style Guide for Python Code:
Absolute imports are recommended, as they are usually more readable
and tend to be better behaved (or at least give better error messages)
if the import system is incorrectly configured (such as when a
directory inside a package ends up on sys.path)
import matplotlib.pyplot as plt
import numpy as np
# the function that I'm going to plot
def z_func(x,y):
return (x+y**2)
x = np.arange(-3.0,3.0,0.1)
y = np.arange(-3.0,3.0,0.1)
X,Y = np.meshgrid(x, y) # grid of point
Z = z_func(X, Y) # evaluation of the function on the grid
plt.xlabel('x axis')
plt.ylabel('y axis')
im = plt.imshow(Z,cmap=plt.cm.RdBu, extent=[-3, 3, -3, 3]) # drawing the function
plt.colorbar(im) # adding the colobar on the right
plt.show()
and you get:

Related

How to reuse figures and axes created with subplot(num= ... )

I have a function I want to use several times to plot data in the same Figure/Axis.
The function toto() in the following code works fine. I use plt.figure(num='Single plot') which check, whenever it is called, if a figure with id 'Single plot' as been already created. If yes, the same figure is reused (the figure instance is global I think) :
import matplotlib.pyplot as plt
import numpy as np
def toto(x, y):
plt.figure('Single plot')
plt.plot(x, y)
x = np.linspace(0, 2)
toto(x, x)
toto(x, x**2)
plt.show()
Now, I want to use subplot()instead of figure() because it will be more useful for me. This function would be :
def toto2(x, y):
fig, axes = plt.subplots(num='Single plot 2')
axes.plot(x, y)
But the result is not as expected at all : y ticks are overprinted and the linear line is not plotted (or as been overprinted).
I'd like to understand the rationale behind this and what to modify so that it works as expected.
Every time you execute plt.subplots, you create a new figure and a new set of axes. If you want to reuse the figure, you should create the fig and axes objects outside of your function, and pass them as argument to your function.
import matplotlib.pyplot as plt
import numpy as np
def toto3(fig, axes, x, y):
axes.plot(x, y)
x = np.linspace(0, 2)
fig, axes = plt.subplots(num='Single plot 2')
toto3(fig, axes, x, x)
toto3(fig, axes, x, x**2)
plt.show()
(in this case, you could even simplify your code to only pass axes to your function, but the idea is applicable to both the fig and the axes objects)

How to slice list from matplotlib ginput

I have a list of values in Python, which I'm plotting with matplotlib. I'm then trying to use ginput in matplotlib to click two points on the graph, from which the X coordinates will be taken, between which to slice my original list. However, I can't seem to find a way to do this.
I already have a list of numbers called MIList, and the following code isn't working for me:
startinput = plt.ginput(2)
print("clicked", startinput)
startinputxvalues = [x[0] for x in startinput]
print(startinputxvalues)
x1 = startinputxvalues[0]
print(x1)
x2 = startinputxvalues[1]
print(x2)
slicedMIList = [MIList[int(x1):int(x2)]]
plt.plot(slicedMIList)
This gives me an array, but it doesn't plot these values on my graph - does anyone have any input as to what I'm doing wrong?
Thanks
The main point is that you need to redraw the canvas, once changes have been made to it. So in order for the new plot to become visible you can call
plt.gcf().canvas.draw()
Here is a complete working code:
import matplotlib.pyplot as plt
import numpy as np
X = np.arange(10)
Y = np.sin(X)
plt.plot(X, Y)
startinput = plt.ginput(2)
x, y = zip(*startinput)
Ysliced = Y[int(x[0]):int(x[1])+1]
Xsliced = X[int(x[0]):int(x[1])+1]
plt.plot(Xsliced, Ysliced, color="C3", linewidth=3)
#draw the canvas, such that the new plot becomes visible
plt.gcf().canvas.draw()
plt.show()

Matplotlib: multiple 3D lines all get drawn using the final y-value in my loop

I am trying to plot multiple lines in a 3D figure. Each line represents a month: I want them displayed parallel in the y-direction.
My plan was to loop over a set of Y values, but I cannot make this work properly, as using the ax.plot command (see working code below) produces a dozen lines all at the position of the final Y value. Confusingly, swapping ax.plot for ax.scatter does produce a set of parallel lines of data (albeit in the form of a set of dots; ax.view_init set to best display the parallel aspect of the result).
How can I use a produce a plot with multiple parallel lines?
My current workaround is to replace the loop with a dozen different arrays of Y values, and that can't be the right answer.
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
# preamble
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
cs = ['r','g','b','y','r','g','b','y','r','g','b','y']
# x axis
X = np.arange(24)
# y axis
y = np.array([15,45,75,105,135,165,195,225,255,285,315,345])
Y = np.zeros(24)
# data - plotted against z axis
Z = np.random.rand(24)
# populate figure
for step in range(0,12):
Y[:] = y[step]
# ax.plot(X,Y,Z, color=cs[step])
ax.scatter(X,Y,Z, color=cs[step])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# set initial view of plot
ax.view_init(elev=80., azim=345.)
plt.show()
I'm still learning python, so simple solutions (or, preferably, those with copious explanatory comments) are greatly appreciated.
Use
ax.plot(X, np.array(Y), Z, color=cs[step])
or
Y = [y[step]] * 24
This looks like a bug in mpl where we are not copying data when you hand it in so each line is sharing the same np.array object so when you update it all of your lines.

Plotting a second scaled y axis in matplotlib from one set of data

I have two views of the same data, which calls for the need to have another y-axis which is scaled appropriately from the first natural y-axis. So when I plot my {x,y} data, the left y-axis shows y, but the right y-axis also shows 1/y or any other function. I do not ever want to plot {x, f(x)} or {x, 1/y}.
Now to complicate matters I am using the .plt style of interaction rather than the axis method.
plt.scatter(X, Y, c=colours[count], alpha=1.0, label=chart, lw = 0)
plt.ylabel(y_lbl)
plt.xlabel(x_lbl)
Is there another way - with plt? Or is it a case of generating two overlain plots and changing the alpha appropriately?
I had to check your previous (duplicate) question and all the comments to understand what you actually want. So to just get a secondary y-axis you can still use twinx. Then you can use set_ylim make sure it has the same limits as the first. To put tick labels according to some function (in your case 1/y) you can use a custom FuncFormatter.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mticker
fig, ax1 = plt.subplots(1,1)
ax1.set_xlabel('x')
ax1.set_ylabel('y')
# plot something
x = np.linspace(0.01, 10*np.pi, 1000)
y = np.sin(x)/x
ax1.plot(x, y)
# add a twin axes and set its limits so it matches the first
ax2 = ax1.twinx()
ax2.set_ylabel('1/y')
ax2.set_ylim(ax1.get_ylim())
# apply a function formatter
formatter = mticker.FuncFormatter(lambda x, pos: '{:.3f}'.format(1./x))
ax2.yaxis.set_major_formatter(formatter)
plt.show()
Result:

How can I predict where matplotlib will draw ticks given some data?

Matplotlib does a good job of setting the limits and ticks on an axes to capture the range of the data while putting the ticks at nicely-spaced round numbers.
I'd like to be able to predict where ticks are going to get drawn for a set of data before it happens (actually, I'd be happy just knowing the limits of the ticks, I don't need to know specifically where the inner ticks will get drawn).
I've poked around the Axes and various Ticker objects, but I can't seem to find where this is happening. Ideally, I am looking for a function automatic_ticker such that if I have two vectors,
x, y = np.random.randn(2, 30)
I could call
xticks_predict = plt.automatic_ticker(x)
and then
plt.plot(x, y)
xticks_actual, _ = plt.xticks()
assert tuple(xticks_predict) == tuple(xticks_actual)
Does this exist?
I think you are looking for something like:
from matplotlib.ticker import MaxNLocator
xticks_predict = MaxNLocator(integer=True, symmetric=True).tick_values(x.min(), x.max())
But I saw some discrepancies in testing vs. the ticks used in plt.plot(x, y), most of which seem to be assymetric tick ranges about the origin --- even with symmetric=True. The docs indicate AutoLocator as the typical default, but I found it to return fractional valued tick locations (e.g. 2.5) when the ticks used in plt.plot() were all integral.
Of course, you could ``cheat'' by setting the plot ticks to the predicted ticks:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import MaxNLocator
np.random.seed(seed=0)
x, y = np.random.randn(2, 30)
ticking = MaxNLocator(integer=True, symmetric=True)
xticks_predict = ticking.tick_values(x.min(), x.max())
plt.plot(x, y)
plt.xticks(xticks_predict)
plt.savefig("example.pdf")

Categories