I am trying to use Matplotlib to generate plot axes within a function called within a for-loop and use these axes to create a final multi-panel figure. However, despite the axes showing the proper lines when they are being created within my function, the multi-panel figure ends up with empty axes. What am I doing wrong? My code is below.
import numpy as np
import matplotlib.pyplot as plt
def create_axis(alpha, beta):
fig, ax = plt.subplots(figsize=(5, 4))
x = np.arange(10, dtype=float)
y = alpha + x * beta
ax.plot(x, y)
return ax
def create_plot():
alpha = 3.
axes_pool = []
for i in range(4):
axes_pool.append(create_axis(alpha, i))
fig, axes = plt.subplots(1, 4)
for i in range(len(axes)):
axes[i] = axes_pool[i]
plt.show()
if __name__ == '__main__':
create_plot()
You are passing the axes to the subfunction to allow the plt on the already created subplots.
Right approach is sending the axes as one of the parameters for create_axis function,which will plot the required data in the supplied axis.
import numpy as np
import matplotlib.pyplot as plt
def create_axis(ax,alpha, beta):
x = np.arange(10, dtype=float)
y = alpha + x * beta
ax.plot(x, y)
def create_plot():
alpha = 3.
fig, axes = plt.subplots(1, 4,figsize=(5,4)))
for i,ax in enumerate(axes):
create_axis(ax,alpha,i)
if __name__ == '__main__':
create_plot()
plt.show()
output:
Related
This question already has answers here:
show origin axis (x,y) in matplotlib plot
(3 answers)
Closed 2 years ago.
So I am working on a program that displays the graph of a function over an interval, and the plot size is automatically handled by matplotlib. The only thing is, it resizes without showing x=0 and y=0 cartesian axes. Everything I tried so far, like plt.subplot(), only affects the axes that show at the bottom and left, not the cartesian axes. Is there a way to add the axes in?
Here is some example code:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-2, 1, 100)
f = lambda x: x**2 - 1
plt.plot(x, f(x))
plt.show()
The graph that comes from this looks like this:
which does not show the cartesian axes. Is there a way to add this in, maybe by adding lines at x=0 and y=0?
You can set the spine axis to be in a custom position, like the origin:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-2,1,100)
y = x**2
fig, ax = plt.subplots(1, figsize=(6, 4))
ax.plot(x, y)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.set(ylim=(-1, 4))
Otherwise, you can add a vertical and a horizontal line:
fig, ax = plt.subplots(1, figsize=(6, 4))
ax.plot(x, y)
ax.axhline(0, color='black')
ax.axvline(0, color='black')
You can do it by drawing arrows:
import matplotlib.pyplot as plt
import numpy as np
from pylab import *
x = np.linspace(-2, 1, 100)
f = lambda x: x**2 - 1
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_aspect('equal')
plt.plot(x, f(x))
l,r = ax.get_xlim()
lo,hi = ax.get_ylim()
arrow( l-1, 0, r-l+2, 0, length_includes_head = False, head_width = 0.2 )
arrow( 0, lo-1, 0, hi-lo+2, length_includes_head = True, head_width = 0.2 )
plt.show()
I plotted the picture using the code below.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(6)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
ax.set_xlim(0, 3)
ax.set_ylim(0, 10)
ax.plot(x, i * x)
plt.show()
This is the result picture.
enter image description here
This is the picture I want to see.
I want to get the results out of the box area.
How can you draw such a plot?
enter image description here
This should work -
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(6)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
ax.set_xlim(0, 3)
ax.set_ylim(0, 10)
ax.plot(x, i * x)
if 3*i >10:
ytx = 10.5
xtx = 10.0/i
else:
ytx = 3*i
xtx = 3.05
tx = plt.text(xtx, ytx, str(i), fontsize=18, color='black')
plt.show()
This generates -
I'm trying to plot 23 graphs in a 6x4 grid, with one figure taking up twice the width of the other figures. I'm using gridspec and my current code is:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
x = np.arange(0, 7, 0.01)
fig = plt.figure(figsize=(6, 4))
gs = gridspec.GridSpec(nrows=6, ncols=4)
for n in range(22):
ax = fig.add_subplot(gs[n])
ax.plot(x, np.sin(0.2*n*x))
corrax = fig.add_subplot(gs[22])
fig.tight_layout()
plt.show()
This produces the following:
I want to increase the width of the rightmost plot in the bottom row so it takes up the remaining space in that row. Is there a way to accomplish this?
You can use slices to select several positions from the gridspec, e.g. gs[22:24].
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
x = np.arange(0, 7, 0.01)
fig = plt.figure(figsize=(6, 4))
gs = gridspec.GridSpec(nrows=6, ncols=4)
for n in range(22):
ax = fig.add_subplot(gs[n])
ax.plot(x, np.sin(0.2*n*x))
corrax = fig.add_subplot(gs[22:24])
corrax.plot(x,np.sin(0.2*22*x), color="crimson", lw=3)
fig.tight_layout()
plt.show()
You can also slice the gridspec two-dimensionally. E.g. to create a 3x3 grid and make the plot in the lower right corner span two columns and two rows, you could slice like gs[1:,1:].
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
x = np.arange(0, 7, 0.01)
fig = plt.figure(figsize=(6, 4))
gs = gridspec.GridSpec(nrows=3, ncols=3)
for n in range(3):
ax = fig.add_subplot(gs[0,n])
ax.plot(x, np.sin(0.2*n*x))
if n !=0:
ax = fig.add_subplot(gs[n,0])
ax.plot(x, np.sin(0.2*n*x))
corrax = fig.add_subplot(gs[1:,1:])
corrax.plot(x,np.sin(0.2*22*x), color="crimson", lw=3)
fig.tight_layout()
plt.show()
#corrax = fig.add_subplot(gs[5,2:])
corrax = fig.add_subplot(6,4,(23,24))
both shold work.
see examples
I have a series of lines representing the change of a variable; each with a unique color. For that reason I want to add a colorbar next to the plot. The desired output is shown below.
The problem is that plot is a non-mappable object, i.e. the colorbar has to be added manually. I consider my current solution (below) sub-optimal as it involves size parameters of which I have no interest in controlling. I'd prefer a similar solution as for a mappable object (example below current solution).
Desired output
Current solution
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.linspace(0, 5, 100)
N = 20
cmap = plt.get_cmap('jet',N)
fig = plt.figure(figsize=(8,6))
ax1 = fig.add_axes([0.10,0.10,0.70,0.85])
for i,n in enumerate(np.linspace(0,2,N)):
y = np.sin(x)*x**n
ax1.plot(x,y,c=cmap(i))
plt.xlabel('x')
plt.ylabel('y')
ax2 = fig.add_axes([0.85,0.10,0.05,0.85])
norm = mpl.colors.Normalize(vmin=0,vmax=2)
cb1 = mpl.colorbar.ColorbarBase(ax2,cmap=cmap,norm=norm,orientation='vertical')
plt.show()
Desired solution
(obviously replacing imshow)
fig,ax = plt.subplots()
cax = ax.imshow(..)
cbar = fig.colorbar(cax,aspect=10)
plt.show()
You may define your own ScalarMappable and use it just as if it was present in the plot.
(Note that I changed the numbero f colors to 21 to have nice spacings of 0.1)
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.linspace(0, 5, 100)
N = 21
cmap = plt.get_cmap('jet',N)
fig = plt.figure(figsize=(8,6))
ax1 = fig.add_axes([0.10,0.10,0.70,0.85])
for i,n in enumerate(np.linspace(0,2,N)):
y = np.sin(x)*x**n
ax1.plot(x,y,c=cmap(i))
plt.xlabel('x')
plt.ylabel('y')
norm = mpl.colors.Normalize(vmin=0,vmax=2)
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
plt.colorbar(sm, ticks=np.linspace(0,2,N),
boundaries=np.arange(-0.05,2.1,.1))
plt.show()
I have two subplots that share the x-axes. The first one has data and a fit function, in the second one is the difference between the data and the fit function. In the figure both subplots have the same y axis size (in pixels). Now i want the y axis of the data and the fit to be bigger than the axis of the errors. my code is the following:
import matplotlib.pyplot as plt
f, axarr = plt.subplots(2, sharex=True,figsize=(15, 12))
axarr[0].scatter(x, data , facecolors='none', edgecolors='crimson')
axarr[0].plot(x, fit, color='g',linewidth=1.5)
axarr[0].set_ylim([18,10])
axarr[1].plot(x,data-fit,color='k',linewidth=width)
axarr[1].set_ylim([-0.4,0.4])
yticks[-1].label1.set_visible(False)
plt.subplots_adjust(hspace=0.)
is there any code that sets the size of the second plot?
Take a look at this example, using gridspec. I believe it is exactly what you want. Below is the example adopted for your case. Edited to also share the x-axis
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
# generate some data
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# plot it
fig = plt.figure(figsize=(8, 6))
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
ax0 = plt.subplot(gs[0])
ax1 = plt.subplot(gs[1], sharex=ax0) # <---- sharex=ax0 will share ax1 with ax2
ax0.plot(x, y)
ax1.plot(y, x)
plt.show()
Or even simpler by following Hagnes answer in the first link:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.2)
y = np.sin(x)
f, (a0, a1) = plt.subplots(2,1, gridspec_kw = {'height_ratios':[1, 3]}, sharex=True) # <---- sharex=True will share the xaxis between the two axes
a0.plot(x, y)
a1.plot(y, x)
plt.show()