While I managed to put a plot inside a plot (see the question here), I am finding trouble putting a colorbar to the larger (outside) plot. The code below is as simple as it gets, but for some reason it places the colorbar in the wrong axis:
import numpy as np
from numpy import random
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Canvas
fig, ax1 = plt.subplots(figsize=(12, 10))
left, bottom, width, height = [0.65, 0.15, 0.32, 0.30]
ax2 = fig.add_axes([left, bottom, width, height])
# Labels
xlabel = 'x'
ylabel = 'y'
cbarlabel = 'Color'
cmap = plt.get_cmap('turbo')
# Data
x, y, z = np.random.rand(3,200)
# Plotting
sc = ax1.scatter(x, y, marker='o', c=z, cmap=cmap)
ax2.scatter(x, y, c=z, cmap=cmap)
#
ax1.set_xlabel(xlabel)
ax1.set_ylabel(ylabel)
ax1.legend(fontsize=12, loc='upper left')
plt.tight_layout()
# Colormap
ax1 = plt.gca()
divider = make_axes_locatable(plt.gca())
cax = divider.append_axes("right", "2%", pad="1%")
cbar = plt.colorbar(sc, cax=cax) # Colorbar
cbar.set_label(cbarlabel, rotation=270, labelpad=30)
sc.set_clim(vmin=min(z), vmax=max(z))
#
plt.show()
I have also tried inset_axes as in the documentation example, to no avail.
The trick is to actually set active axes with plt.sca(ax1) and then create colorbar. I also simplified a code little bit.
Here is modified code putting colormap to the large plot:
import matplotlib.pyplot as plt
import numpy as np
from numpy import random
# Canvas
fig, ax1 = plt.subplots(figsize=(12, 10))
left, bottom, width, height = [0.45, 0.15, 0.32, 0.30]
ax2 = fig.add_axes([left, bottom, width, height])
# Labels
xlabel = 'x'
ylabel = 'y'
cbarlabel = 'Color'
cmap = plt.get_cmap('turbo')
# Data
x, y, z = np.random.rand(3,200)
# Plotting
sc = ax1.scatter(x, y, marker='o', c=z, cmap=cmap)
ax2.scatter(x, y, c=z, cmap=cmap)
# Set active axes
plt.sca(ax1)
cbar = plt.colorbar(sc) # Colorbar
cbar.set_label(cbarlabel, rotation=270, labelpad=30)
sc.set_clim(vmin=min(z), vmax=max(z))
#
ax1.set_xlabel(xlabel)
ax1.set_ylabel(ylabel)
ax1.legend(fontsize=12, loc='upper left')
plt.tight_layout()
plt.show()
Resulting in:
Related
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_xlim([0, 1])
plt.show()
results in the following plot:
There is a small margin between the axis and there limits.
How can I get rid of this offset of the axis.
For example ax.set_xlim([0, 1]) has not effect!
We can change the height and width and position of colorbar by using :
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
fig = plt.figure()
ax = plt.subplot(111)
im = ax.imshow(np.arange(100).reshape((10, 10)))
c = plt.colorbar(im, cax = fig.add_axes([0.78, 0.5, 0.03, 0.38]))
from here,
I am going to use this in subplots with add_axes and transorm:
fig, axs = plt.subplots(nrows=1, ncols=2)
im0 = axs[0].imshow(np.arange(100).reshape((10, 10)), cmap='afmhot')
c = plt.colorbar(im0, cax=fig.add_axes([0.45, 0.52, 0.03, 0.2],
transform=axs[0].transAxes))
# transform=axs[0].transAxes) does not make any difference
im1 = axs[1].imshow(np.arange(100).reshape((10, 10)), cmap='afmhot_r')
c = plt.colorbar(im1, cax=fig.add_axes([0.87, 0.52, 0.03, 0.2]))
I am going to use transform (transAxes) option to set the location from axes not the figure, but it does not work.
First of all, from the add_axes documentation,
rect : sequence of float
The dimensions [left, bottom, width, height] of the new axes. All quantities are in
fractions of figure width and height.
This is the reason your code doesn't work.
You may instead use an inset_axes.
inset_axes(self, bounds, transform=None, ...)
bounds : [x0, y0, width, height]
Lower-left corner of inset axes, and its width and height.
transform : Transform
Defaults to ax.transAxes, i.e. the units of rect are in axes-relative coordinates.
Here the bounds default to units of axes coordinates, but can be changed if needed.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
im = ax.imshow(np.arange(100).reshape((10, 10)))
cax = ax.inset_axes([0.78, 0.5, 0.03, 0.38])
cb = fig.colorbar(im, cax = cax)
plt.show()
An alternative to the above is to use mpl_toolkits.axes_grid1.inset_locator.inset_axes.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
import numpy as np
fig, ax = plt.subplots()
im = ax.imshow(np.arange(100).reshape((10, 10)))
cax = inset_axes(ax, "100%", "100%", bbox_to_anchor=[0.78, 0.5, 0.03, 0.38],
bbox_transform=ax.transAxes, borderpad=0)
cb = fig.colorbar(im, cax = cax)
plt.show()
I am plotting a function on the surface of a sphere. To test my code, I simply plot the spherical coordinate phi divided by pi. I get
Unexpectedly, half of the sphere is of the same color, and the colors on the other half aren't correct (at phi=pi, i should get 1, not 2). If I divide the data array by 2, the problem disappears. Can someone explain to me what is happening?
Here is the code I use:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
# prepare the sphere surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
phi = np.linspace(0,2*np.pi, 50)
theta = np.linspace(0, np.pi, 25)
x=np.outer(np.cos(phi), np.sin(theta))
y=np.outer(np.sin(phi), np.sin(theta))
z=np.outer(np.ones(np.size(phi)), np.cos(theta))
# prepare function to plot
PHI=np.outer(phi,np.ones(np.size(theta)))
THETA=np.outer(np.ones(np.size(phi)),theta)
data = PHI/np.pi
# plot
surface=ax.plot_surface(x, y, z, cstride=1, rstride=1,
facecolors=cm.jet(data),cmap=plt.get_cmap('jet'))
# add colorbar
m = cm.ScalarMappable(cmap=surface.cmap,norm=surface.norm)
m.set_array(data)
plt.colorbar(m)
plt.show()
There is a little bit of chaos in the code.
When specifying facecolors, there is no reason to supply a colormap, because the facecolors do not need to be retrieved from a colormap.
Colormaps range from 0 to 1. Your data ranges from 0 to 2. Hence half of the facecolors are just the same. So you first need to normalize the data to the (0,1)-range, e.g. using a Normalize instance, then you can apply the colormap.
norm = plt.Normalize(vmin=data.min(), vmax=data.max())
surface=ax.plot_surface(x, y, z, cstride=1, rstride=1,
facecolors=cm.jet(norm(data)))
For the colorbar you should then use the same colormap and the same normalization as for the plot itself.
m = cm.ScalarMappable(cmap=cm.jet,norm=norm)
m.set_array(data)
Complete code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
# prepare the sphere surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
phi = np.linspace(0,2*np.pi, 50)
theta = np.linspace(0, np.pi, 25)
x=np.outer(np.cos(phi), np.sin(theta))
y=np.outer(np.sin(phi), np.sin(theta))
z=np.outer(np.ones(np.size(phi)), np.cos(theta))
# prepare function to plot
PHI=np.outer(phi,np.ones(np.size(theta)))
THETA=np.outer(np.ones(np.size(phi)),theta)
data = PHI/np.pi
# plot
norm = plt.Normalize(vmin=data.min(), vmax=data.max())
surface=ax.plot_surface(x, y, z, cstride=1, rstride=1,
facecolors=cm.jet(norm(data)))
# add colorbar
m = cm.ScalarMappable(cmap=cm.jet,norm=norm)
m.set_array(data)
plt.colorbar(m)
plt.show()
I'm plotting subplots with matplotlib and the legend does not show up for some plots.
In this example, the scatter plot legend does not show up.
import numpy as np
import matplotlib
from matplotlib import pyplot as plt
from matplotlib.legend_handler import HandlerLine2D
from matplotlib.patches import Rectangle, Circle
fig = plt.figure()
plt.cla()
plt.clf()
x = np.arange(5) + 1
y = np.full(5, 10)
fig, subplots = plt.subplots(2, sharex=False, sharey=False)
subplots[0].bar(x, y, color='r', alpha=0.5, label='a')
scat = subplots[0].scatter(x, y-1, color='g', label='c')
subplots[0].set_yscale('log')
subplots[1].bar(x, y, color='r', alpha=0.5, label='a')
x = [2, 3]
y = [4, 4]
subplots[1].bar(x, y, color='b', alpha=1, label='b')
subplots[1].set_yscale('log')
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5), handler_map={scat: HandlerLine2D(numpoints=4)})
plt.show()
Here is what I tried as a workaround:
p1 = Rectangle((0, 0), 1, 1, fc="r", alpha=0.5)
p2 = Rectangle((0, 0), 1, 1, fc="b")
p3 = Circle((0, 0), 1, fc="g")
legend([p1, p2, p3], ['a', 'b', 'c'], loc='center left', bbox_to_anchor=(1, 0.5))
I really prefer to fix this without the workaround so if anyone knows how to fix it please let me know.
Also, an issue with the workaround is that the Circle object still appears as a bar on the legend.
plt.legend starts with a gca() (which returns the current axes):
# from pyplot.py:
def legend(*args, **kwargs):
ret = gca().legend(*args, **kwargs)
So calling plt.legend will only get you a legend on your last subplot. But it is also possible to call e.g. ax.legend(), or in your case subplots[0].legend(). Adding that to the end of your code gives me a legend for both subplots.
Sample:
for subplot in subplots:
subplot.legend(loc='center left', bbox_to_anchor=(1, 0.5))
I am not able to get nicer spaces between the xticks with the following code:
import random
import matplotlib.pyplot as plt
coverages = [random.randint(1,10)*2] * 100
contig_names = ['AAB0008r'] * len(coverages)
fig = plt.figure()
fig.clf()
ax = fig.add_subplot(111)
ax.yaxis.grid(True, linestyle='-', which='major', color='grey', alpha=0.5)
ind = range(len(coverages))
rects = ax.bar(ind, coverages, width=0.2, align='center', color='thistle')
ax.set_xticks(ind)
ax.set_xticklabels(contig_names)
#function to auto-rotate the x axis labels
fig.autofmt_xdate()
plt.show()
How to get more space between the xticks so they do not look like overlapped anymore?
Thank you in advance.
You can try changing the figure size, the size of the xticklabels, their angle of rotation, etc.
# Set the figure size
fig = plt.figure(1, [20, 8])
# Set the x-axis limit
ax.set_xlim(-1,100)
# Change of fontsize and angle of xticklabels
plt.setp(ax.get_xticklabels(), fontsize=10, rotation='vertical')