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])
Related
I have a matplotlib bar chart, which bars are colored according to some rules through a colormap. I need a colorbar on the right of the main axes, so I added a new axes with
fig, (ax, ax_cbar) = plt.subplots(1,2)
and managed to draw my color bar in the ax_bar axes, while I have my data displayed in the ax axes. Now I need to reduce the width of the ax_bar, because it looks like this:
How can I do?
Using subplots will always divide your figure equally. You can manually divide up your figure in a number of ways. My preferred method is using subplot2grid.
In this example, we are setting the figure to have 1 row and 10 columns. We then set ax to be the start at row,column = (0,0) and have a width of 9 columns. Then set ax_cbar to start at (0,9) and has by default a width of 1 column.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
num_columns = 10
ax = plt.subplot2grid((1,num_columns), (0,0), colspan=num_columns-1)
ax_cbar = plt.subplot2grid((1,num_columns), (0,num_columns-1))
The ususal way to add a colorbar is by simply putting it next to the axes:
fig.colorbar(sm)
where fig is the figure and sm is the scalar mappable to which the colormap refers. In the case of the bars, you need to create this ScalarMappable yourself. Apart from that there is no need for complex creation of multiple axes.
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
fig , ax = plt.subplots()
x = [0,1,2,3]
y = np.array([34,40,38,50])*1e3
norm = matplotlib.colors.Normalize(30e3, 60e3)
ax.bar(x,y, color=plt.cm.plasma_r(norm(y)) )
ax.axhline(4.2e4, color="gray")
ax.text(0.02, 4.2e4, "42000", va='center', ha="left", bbox=dict(facecolor="w",alpha=1),
transform=ax.get_yaxis_transform())
sm = plt.cm.ScalarMappable(cmap=plt.cm.plasma_r, norm=norm)
sm.set_array([])
fig.colorbar(sm)
plt.show()
If you do want to create a special axes for the colorbar yourself, the easiest method would be to set the width already inside the call to subplots:
fig , (ax, cax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios" : [10,1]})
and later put the colorbar to the cax axes,
fig.colorbar(sm, cax=cax)
Note that the following questions have been asked for this homework assignment already:
Point picker event_handler drawing line and displaying coordinates in matplotlib
Matplotlib's widget to select y-axis value and change barplot
Display y axis value horizontal line drawn In bar chart
How to change colors automatically once a parameter is changed
Interactively Re-color Bars in Matplotlib Bar Chart using Confidence Intervals
I'm trying to save an image I generated using seaborn. The image is 4x4 confusion matrix ('confmat' np.array). I learned that when I save the image in vector format, certain viewers have issues resulting in white lines on colorbar, quoting from matplotlib reference:
It is known that some vector graphics viewer (svg and pdf) renders
white gaps between segments of the colorbar. This is due to bugs in
the viewers not matplotlib. As a workaround the colorbar can be
rendered with overlapping segments:
cbar = colorbar()
cbar.solids.set_edgecolor("face")
draw()
However, I have trouble doing what is suggested.
Here is what I did:
import seaborn as sns
import matplotlib.pyplot as plt
cmap=plt.cm.Blues
fig, ax = plt.subplots()
ax = sns.heatmap(confmat, annot=True, cmap=cmap)
ax.set_title('title')
ax.tick_params(
axis='both', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom='off', # ticks along the bottom edge are off
top='off', # ticks along the top edge are off
labelbottom='off', # labels along the bottom edge are off
labelleft='off',
right='off')
fig.savefig('confusion_matrix.svg', format='svg')
I tried to get colorbar using
cbar = ax.colorbar()
But get an error AttributeError: 'AxesSubplot' object has no attribute 'colorbar'.
I searched for solution and found a few questions here that suggest using plt.imshow() to get the colorbar object, but I'm completely confused about what I'm doing by now.
Can someone suggest, and if possible, explain why, the solution for implementing what matplotlib documentation has offered for colorbar?
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
x = np.random.randn(10, 10)
f, ax = plt.subplots()
sns.heatmap(x)
cbar_ax = f.axes[-1]
cbar_solids = cbar_ax.collections[0]
cbar_solids.set_edgecolor("face")
f.savefig("heatmap.svg")
Changing a colorbar with cb.solid.set_edgecolor("face") as suggested in the matplotlib docs appears to be a bit of a hack to ensure there are no white lines between elements on the colorbar. I think seaborn is designed assuming you should be able to do everything you need by passing kwargs (cbar_kws in heatmap). For example, you can pass cb_kwargs to the sns.heatmap function cbar_kws={"drawedges": "False"} but unfortunately this doesn't fix the problem.
As the Seaborn Heatmap only returns an axis handle on which the heatplot and the colorbar are plotted, you don't have direct access to the mappable object, cbar in the source code. As a result you can't apply this hack.
One solution is to just plot this using pcolormesh and colorbar. I think seaborn actually redefines matplotlib styles so should look the same,
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
cmap=plt.cm.Blues
fig, ax = plt.subplots()
confmat = np.random.rand(4, 4)
cb = ax.pcolormesh(confmat, cmap=cmap)
ax.set_title('title')
ax.tick_params(
axis='both', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom='off', # ticks along the bottom edge are off
top='off', # ticks along the top edge are off
labelbottom='off', # labels along the bottom edge are off
labelleft='off',
right='off')
cbar = plt.colorbar(cb)
cbar.solids.set_edgecolor("face")
plt.draw()
fig.savefig('confusion_matrix.svg', format='svg')
The result for me looks to be rid of the white lines when you zoom in.
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").
I am trying to produce a scatter plot that has two different y-axes and also a colorbar.
Here is the pseudo-code used:
#!/usr/bin/python
import matplotlib.pyplot as plt
from matplotlib import cm
fig = plt.figure()
ax1 = fig.add_subplot(111)
plt.scatter(xgrid,
ygrid,
c=be, # set colorbar to blaze efficiency
cmap=cm.hot,
vmin=0.0,
vmax=1.0)
cbar = plt.colorbar()
cbar.set_label('Blaze Efficiency')
ax2 = ax1.twinx()
ax2.set_ylabel('Wavelength')
plt.show()
And it produces this plot:
My question is, how do you use a different scale for the "Wavelength" axes, and also, how do you move the colorbar more to right so that it is not in the Wavelength's way?
#OZ123 Sorry that I took so long to respond. Matplotlib has extensible customizability, sometimes to the point where you get confused to what you are actually doing. Thanks for the help on creating separate axes.
However, I didn't think I needed that much control, and I ended up just using the PAD keyword argument in
fig.colorbar()
and this provided what I needed.
The pseudo-code then becomes this:
#!/usr/bin/python
import matplotlib.pyplot as plt
from matplotlib import cm
fig = plt.figure()
ax1 = fig.add_subplot(111)
mappable = ax1.scatter(xgrid,
ygrid,
c=be, # set colorbar to blaze efficiency
cmap=cm.hot,
vmin=0.0,
vmax=1.0)
cbar = fig.colorbar(mappable, pad=0.15)
cbar.set_label('Blaze Efficiency')
ax2 = ax1.twinx()
ax2.set_ylabel('Wavelength')
plt.show()
Here is to show what it looks like now::
the plt.colorbar() is made for really simple cases, e.g. not really thought for a plot with 2 y-axes.
For a fine grained control of the colorbar location and properties you should almost always rather work with colorbar specifying on which axes you want to plot the colorbar.
# on the figure total in precent l b w , height
cbaxes = fig.add_axes([0.1, 0.1, 0.8, 0.05]) # setup colorbar axes.
# put the colorbar on new axes
cbar = fig.colorbar(mapable,cax=cbaxes,orientation='horizontal')
Note that colorbar takes the following keywords:
keyword arguments:
cax
None | axes object into which the colorbar will be drawn ax
None | parent axes object from which space for a new
colorbar axes will be stolen
you could also see here a more extended answer of mine regarding figure colorbar on separate axes.