I need to generate a plot with equal aspect in both axis and a colorbar to the right. I've tried setting aspect='auto', aspect=1, and aspect='equal' with no good results. See below for examples and the MWE.
Using aspect='auto' the colorbars are of the correct height but the plots are distorted:
Using aspect=1 or aspect='equal' the plots are square (equal aspect in both axis) but the colorbars are distorted:
In both plots the colorbars are positioned too far to the right for some reason. How can I get a square plot with colorbars of matching heights?
MWE
import numpy as np
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
def col_plot(params):
gs, i, data = params
xarr, yarr, zarr = zip(*data)[0], zip(*data)[1], zip(*data)[2]
xmin, xmax = min(xarr), max(xarr)
ymin, ymax = min(yarr), max(yarr)
#plt.subplot(gs[i], aspect='auto')
plt.subplot(gs[i], aspect=1)
#plt.subplot(gs[i], aspect='equal')
plt.xlim(xmin, xmax)
plt.ylim(xmin, xmax)
plt.xlabel('$x axis$', fontsize=20)
plt.ylabel('$y axis$', fontsize=20)
# Scatter plot.
cm = plt.cm.get_cmap('RdYlBu_r')
SC = plt.scatter(xarr, yarr, marker='o', c=zarr, s=60, lw=0.25, cmap=cm,
zorder=3)
# Colorbar.
ax0 = plt.subplot(gs[i + 1])
cbar = plt.colorbar(SC, cax=ax0)
cbar.set_label('$col bar$', fontsize=21, labelpad=-2)
# Generate data.
data0 = np.random.uniform(0., 1., size=(50, 3))
data1 = np.random.uniform(0., 1., size=(50, 3))
# Create the top-level container
fig = plt.figure(figsize=(14, 25))
gs = gridspec.GridSpec(4, 4, width_ratios=[1, 0.05, 1, 0.05])
# Generate plots.
par_lst = [[gs, 0, data0], [gs, 2, data1]]
for pl_params in par_lst:
col_plot(pl_params)
# Output png file.
fig.tight_layout()
plt.savefig('colorbar_aspect.png', dpi=300)
You can use an AxesDivider to do that. I have modified your code a bit to make use of an AxesDivider.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
def col_plot(data):
xarr, yarr, zarr = zip(*data)[0], zip(*data)[1], zip(*data)[2]
xarr = [2*x for x in xarr]
xmin, xmax = min(xarr), max(xarr)
ymin, ymax = min(yarr), max(yarr)
fig = plt.figure()
ax0 = fig.add_subplot(111, aspect='equal')
plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)
plt.xlabel('$x axis$', fontsize=20)
plt.ylabel('$y axis$', fontsize=20)
# Scatter plot.
cm = plt.cm.get_cmap('RdYlBu_r')
SC = ax0.scatter(xarr, yarr, marker='o', c=zarr, s=60, lw=0.25, cmap=cm,
zorder=3)
the_divider = make_axes_locatable(ax0)
color_axis = the_divider.append_axes("right", size="5%", pad=0.1)
# Colorbar.
cbar = plt.colorbar(SC, cax=color_axis)
cbar.set_label('$col bar$', fontsize=21, labelpad=-2)
# Generate data.
data0 = np.random.uniform(0., 1., size=(20, 3))
col_plot(data0)
And here is the result (I changed your data so it spans a range of [0, 2] in the x-direction for demonstration purposes):
On Joseph Long's blog there is the following nice solution.
1) Define a colorbar function as:
from mpl_toolkits.axes_grid1 import make_axes_locatable
def colorbar(mappable):
ax = mappable.axes
fig = ax.figure
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
return fig.colorbar(mappable, cax=cax)
2) Call colorbar(thing) when you want to make a colorbar. In your case:
SC = ax0.scatter(xarr, yarr, marker='o', c=zarr, s=60, lw=0.25, cmap=cm,
zorder=3)
colorbar(SC)
3) And you get:
Related
I saw this tutorial on how to make a scatter plot with a histogram for the x and y axes and I thought it would be neat to also tack on a colorbar for an extra dimension of information. To do this, I utilized "the make_axes_locatable" function, like so:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# generating fake data
tx = np.random.randn(1000)
ty = np.random.randn(1000)
tz = np.random.randn(1000)
fig = plt.figure(figsize=(5, 5))
gs = fig.add_gridspec(2, 2, width_ratios=(4, 1), height_ratios=(1, 4),
left=0.1, right=0.9, bottom=0.1, top=0.9,
wspace=0.05, hspace=0.05)
# Create the Axes.
ax = fig.add_subplot(gs[1, 0])
ax_histx = fig.add_subplot(gs[0, 0], sharex=ax)
ax_histy = fig.add_subplot(gs[1, 1], sharey=ax)
def scatter_hist_and_colorbar(x, y, c, ax, ax_histx, ax_histy,label):
# no labels
ax_histx.tick_params(axis="x", labelbottom=False)
ax_histy.tick_params(axis="y", labelleft=False)
# the scatter plot:
sc=ax.scatter(x,y,marker='o',label=label,c=c)
# now determine nice limits by hand:
binwidth = 0.25
xlim = (int(np.max(np.abs(x))/binwidth) + 1) * binwidth
ylim = (int(np.max(np.abs(y))/binwidth) + 1) * binwidth
xbins = np.arange(-xlim, xlim + binwidth, binwidth)
ybins = np.arange(-ylim, ylim + binwidth, binwidth)
ax_histx.hist(x, bins=xbins)
ax_histy.hist(y, bins=ybins, orientation='horizontal')
return sc
sc1= scatter_hist_and_colorbar(tx,ty,tz, ax, ax_histx, ax_histy,label='data')
ax.set_ylabel('x data')
ax.set_xlabel('y data')
ax.legend()
divider = make_axes_locatable(ax)
cax = divider.append_axes('left', size='5%', pad=1)
cbar=fig.colorbar(sc1, cax=cax, orientation='vertical')
cbar.ax.set_ylabel('z data',rotation=90,labelpad=5)
cbar.ax.yaxis.set_ticks_position("left")
plt.savefig('example.png')
plt.show()][2]][2]
This almost works except the "ax_histx" axis is now stretched and doesn't properly line up due to the addition of the colorbar. Is there a way to resize the "ax_histx" axis or is there a better way to add a colorbar to the "ax" subplot so that it wouldn't affect the "ax_histx" or "ax_histy" axes?
After getting a suggestion form #r-beginners , I tried tweaking this code to place a colorbar in the upper right, perpendicular to the histogram axes. This way, it doesn't distort the width/heights of the other shared axes:
# some random data
tx = np.random.randn(1000)
ty = np.random.randn(1000)
tz = np.random.randn(1000)
fig = plt.figure(figsize=(5, 5))
gs = fig.add_gridspec(2, 2, width_ratios=(4, 1), height_ratios=(1, 4),
left=0.1, right=0.9, bottom=0.1, top=0.9,
wspace=0.05, hspace=0.05)
# Create the Axes.
ax0 = fig.add_subplot(gs[0, 1])
ax = fig.add_subplot(gs[1, 0])
ax_histx = fig.add_subplot(gs[0, 0], sharex=ax)
ax_histy = fig.add_subplot(gs[1, 1], sharey=ax)
def scatter_hist_and_colorbar(x, y, c, ax, ax_histx, ax_histy,label):
# no labels
ax_histx.tick_params(axis="x", labelbottom=False)
ax_histy.tick_params(axis="y", labelleft=False)
# the scatter plot:
sc=ax.scatter(x,y,marker='o',label=label,c=c)
# now determine nice limits by hand:
binwidth = 0.25
xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
lim = (int(xymax/binwidth) + 1) * binwidth
xlim = (int(np.max(np.abs(x))/binwidth) + 1) * binwidth
ylim = (int(np.max(np.abs(y))/binwidth) + 1) * binwidth
xbins = np.arange(-xlim, xlim + binwidth, binwidth)
ybins = np.arange(-ylim, ylim + binwidth, binwidth)
ax_histx.hist(x, bins=xbins)
ax_histy.hist(y, bins=ybins, orientation='horizontal')
return sc
sc1= scatter_hist_and_colorbar(tx,ty,tz, ax, ax_histx, ax_histy,label='data')
ax.set_ylabel('x data')
ax.set_xlabel('y data')
ax.legend()
divider = make_axes_locatable(ax)
divider = make_axes_locatable(ax0)
ca = divider.append_axes('left', size='50%')
ax0.axis('off')
cbar=fig.colorbar(sc1, cax=ca, orientation='vertical')
cbar.ax.set_ylabel('z data',rotation=270,labelpad=5)
cbar.ax.yaxis.set_ticks_position("right")
gs.tight_layout(fig,pad=1)
plt.savefig('example.png')
plt.show()
I use the following code to generate side-by-size images and I need to add colorbar only to the second image in the row. I use the following code for it
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import matplotlib.gridspec as gridspec
def plotting(x):
gs1 = gridspec.GridSpec(1, 2)
gs1.update(wspace=0.005, hspace=0.005)
plt.subplot(gs1[0])
plt.imshow(x)
plt.axis('off')
plt.title('dog')
ax1 = plt.subplot(gs1[1])
imc = plt.imshow(x, cmap='hot', interpolation='nearest')
plt.axis('off')
plt.title('dog')
divider = make_axes_locatable(ax1)
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(imc, cax=cax)
plt.tight_layout()
plt.show()
However it comes out the size of side-by-side images are not equal. I wonder how I could fix this issue?
You can use ImageGrid, which was created exactly for this purpose:
from mpl_toolkits.axes_grid1 import ImageGrid
x = np.random.random(size=(10,10))
fig = plt.figure()
grid = ImageGrid(fig, 111,
nrows_ncols = (1,2),
axes_pad = 0.05,
cbar_location = "right",
cbar_mode="single",
cbar_size="5%",
cbar_pad=0.05
)
grid[0].imshow(x)
grid[0].axis('off')
grid[0].set_title('dog')
imc = grid[1].imshow(x, cmap='hot', interpolation='nearest')
grid[1].axis('off')
grid[1].set_title('dog')
plt.colorbar(imc, cax=grid.cbar_axes[0])
I have the following code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
arr = np.random.randint(0, 100, (2, 3, 4))
fig, ax = plt.subplots(1, 1)
pax = ax.imshow(arr, vmin=0, vmax=100)
cbar_kws=dict(ticks=(0, 100))
cbar_txt='arb. units'
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.05)
cbar = ax.figure.colorbar(pax, cax=cax, **dict(cbar_kws))
# cbar = ax.figure.colorbar(plot, ax=ax, **dict(cbar_kws))
if cbar_txt is not None:
only_extremes = 'ticks' in cbar_kws and len(cbar_kws['ticks']) == 2
if only_extremes:
cbar.ax.text(
2.0, 0.5, cbar_txt, fontsize='medium', rotation=90,
va='center', ha='left')
else:
cbar.set_label(cbar_txt)
plt.tight_layout()
plt.show()
This works fine for Matplotlib 2.2.3 where I get a text in the middle of the colorbar (on the right):
But does not work the same way for Matplotlib 3.0.1, where the text gets rendered at the bottom of the colorbar:
Why? Any suggestion for obtaining the same behavior with both versions?
How
Using cbar.ax.text seems to be a workaround for some other problem. The recommended way to set a label to the colorbar is either via the colorbar call itself, or via cbar.set_label("label").
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
arr = np.random.randint(0, 100, (2, 3))
fig, ax = plt.subplots(1, 1)
pax = ax.imshow(arr, vmin=0, vmax=100)
cbar_kws=dict(ticks=(0, 100))
cbar_txt='arb. units'
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.05)
cbar = ax.figure.colorbar(pax, cax=cax, **dict(cbar_kws))
cbar.set_label(cbar_txt, labelpad=-12)
plt.tight_layout()
plt.show()
The result is the same in matplotlib 2.2.3 and 3.0.1:
To have the label distance independent of the length of the colorbar labels you may label the left side of the colorbar and shift the label even more.
cbar.set_label(cbar_txt, labelpad=-36)
cbar.ax.yaxis.set_label_position("left")
Finally, you may indeed use a text on the axes, but position it in axes coordinates instead of data coordinates,
cbar.ax.text(2, 0.5, cbar_txt, fontsize='medium', rotation=90,
va='center', ha='left', transform=cbar.ax.transAxes)
Why
As to why cbar.ax.text works differently between the versions: The internal units of the colorbar have changed. This shouldn't affect any external application, but makes it easier to apply different locators to colorbars. In fact it has become more consistent. E.g. if the colorbar range is 0 to 100, and you place a text at y=0.5, it'll appear very close to 0.
Why not use the label directly? Edit: didn't see answer below. See for better explanation.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
arr = np.random.randint(0, 100, (2, 3, 4))
fig, ax = plt.subplots(1, 1)
pax = ax.imshow(arr, vmin=0, vmax=100)
cbar_txt='arb. units'
cbar_kws=dict(ticks=(0, 100))
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.05)
cbar = ax.figure.colorbar(pax, cax=cax, **dict(cbar_kws))
cbar.set_label(cbar_txt, size = 20)
cbar.ax.tick_params(labelsize = 10)
plt.tight_layout()
plt.show()
I'm trying to generate a plot with several axis, each one with their own colorbar (code below). If I use the default colorbar plotting I get too much horizontal spacing between the plot and the colorbar:
If I try to use the make_axes_locatable() method I get this horrible result:
What is going on and how can I fix this?
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.gridspec as gridspec
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Random data to plot
data = np.random.uniform(0., 1., (2, 100))
z = np.random.uniform(0., 10., 100)
# Define figure
fig = plt.figure(figsize=(30, 30))
gs = gridspec.GridSpec(12, 12)
for i in range(2):
ax = plt.subplot(gs[2 * i:2 + (2 * i), 0:8])
SC = plt.scatter(*data, c=z)
# Colorbar 1
cbar = plt.colorbar()
# Colorbar 2
# the_divider = make_axes_locatable(ax)
# color_axis = the_divider.append_axes("right", size="1%", pad=0.)
# cbar = plt.colorbar(SC, cax=color_axis)
cbar.set_label("test", fontsize=15, labelpad=10)
fig.tight_layout()
plt.savefig('test.png', dpi=300, bbox_inches='tight')
Use the pad argument of colorbar to set the padding between the axes and the colorbar. pad is given in units of the fraction of the original axes' size to use as space. Here e.g. pad=0.01 might make sense.
import numpy as np
from matplotlib import pyplot as plt
# Random data to plot
data = np.random.uniform(0., 1., (2, 100))
z = np.random.uniform(0., 10., 100)
# Define figure
fig, axes = plt.subplots(nrows=2, figsize=(30, 30))
for i, ax in enumerate(axes.flat):
sc = ax.scatter(*data, c=z)
cbar = fig.colorbar(sc, ax=ax, pad=0.01)
cbar.set_label("test", fontsize=15, labelpad=10)
fig.tight_layout()
plt.savefig('test.png', dpi=300, bbox_inches='tight')
I am trying to create a density plot with a given data and using log scales in the two axes x,y, using the version of Matplotlib 2.0.0. I have made the following code, the problem is that for the log plot case don't give the correct functional behaviour.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
init = 0.0
points = 500
final_value = 100
steep = (final_value-init)/points
list_values_x = np.arange(init,final_value,steep)
list_values_y = np.arange(init,final_value,steep)
#WE CREATE OUT DATA FILE
f1 = open("data.txt", "w")
for i in list_values_x:
for j in list_values_y:
f1.write( str(i) +" "+str(j)+" "+str(0.0001*(i**2+j**2)) +"\n")
f1.close()
#NOW WE OPEN THE FILE WITH THE DATA AND MAKE THE PLOT
x,y,temp = np.loadtxt('data.txt').T #Transposed for easier unpacking
nrows, ncols = points, points
grid = temp.reshape((nrows, ncols))
# LINEAR PLOT
fig1 = plt.imshow(grid, extent=(x.min(), x.max(), y.max(), y.min()),
interpolation='nearest', cmap=cm.gist_rainbow)
plt.axis([x.min(), x.max(),y.min(), y.max()])
plt.colorbar()
plt.suptitle('Example', fontsize=15)
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.show()
# LOG-LOG PLOT
fig, (ax1) = plt.subplots(ncols=1, figsize=(8, 4))
ax1.imshow(grid, aspect="auto", extent=(1, 1e2, 1, 1e2), interpolation='nearest')
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_title('Example with log scale')
plt.show()
The data that I am using in order to make the plot is irrelevant, it's just an example. So that, the first plot is given with a linear scale. The second plot is given with a log-log scale, but is clear that it's incorrect, the behaviour beetwen the two plots is absolutely different and I am using the same data. Moreover, I don't know how put a colorbar in the log-log plot
Any idea why this happens? Thanks for your attention.
PD: In order to build the log-log plot, I have used part of the code that apears in "Non-linear scales on image plots" given in (http://matplotlib.org/devdocs/users/whats_new.html#non-linear-scales-on-image-plots)
Using the extent keyword and it with extent=(xmin, xmax, ymin, ymax) makes more sense when additionally using origin="lower" in imshow. You might also want to set the limits for the axes, since the automatic feature does not work too well for log scales.
Here is the complete example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from mpl_toolkits.axes_grid1 import make_axes_locatable
points = 500
init = 0.0
final_value = 100
steep = (final_value-init)/points
x = np.arange(init,final_value,steep)
y = np.arange(init,final_value,steep)
X,Y = np.meshgrid(x,y)
Z = 0.0001*(X**2+Y**2)
fig, (ax, ax1) = plt.subplots(ncols=2, figsize=(8, 4))
# LINEAR PLOT
im = ax.imshow(Z, extent=(x.min(), x.max(), y.min(), y.max() ),
interpolation='nearest', cmap=cm.gist_rainbow, origin="lower")
ax.set_title('lin scale')
#make colorbar
divider = make_axes_locatable(ax)
ax_cb = divider.new_horizontal(size="5%", pad=0.05)
fig.add_axes(ax_cb)
fig.colorbar(im, cax = ax_cb, ax=ax)
# LOG-LOG PLOT
im1 = ax1.imshow(Z, extent=(1, 1e2, 1, 1e2),
interpolation='nearest',cmap=cm.gist_rainbow, origin="lower")
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_xlim([1, x.max()])
ax1.set_ylim([1, y.max()])
ax1.set_title('log scale')
#make colorbar
divider1 = make_axes_locatable(ax1)
ax_cb1 = divider1.new_horizontal(size="5%", pad=0.05)
fig.add_axes(ax_cb1)
fig.colorbar(im1, cax = ax_cb1, ax=ax1)
plt.tight_layout()
plt.show()