I'm rendering some graphics in python with matplotlib, and will include them into a LaTeX paper (using LaTex's nice tabular alignment instead of fiddling with matplotlib's ImageGrid, etc.). I would like to create and save a standalone colorbar with savefig, without needing to use imshow.
(the vlim, vmax parameters, as well as the cmap could be provided explicitly)
The only way I could find was quite complicated and (from what I understand) draws a hard-coded rectangle onto the canvas:
http://matplotlib.org/examples/api/colorbar_only.html
Is there an elegant way to create a standalone colorbar with matplotlib?
You can create some dummy image and then hide it's axe. Draw your colorbar in a customize Axes.
import pylab as pl
import numpy as np
a = np.array([[0,1]])
pl.figure(figsize=(9, 1.5))
img = pl.imshow(a, cmap="Blues")
pl.gca().set_visible(False)
cax = pl.axes([0.1, 0.2, 0.8, 0.6])
pl.colorbar(orientation="h", cax=cax)
pl.savefig("colorbar.pdf")
the result:
Using the same idea as in HYRY's answer, if you want a "standalone" colorbar in the sense that it is independent of the items on a figure (not directly connected with how they are colored), you can do something like the following:
from matplotlib import pyplot as plt
import numpy as np
# create dummy invisible image
# (use the colormap you want to have on the colorbar)
img = plt.imshow(np.array([[0,1]]), cmap="Oranges")
img.set_visible(False)
plt.colorbar(orientation="vertical")
# add any other things you want to the figure.
plt.plot(np.random.rand(30))
So, based on this answer here, if you're like me and want to avoid this ugly fake plt.imshow(), you can do this in basically two lines:
import matplotlib as mpl
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
col_map = plt.get_cmap('nipy_spectral')
mpl.colorbar.ColorbarBase(ax, cmap=col_map, orientation = 'vertical')
# As for a more fancy example, you can also give an axes by hand:
c_map_ax = fig.add_axes([0.2, 0.8, 0.6, 0.02])
c_map_ax.axes.get_xaxis().set_visible(False)
c_map_ax.axes.get_yaxis().set_visible(False)
# and create another colorbar with:
mpl.colorbar.ColorbarBase(c_map_ax, cmap=col_map, orientation = 'horizontal')
That reference to http://matplotlib.org/examples/api/colorbar_only.html solved it for me. That example is a little verbose, so here is an easy way to make a standalone colorbar (for posterity)...
import matplotlib.pyplot as plt
import matplotlib as mpl
fig = plt.figure()
ax = fig.add_axes([0.05, 0.80, 0.9, 0.1])
cb = mpl.colorbar.ColorbarBase(ax, orientation='horizontal',
cmap='RdBu')
plt.savefig('just_colorbar', bbox_inches='tight')
Of course, you can specify many other aspects of the Colorbar
import matplotlib.pyplot as plt
import matplotlib as mpl
fig = plt.figure()
ax = fig.add_axes([0.05, 0.80, 0.9, 0.1])
cb = mpl.colorbar.ColorbarBase(ax, orientation='horizontal',
cmap='gist_ncar',
norm=mpl.colors.Normalize(0, 10), # vmax and vmin
extend='both',
label='This is a label',
ticks=[0, 3, 6, 9])
plt.savefig('just_colorbar', bbox_inches='tight')
This solution can be also used to draw the colorbar independenly of the content of ax.
Just set fraction = .05.
Code
import matplotlib as mpl
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
fraction = 1 # .05
norm = mpl.colors.Normalize(vmin=-3, vmax=99)
cbar = ax.figure.colorbar(
mpl.cm.ScalarMappable(norm=norm, cmap='Blues'),
ax=ax, pad=.05, extend='both', fraction=fraction)
ax.axis('off')
plt.show()
To add to #blaylockbk's answer (which is a great solution, even to add to an already created figure), for me the cmap argument won't take strings for colormap names, but cmap = plt.cm.viridis works, if anyone run into the same problem as I.
Related
I'm making some interactive plots and I would like to add a colorbar legend. I don't want the colorbar to be in its own axes, so I want to add it to the existing axes. I'm having difficulties doing this, as most of the example code I have found creates a new axes for the colorbar.
I have tried the following code using matplotlib.colorbar.ColorbarBase, which adds a colorbar to an existing axes, but it gives me strange results and I can't figure out how to specify attributes of the colorbar (for instance, where on the axes it is placed and what size it is)
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.cm import coolwarm
import numpy as np
x = np.random.uniform(1, 10, 10)
y = np.random.uniform(1, 10, 10)
v = np.random.uniform(1, 10, 10)
fig, ax = plt.subplots()
s = ax.scatter(x, y, c=v, cmap=coolwarm)
matplotlib.colorbar.ColorbarBase(ax=ax, cmap=coolwarm, values=sorted(v),
orientation="horizontal")
Using fig.colorbar instead ofmatplotlib.colorbar.ColorbarBase still doesn't give me quite what I want, and I still don't know how to adjust the attributes of the colorbar.
fig.colorbar(s, ax=ax, cax=ax)
Let's say I want to have the colorbar in the top left corner, stretching about halfway across the top of the plot. How would I go about doing that?
Am I better off writing a custom function for this, maybe using LineCollection?
This technique is usually used for multiple axis in a figure. In this context it is often required to have a colorbar that corresponds in size with the result from imshow. This can be achieved easily with the axes grid tool kit:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
data = np.arange(100, 0, -1).reshape(10, 10)
fig, ax = plt.subplots()
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.05)
im = ax.imshow(data, cmap='bone')
fig.colorbar(im, cax=cax, orientation='vertical')
plt.show()
The colorbar has to have its own axes. However, you can create an axes that overlaps with the previous one. Then use the cax kwarg to tell fig.colorbar to use the new axes.
For example:
import numpy as np
import matplotlib.pyplot as plt
data = np.arange(100, 0, -1).reshape(10, 10)
fig, ax = plt.subplots()
cax = fig.add_axes([0.27, 0.8, 0.5, 0.05])
im = ax.imshow(data, cmap='gist_earth')
fig.colorbar(im, cax=cax, orientation='horizontal')
plt.show()
Couldn't add this as a comment, but in case anyone is interested in using the accepted answer with subplots, the divider should be formed on specific axes object (rather than on the numpy.ndarray returned from plt.subplots)
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
data = np.arange(100, 0, -1).reshape(10, 10)
fig, ax = plt.subplots(ncols=2, nrows=2)
for row in ax:
for col in row:
im = col.imshow(data, cmap='bone')
divider = make_axes_locatable(col)
cax = divider.append_axes('right', size='5%', pad=0.05)
fig.colorbar(im, cax=cax, orientation='vertical')
plt.show()
I would like to change the transparency of the grid in matplotlib 3d plot.
But I find that it is not as easy as in 2d, which is simply plt.grid(alpha=0.2).
Here I give a mini code
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(3, 100)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.scatter(data[0], data[1], data[2])
# How to change the grid transparency?
plt.show()
How to set the transparency of the x,y,z-grids?
I have tried:
Using ax.zaxis._axinfo['grid'].update({"alpha": 0.1}). But it appears that it does not have the key alpha.
I checked the source code of ax.grid() here in github. From the comments, it seems that the alpha functionality is not implemented for 3d case at all.
plt.grid does not seem to do anything for 3d plots. But you can set the color as a RGB+Alpha tuple using rcparams:
import numpy as np
import matplotlib.pyplot as plt
# fourth parameter is alpha=0.1
plt.rcParams['grid.color'] = (0.5, 0.5, 0.5, 0.1)
data = np.random.randn(3, 100)
fig = plt.figure()
ax = plt.axes(projection ="3d")
ax.scatter(data[0], data[1], data[2])
plt.show()
Result:
I am using custom colorbar in my plot with following code
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
cmap = matplotlib.cm.get_cmap("hot")
norm = matplotlib.colors.Normalize(vmin=0, vmax=10)
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
fig = plt.figure()
gs = GridSpec(10, 10, figure=fig)
ax = fig.add_subplot(gs[1:, :])
colorbar_ax = fig.add_subplot(gs[0, :])
plt.colorbar(cax=colorbar_ax, mappable=sm, orientation="horizontal",
shrink=0.5)
plt.tight_layout()
plt.show()
This gives me following output,
For some other aspects of my other code, I HAVE to use gridspec. How can I shrink the colorbar by half (or any other fraction)? shrink=0.5 or fraction=0.5 are not working.
From the documentation for colorbar:
The shrink kwarg provides a simple way to scale the colorbar with
respect to the axes. Note that if cax is specified, it determines the
size of the colorbar and shrink and aspect kwargs are ignored.
So you can't use shrink if you are using gridspec.
Of course, gridspec is designed to make sizing of subplot axes easy, so we can use that to define the colorbar axes size. Since you already have a 10x10 grid defined by gridspec, we could just use the middle portion in the x-direction; For example, you could change to something like this to shorten your colorbar:
colorbar_ax = fig.add_subplot(gs[0, 3:7])
When plotting matrix with imshow in Matplotlib, how to change colorbar legend bar size, location, font and other parameters?
Here I created an example code
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def plot_matrix(mat, title='example', cmap=plt.cm.Blues):
plt.imshow(mat, interpolation='nearest', cmap=cmap)
plt.grid(False)
plt.title(title)
plt.colorbar()
data = np.random.random((20, 20))
plt.figure(figsize=(8,8))
plt.tick_params(axis='both', which='major', labelsize=12)
plot_matrix(data)
In a real use case, I got complex labels and the legend bar becomes much higher then the matrix itself. I want to change the legend bar to make the plot more efficiently use the space.
I found a documentation for the matplotlib.pyplot.colorbar, however have not figure out a good way to set the size, location and font size for the color legend bar.
imshow enforces a 1:1 aspect (by default, but you can change it with aspect parameter), which makes things a little trickier. To always get consistent result, I might suggest manually specify the size of axes:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def plot_matrix(mat, figsize, title='example', cmap=plt.cm.Blues):
f = plt.figure(figsize=figsize)
ax = plt.axes([0, 0.05, 0.9, 0.9 ]) #left, bottom, width, height
#note that we are forcing width:height=1:1 here,
#as 0.9*8 : 0.9*8 = 1:1, the figure size is (8,8)
#if the figure size changes, the width:height ratio here also need to be changed
im = ax.imshow(mat, interpolation='nearest', cmap=cmap)
ax.grid(False)
ax.set_title(title)
cax = plt.axes([0.95, 0.05, 0.05,0.9 ])
plt.colorbar(mappable=im, cax=cax)
return ax, cax
data = np.random.random((20, 20))
ax, cax = plot_matrix(data, (8,8))
Now you have the axis where the colorbar is plotted in, cax. You can do a lot of thing with that, say, rotate the labels, using plt.setp(cax.get_yticklabels(), rotation=45)
The code below produces gaps between the subplots. How do I remove the gaps between the subplots and make the image a tight grid?
import matplotlib.pyplot as plt
for i in range(16):
i = i + 1
ax1 = plt.subplot(4, 4, i)
plt.axis('on')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_aspect('equal')
plt.subplots_adjust(wspace=None, hspace=None)
plt.show()
The problem is the use of aspect='equal', which prevents the subplots from stretching to an arbitrary aspect ratio and filling up all the empty space.
Normally, this would work:
import matplotlib.pyplot as plt
ax = [plt.subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
plt.subplots_adjust(wspace=0, hspace=0)
The result is this:
However, with aspect='equal', as in the following code:
import matplotlib.pyplot as plt
ax = [plt.subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
a.set_aspect('equal')
plt.subplots_adjust(wspace=0, hspace=0)
This is what we get:
The difference in this second case is that you've forced the x- and y-axes to have the same number of units/pixel. Since the axes go from 0 to 1 by default (i.e., before you plot anything), using aspect='equal' forces each axis to be a square. Since the figure is not a square, pyplot adds in extra spacing between the axes horizontally.
To get around this problem, you can set your figure to have the correct aspect ratio. We're going to use the object-oriented pyplot interface here, which I consider to be superior in general:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,8)) # Notice the equal aspect ratio
ax = [fig.add_subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
a.set_aspect('equal')
fig.subplots_adjust(wspace=0, hspace=0)
Here's the result:
You can use gridspec to control the spacing between axes. There's more information here.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.figure(figsize = (4,4))
gs1 = gridspec.GridSpec(4, 4)
gs1.update(wspace=0.025, hspace=0.05) # set the spacing between axes.
for i in range(16):
# i = i + 1 # grid spec indexes from 0
ax1 = plt.subplot(gs1[i])
plt.axis('on')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_aspect('equal')
plt.show()
Without resorting gridspec entirely, the following might also be used to remove the gaps by setting wspace and hspace to zero:
import matplotlib.pyplot as plt
plt.clf()
f, axarr = plt.subplots(4, 4, gridspec_kw = {'wspace':0, 'hspace':0})
for i, ax in enumerate(f.axes):
ax.grid('on', linestyle='--')
ax.set_xticklabels([])
ax.set_yticklabels([])
plt.show()
plt.close()
Resulting in:
With recent matplotlib versions you might want to try Constrained Layout. This does (or at least did) not work with plt.subplot() however, so you need to use plt.subplots() instead:
fig, axs = plt.subplots(4, 4, constrained_layout=True)
Have you tried plt.tight_layout()?
with plt.tight_layout()
without it:
Or: something like this (use add_axes)
left=[0.1,0.3,0.5,0.7]
width=[0.2,0.2, 0.2, 0.2]
rectLS=[]
for x in left:
for y in left:
rectLS.append([x, y, 0.2, 0.2])
axLS=[]
fig=plt.figure()
axLS.append(fig.add_axes(rectLS[0]))
for i in [1,2,3]:
axLS.append(fig.add_axes(rectLS[i],sharey=axLS[-1]))
axLS.append(fig.add_axes(rectLS[4]))
for i in [1,2,3]:
axLS.append(fig.add_axes(rectLS[i+4],sharex=axLS[i],sharey=axLS[-1]))
axLS.append(fig.add_axes(rectLS[8]))
for i in [5,6,7]:
axLS.append(fig.add_axes(rectLS[i+4],sharex=axLS[i],sharey=axLS[-1]))
axLS.append(fig.add_axes(rectLS[12]))
for i in [9,10,11]:
axLS.append(fig.add_axes(rectLS[i+4],sharex=axLS[i],sharey=axLS[-1]))
If you don't need to share axes, then simply axLS=map(fig.add_axes, rectLS)
Another method is to use the pad keyword from plt.subplots_adjust(), which also accepts negative values:
import matplotlib.pyplot as plt
ax = [plt.subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
plt.subplots_adjust(pad=-5.0)
Additionally, to remove the white at the outer fringe of all subplots (i.e. the canvas), always save with plt.savefig(fname, bbox_inches="tight").