matplotlib subplots with shared axis - python

I have some troubles understanding how the matplotlib subplots allow for sharing axis between them. I saw some exemples but i could not modify one to fit my use case..; Here i replaced my data by uniforms so the plots wont be interesting but whatever...
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib import cm
d = 4
n1 = 100000
n2 = 100
background_data = np.random.uniform(size=(n1,d))
foreground_data = np.random.uniform(size=(n2,d))
fig = plt.figure()
for i in np.arange(d):
for j in np.arange(d):
if i != j:
ax = fig.add_subplot(d,d,1+i*d+j)
ax = plt.hist2d(background_data[:, i], background_data[:, j],
bins=3*n2,
cmap=cm.get_cmap('Greys'),
norm=mpl.colors.LogNorm())
ax = plt.plot(foreground_data[:,i],foreground_data[:,j],'o',markersize=0.2)
Q : How can i share the x and y axes for all plots ?

By far the easiest option is to use sharex and sharey arguments of plt.subplots.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
d = 4
n1 = 100000
n2 = 100
background_data = np.random.uniform(size=(n1,d))
foreground_data = np.random.uniform(size=(n2,d))
fig, axs = plt.subplots(d,d, sharex=True, sharey=True)
for i in np.arange(d):
for j in np.arange(d):
if i != j:
ax = axs[j,i]
ax.hist2d(background_data[:, i], background_data[:, j],
bins=3*n2,
cmap=plt.get_cmap('Greys'),
norm=mpl.colors.LogNorm())
ax.plot(foreground_data[:,i],foreground_data[:,j],'o',markersize=2)
else:
axs[j,i].remove()
fig.savefig("sharedaxes.png")
plt.show()

Related

Change number of axes of an existing figure in matplotlib

Given a figure with n x m axes, I want to replace the n x m axes with n' x m' axes on the same figure.
Through some research and reading I came up with the following solution:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib
plt.ion()
ncols = 4
nrows = 3
fig, axes = plt.subplots(nrows, ncols, sharex=True, squeeze=False)
df = pd.DataFrame(data=np.random.rand(10, 12))
df.plot(subplots=True, ax=axes)
# %% Attempt to change figure layout.
# Remove all old axes
for ii in range(nrows):
for jj in range(ncols):
axes[ii, jj].remove()
# New layout
new_nrows = 5
new_ncols = 4
gs = gridspec.GridSpec(new_nrows, new_ncols, figure=fig)
new_axes: np.ndarray = np.ndarray(
(new_nrows, new_ncols), dtype=matplotlib.axes.SubplotBase
)
# Add new axes
for ii in range(new_nrows):
for jj in range(new_ncols):
new_axes[ii, jj] = fig.add_subplot(gs[ii, jj], sharex=new_axes[0, 0])
df_new = pd.DataFrame(data=np.random.rand(10, 20))
df_new.plot(subplots=True, ax=new_axes)
However, I am wondering if there is a nicer way for achieving the same goal (for example a way for getting rid off the for loops or to shorten the overall code).

How to plot 4 plots per row in matplotlib? [duplicate]

This question already has answers here:
How to plot in multiple subplots
(12 answers)
Closed 1 year ago.
I have 864 plots to plot.
On running a loop, I can do plt.show() in the for loop, and print 864 plots, but that's difficult to view.
Is there a way I can print 4 plots per row? (That would be 216 x 4).
And how can I save them at the same time?
Edit: Example:
import matplotlib.pyplot as plt
i = 0
for i in range(100):
plt.scatter(x[i],x[i])
plt.scatter(y[i],y[i])
plt.title('Vector: {}/100'.format(i+1))
plt.show()
where x & y are list of list of cosine vectors.
The easiest option that comes to my mind is using subplot:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(4,216))
axs = fig.axes
gs = fig.add_gridspec(ncols=4, nrows=216)
axs = gs.subplots()
There we are creating a figure with 4x216 plots. Probably you will need to adjust the figsize to your desired dimentions.
To plot something you just need to acces the axis using its index. For example:
x = [1, 2, 3]
y = [[1, 2], [3, 4], [5, 6]]
axs[0,0].plot(x, y)
To save it you can use fig.savefig("plot.png"). That creates a huge image. My suggestion is to create a pdf and store 4x6 plots per page. Here is an example of how to do it:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
pp = PdfPages('plot.pdf')
for i in range(36):
fig = plt.figure(figsize=(16,24))
axs = fig.axes
gs = fig.add_gridspec(ncols=4, nrows=6)
axs = gs.subplots()
pp.savefig()
pp.close()
This process takes some time since it has to render a lot of images. Plotting a line made of 1000 random points (previously calculated) in each figure takes 37s. Here is the code of the test:
import time
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import numpy as np
start = time.time()
pp = PdfPages('plot.pdf')
xx = np.random.randint(0,100, 1000)
yy = np.random.randint(0,100, 1000)
for i in range(36):
print(i)
fig = plt.figure(figsize=(16,24))
axs = fig.axes
gs = fig.add_gridspec(ncols=4, nrows=6)
axs = gs.subplots()
for x in range(4):
for y in range(6):
axs[y,x].plot(xx, yy)
pp.savefig()
pp.close()
print(time.time() - start)
Commented inline
# 10 plots
for j in range(10):
# close existing plots if any
plt.close('all')
plt.figure(figsize=(10,5))
for i in range(4):
# Plot with 1 row and 4 columns and current plot being drawn is (i+1)
plt.subplot(1,4,i+1)
x = np.random.randint(0,100, 100)
y = np.random.randint(0,100, 100)
plt.scatter(x,y)
plt.scatter(y,x)
# Finally save the plot
plt.savefig(f"plot_{j}.png")
Adjust figsize based on length of your x and y axis.

Adding a colorbar to pyplot [duplicate]

I have a sequence of line plots for two variables (x,y) for a number of different values of a variable z. I would normally add the line plots with legends like this:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
# suppose mydata is a list of tuples containing (xs, ys, z)
# where xs and ys are lists of x's and y's and z is a number.
legns = []
for(xs,ys,z) in mydata:
pl = ax.plot(xs,ys,color = (z,0,0))
legns.append("z = %f"%(z))
ax.legends(legns)
plt.show()
But I have too many graphs and the legends will cover the graph. I'd rather have a colorbar indicating the value of z corresponding to the color. I can't find anything like that in the galery and all my attempts do deal with the colorbar failed. Apparently I must create a collection of plots before trying to add a colorbar.
Is there an easy way to do this? Thanks.
EDIT (clarification):
I wanted to do something like this:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111)
mycmap = cm.hot
# suppose mydata is a list of tuples containing (xs, ys, z)
# where xs and ys are lists of x's and y's and z is a number between 0 and 1
plots = []
for(xs,ys,z) in mydata:
pl = ax.plot(xs,ys,color = mycmap(z))
plots.append(pl)
fig.colorbar(plots)
plt.show()
But this won't work according to the Matplotlib reference because a list of plots is not a "mappable", whatever this means.
I've created an alternative plot function using LineCollection:
def myplot(ax,xs,ys,zs, cmap):
plot = lc([zip(x,y) for (x,y) in zip(xs,ys)], cmap = cmap)
plot.set_array(array(zs))
x0,x1 = amin(xs),amax(xs)
y0,y1 = amin(ys),amax(ys)
ax.add_collection(plot)
ax.set_xlim(x0,x1)
ax.set_ylim(y0,y1)
return plot
xs and ys are lists of lists of x and y coordinates and zs is a list of the different conditions to colorize each line. It feels a bit like a cludge though... I thought that there would be a more neat way to do this. I like the flexibility of the plt.plot() function.
(I know this is an old question but...) Colorbars require a matplotlib.cm.ScalarMappable, plt.plot produces lines which are not scalar mappable, therefore, in order to make a colorbar, we are going to need to make a scalar mappable.
Ok. So the constructor of a ScalarMappable takes a cmap and a norm instance. (norms scale data to the range 0-1, cmaps you have already worked with and take a number between 0-1 and returns a color). So in your case:
import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(min=0, max=1))
plt.colorbar(sm)
Because your data is in the range 0-1 already, you can simplify the sm creation to:
sm = plt.cm.ScalarMappable(cmap=my_cmap)
EDIT: For matplotlib v1.2 or greater the code becomes:
import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)
EDIT: For matplotlib v1.3 or greater the code becomes:
import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)
EDIT: For matplotlib v3.1 or greater simplifies to:
import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
plt.colorbar(sm)
Here's one way to do it while still using plt.plot(). Basically, you make a throw-away plot and get the colorbar from there.
import matplotlib as mpl
import matplotlib.pyplot as plt
min, max = (-40, 30)
step = 10
# Setting up a colormap that's a simple transtion
mymap = mpl.colors.LinearSegmentedColormap.from_list('mycolors',['blue','red'])
# Using contourf to provide my colorbar info, then clearing the figure
Z = [[0,0],[0,0]]
levels = range(min,max+step,step)
CS3 = plt.contourf(Z, levels, cmap=mymap)
plt.clf()
# Plotting what I actually want
X=[[1,2],[1,2],[1,2],[1,2]]
Y=[[1,2],[1,3],[1,4],[1,5]]
Z=[-40,-20,0,30]
for x,y,z in zip(X,Y,Z):
# setting rgb color based on z normalized to my range
r = (float(z)-min)/(max-min)
g = 0
b = 1-r
plt.plot(x,y,color=(r,g,b))
plt.colorbar(CS3) # using the colorbar info I got from contourf
plt.show()
It's a little wasteful, but convenient. It's also not very wasteful if you make multiple plots as you can call plt.colorbar() without regenerating the info for it.
Here is a slightly simplied example inspired by the top answer given by Boris and Hooked (Thanks for the great idea!):
1. Discrete colorbar
Discrete colorbar is more involved, because colormap generated by mpl.cm.get_cmap() is not a mappable image needed as a colorbar() argument. A dummie mappable needs to generated as shown below:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)
cmap = mpl.cm.get_cmap('jet', n_lines)
fig, ax = plt.subplots(dpi=100)
# Make dummie mappable
dummie_cax = ax.scatter(c, c, c=c, cmap=cmap)
# Clear axis
ax.cla()
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap(i))
fig.colorbar(dummie_cax, ticks=c)
plt.show();
This will produce a plot with a discrete colorbar:
2. Continuous colorbar
Continuous colorbar is less involved, as mpl.cm.ScalarMappable() allows us to obtain an "image" for colorbar().
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)
norm = mpl.colors.Normalize(vmin=c.min(), vmax=c.max())
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.jet)
cmap.set_array([])
fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap.to_rgba(i + 1))
fig.colorbar(cmap, ticks=c)
plt.show();
This will produce a plot with a continuous colorbar:
[Side note] In this example, I personally don't know why cmap.set_array([]) is necessary (otherwise we'd get error messages). If someone understand the principles under the hood, please comment :)
As other answers here do try to use dummy plots, which is not really good style, here is a generic code for a
Discrete colorbar
A discrete colorbar is produced in the same way a continuous colorbar is created, just with a different Normalization. In this case a BoundaryNorm should be used.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1., n_lines + 1)
cmap = plt.get_cmap("jet", len(c))
norm = matplotlib.colors.BoundaryNorm(np.arange(len(c)+1)+0.5,len(c))
sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([]) # this line may be ommitted for matplotlib >= 3.1
fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap(i))
fig.colorbar(sm, ticks=c)
plt.show()

Python How to set axes for a matplotlib plot

Hi for the matplotlib plot below I want to set the axes titles such that they show that the x-axis values run from
2**-5, 2**-4, 2**-3,..., 2**14, 2**15
and the y-axis values run from
2**-15, 2**-14,...., 2**4, 2**5
The graph I want to display them on is:
The code for the graph is below:
from matplotlib import pyplot
import matplotlib as mpl
import numpy as np
zvals = 100*np.random.randn(21, 21)
fig = pyplot.figure(2)
cmap2 = mpl.colors.LinearSegmentedColormap.from_list('my_colormap',
['blue','green','brown'],
256)
img2 = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap2,
origin='lower')
pyplot.colorbar(img2,cmap=cmap2)
pyplot.show()
You can use a range with a stepsize to label every 5th cell:
locs = range(0, N, 5)
ax.set(xticks=locs, xlabels=...)
For example,
from matplotlib import pyplot as plt
from matplotlib import colors as mcolors
import numpy as np
N = 21
zvals = 100*np.random.randn(N, N)
fig = plt.figure(2)
ax = fig.add_subplot(111)
cmap2 = mcolors.LinearSegmentedColormap.from_list(
'my_colormap', ['blue','green','brown'], 256)
img2 = plt.imshow(zvals,interpolation='nearest',
cmap=cmap2, origin='lower')
plt.colorbar(img2, cmap=cmap2)
step = 5
locs = range(0, N, step)
ax.set(
xticks=locs,
xticklabels=['$2^{{{}}}$'.format(i-5) for i in locs],
yticks=locs,
yticklabels=['$2^{{{}}}$'.format(i-15) for i in locs])
plt.show()

3D scatter plot colorbar matplotlib Python

I cannot add a colorbar to my 3D scatter plot that is coloured in range of min and max according to the value of bifurWidth. I've tried various attempts shown on stackoverflow, none have had any success. Any help would really be appreciated, as I am at a major loss with this.
My most recent attempt is hashed out of the code shown below.
My code:
from glob import glob
from pylab import *
import numpy as np
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
def myScatter(x0,y0,power_array,c,lw,s,vmin,vmax,cmap,label,ax):
ax.scatter(x0,y0,power_array,c=c,lw=lw,s=s,vmin=min,vmax=max,cmap=cmhot,label=label)
fig = figure()
ax = fig.add_subplot(111, projection='3d')
cmhot = get_cmap("jet")
fig.tight_layout()
fig.set_size_inches(25,15)
min = 3 #colorbar range
max = 10
lw = 0 #linewidth
s = 10 #scatter size
for idx, p in enumerate(dataSorted[:,1]):
powerLoop = dataSorted[idx,0]
powerLoop = powerLoop.astype(np.float)
bifurWidthLoop = dataSorted[idx,2]
bifurWidthLoop = bifurWidthLoop.astype(np.float)
y0 = genfromtxt(p, unpack=True, usecols=[0], skiprows=19, skip_footer=1)
length = len(x0)
power_array = [powerLoop] * length
bifurWidth_array = [bifurWidthLoop] * length
label = str(bifurWidth)
a = myScatter(x0,power_array,y0,bifurWidth_array,lw,s,min,max,cmhot,label,ax)
#cax = ax.imshow(y0, interpolation='nearest', vmin=min, vmax=max)
#fig.colorbar(cax)
fig.savefig('test.png',dpi=300)
Example of an attempt and its error:
If I use fig.colorbar(a) inside or outside of the plotting for loop, I return NoneType oject has no attribute autoscale_None.
Your code doesn't run (x0,dataSorted,y0,etc missing) so can't get it to work (also note x0,power_array,y0 are wrong order in fn call). You need to return the handle to the scatter plot in order to plot a colorbar. If you change your myScatter function to return the handle,
def myScatter(x0,y0,power_array,c,lw,s,vmin,vmax,cmap,label,ax):
return ax.scatter(x0,y0,power_array,c=c,lw=lw,s=s,vmin=min,vmax=max,cmap=cmhot,label=label)
and then call plt.colorbar(a). A minimal(ish) example would be,
from glob import glob
from pylab import *
import numpy as np
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
def myScatter(x0,y0,power_array,c,lw,s,vmin,vmax,cmap,label,ax):
return ax.scatter(x0,y0,power_array,c=c,lw=lw,s=s,vmin=min,vmax=max,cmap=cmhot,label=label)
fig = figure()
ax = fig.add_subplot(111, projection='3d')
cmhot = get_cmap("jet")
fig.tight_layout()
fig.set_size_inches(25,15)
min = 3 #colorbar range
max = 10
lw = 0 #linewidth
s = 10 #scatter size
label = 'test'
power_array = np.random.random((100,10))
bifurWidth_array = np.random.random((100,10))*(max-min)+min
x0 = np.random.random((100,10))
y0 = np.random.random((100,10))
a = myScatter(x0,power_array,y0,bifurWidth_array,lw,s,min,max,cmhot,label,ax)
plt.colorbar(a)
plt.show()

Categories