I have a plot with both a colorbar and a legend. I want to place the legend outside of the plot to the right of the colorbar. To accomplish this, I use bbox_to_anchor argument, but this causes the legend to get cut off:
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
_, ax = plt.subplots()
extent = np.r_[0, 1, 0, 1]
space = np.linspace(0, 1)
probs = np.array([[norm.cdf(x + y) for x in space] for y in space])
colormap = ax.imshow(probs, aspect="auto", origin="lower", extent=extent, alpha=0.5)
colorbar = plt.colorbar(colormap, ax=ax)
colorbar.set_label(f"Probability")
ax.scatter(
[0.2, 0.4, 0.6], [0.8, 0.6, 0.4], color="r", label="Labeled Points",
)
plt.legend(loc="center left", bbox_to_anchor=(1.3, 0.5))
plt.title
plt.show()
Plot with legend cut off
To fix the legend, I insert a call to plt.tight_layout() before plt.show(), but this causes the aspect ratio to get distorted:
Plot with distorted aspect ratio
How can I show the entire legend and preserve the aspect ratio of the axes?
You can manage the ratio between axis height and width with matplotlib.axes.Axes.set_aspect. Since you want them to be equal:
ax.set_aspect(1)
Then you can use matplotlib.pyplot.tight_layout to fit the legend within the figure.
If you want to adjust margins too, you can use matplotlib.pyplot.subplots_adjust.
Complete Code
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
_, ax = plt.subplots()
extent = np.r_[0, 1, 0, 1]
space = np.linspace(0, 1)
probs = np.array([[norm.cdf(x + y) for x in space] for y in space])
colormap = ax.imshow(probs, aspect="auto", origin="lower", extent=extent, alpha=0.5)
colorbar = plt.colorbar(colormap, ax=ax)
colorbar.set_label(f"Probability")
ax.scatter([0.2, 0.4, 0.6], [0.8, 0.6, 0.4], color="r", label="Labeled Points",)
plt.legend(loc="center left", bbox_to_anchor=(1.3, 0.5))
ax.set_aspect(1)
plt.tight_layout()
plt.subplots_adjust(left = 0.1)
plt.show()
Related
I have the following issue.
I have a graph of which has colored segments. The problem is in relating those segments to the color bar (which also contains text), so that each color segment is aligned with the color bar.
The code is the following:
from matplotlib.colorbar import colorbar_factory
x_v = datosg["Hour"]+div
y_v = datosg["UV Index"]
fig, ax= plt.subplots(figsize = (7,7))
ax.plot(x_v, y_v, color = "green")
ax.set_xlim(7, 19)
ax.grid()
ax.axhspan(0, 2.5, facecolor='green', alpha=0.8)
ax.axhspan(2.5, 5.5, facecolor='blue', alpha=0.7)
ax.axhspan(5.5, 7.5, facecolor='red', alpha=0.7)
ax.axhspan(7.5, 10.5, facecolor='yellow', alpha=0.7)
ax.axhspan(10.5, 16, facecolor='pink', alpha=0.7)
ax.margins(0)
from matplotlib.colors import ListedColormap
#discrete color scheme
cMap = ListedColormap(['green', 'blue','red', 'yellow', 'pink'])
#data
np.random.seed(42)
data = np.random.rand(5, 5)
heatmap = ax.pcolor(data, cmap=cMap)
#legend
cbar_ay = fig.add_axes([0.93, 0.125, 0.2, 0.755])
cbar = plt.colorbar(heatmap, cax=cbar_ay, orientation="vertical")
cbar.ax.get_yaxis().set_ticks([])
for j, lab in enumerate(['$Bajo$','$Medio$','$Alto$','$Muy Alto$','$Extremo$']):
cbar.ax.text(.5, (2 * j + 1) / 10.0, lab, ha='center', va='center')
plt.show()
The graph that results from this code is as follows:
Result_code
I have tried everything, the result I expect is very similar to this graph:
resulting image
But I can't change the range of the colors in the color bar.
Also note that I created random values in order to create the colorbar, I couldn't think of any other way, however so far it has worked. I only have to modify the range, so that it is similar to the last graph.
Any help would be appreciated.
I guess it's much easier to just draw a second Axes and fill it with axhspans the same way you did it with the main Axes, but if you want to use a colorbar, you can do it as follows:
import itertools
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
colors = ['green', 'blue','red', 'yellow', 'pink']
labels = ['$Bajo$','$Medio$','$Alto$','$Muy Alto$','$Extremo$']
bounds = np.array([0, 2.5, 5.5, 7.5, 10.5, 16 ])
fig, ax= plt.subplots()
for span, color in zip(itertools.pairwise(bounds), colors):
ax.axhspan(*span, facecolor=color, alpha=0.8)
ax.margins(0)
cmap = mpl.colors.ListedColormap(colors)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
ax_pos = ax.get_position().bounds
cbar_ay = fig.add_axes([0.93, ax_pos[1], 0.2, ax_pos[3]])
cbar = plt.colorbar(mpl.cm.ScalarMappable(cmap=cmap, norm=norm), cax=cbar_ay, orientation="vertical", spacing='proportional')
cbar.ax.set_axis_off()
for y, lab in zip(bounds[:-1] + np.diff(bounds) / 2, labels):
cbar.ax.text(.5, y, lab, ha='center', va='center')
This is my code:
import numpy as np
import matplotlib.pyplot as plt
def plot_graph():
fig = plt.figure()
data = [[top3_empsearch, top5_empsearch, top7_empsearch], [top3_elastic, top5_elastic, top7_elastic]]
X = np.arange(3)
ax = fig.add_axes([0, 0, 1, 1])
ax.bar(X + 0.00, data[0], color='b', width=0.25)
ax.bar(X + 0.25, data[1], color='g', width=0.25)
ax.set_ylabel('Accuracy (in %)')
plt.title('Percentage accuracy for selected result in Top-3, Top-5, Top-7 in employee search vs elastic search')
plt.yticks(np.arange(0, 101, 10))
colors = {'empsearch':'blue', 'elastic':'green'}
labels = list(colors.keys())
handles = [plt.Rectangle((0,0),1,1, color=colors[label]) for label in labels]
plt.legend(handles, labels)
plt.style.use('dark_background')
plt.show()
plot_graph()
The outcome of this code is ->
No ticks, no labels, no title nothing is visible and I'm bamboozled. Will appreciate the help.
The only problem is in this line:
ax = fig.add_axes([0, 0, 1, 1])
Looking to the bibliography (https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.figure.Figure.html), you will see that the first parameter of add_axes() function is "rect", which refers to the the dimensions [left, bottom, width, height] of the new axes, all quantitie in fractions of figure width and height. So in your code you are giving exactly the dimensions of the figure, so the title, ticks, labels... are there but hidden. So you have to leave some space, reducing a bit the plot's dimensions. You could do it just by modifying:
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
Alternatively, you could replace that line by:
ax = fig.add_subplot(1,1,1)
and the result should be the same.
Here is my result:
Using the below code I have made the data to be plotted using only the upper half (0.5 to 1) of the default 'jet' colormap, the range of the colormap being 0 to 1.
If I want the data to show colors only between the range of 0.7 - 1, how do I do it?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
np.random.seed(1)
# Evaluate an existing colormap from 0.5 (midpoint) to 1 (upper end)
cmap = plt.get_cmap('jet')
colors = cmap(np.linspace(0.5, 1, cmap.N ))
# Create a new colormap from those colors
cmap2 = LinearSegmentedColormap.from_list('Upper Half', colors)
z = np.random.random((4,4))
fig, axes = plt.subplots(ncols=2)
for ax, cmap in zip(axes.flat, [cmap, cmap2]):
cax = ax.imshow(z, cmap=cmap, origin='lower')
cbar = fig.colorbar(cax, ax=ax, orientation='horizontal')
cbar.set_label(cmap.name)
plt.show()
Result:
I want to get something looking like
You can use vmin and vmax argument. Define the ranges in a list called vlst which are 0-1 for the left figure and 0.7-1 for the right figure.
vlst = [[0, 1], [0.7, 1]]
fig, axes = plt.subplots(ncols=2)
for ax, cmap, v in zip(axes.flat, [cmap, cmap2], vlst):
cax = ax.imshow(z, cmap=cmap, origin='lower',vmin=v[0], vmax=v[1])
cbar = fig.colorbar(cax, ax=ax, orientation='horizontal')
cbar.set_label(cmap.name)
plt.show()
I have tried the following to produce a regular polygon:
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig2 = plt.figure()
ax2 = fig2.add_subplot(111, aspect='equal')
ax2.add_patch(
patches.RegularPolygon(
(0.5, 0.5),
3,
0.2,
fill=False # remove background
)
)
fig2.savefig('reg-polygon.png', dpi=90, bbox_inches='tight')
plt.show()
While this produces a triangle, I haven't found any way to produce a trapezoid and and a parallelogram.
Are there any commands to do this? Or can I transform the regular polygon into one of the other shapes?
You would need to use a matplotlib.patches.Polygon and define the corners yourself.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
# Parallelogram
x = [0.3,0.6,.7,.4]
y = [0.4,0.4,0.6,0.6]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=False))
# Trapez
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=False))
plt.show()
For filled patches with size greater than 1 x 1
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)
x = [0, 1.16, 2.74, 2, 0]
y = [0, 2.8, 2.8, 0, 0]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=True))
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=True, color='magenta'))
One simple way to do it is creating a list of lists as the end points of the polygon( parallelogram/trapezoid) and plotting(or rather tracing) them.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig2 = plt.figure()
ax2 = fig2.add_subplot(111, aspect='equal')
points = [[0.2, 0.4], [0.4, 0.8], [0.8, 0.8], [0.6, 0.4], [0.2,0.4]] #the points to trace the edges.
polygon= plt.Polygon(points, fill=None, edgecolor='r')
ax2.add_patch(polygon)
fig2.savefig('reg-polygon.png', dpi=90, bbox_inches='tight')
plt.show()
Also, note that you should use Polygon instead of RegularPolygon.
I would like to plot a constant field but it s not very beautiful...
its value is zero and i want a colorbar between -1 and 1!
I try that with :
import numpy as np
import numpy.ma as ma
import matplotlib.pyplot as plt
from matplotlib import cm
plt.rcParams['text.usetex']=True
plt.rcParams['text.latex.unicode']=True
Lx=80.
Ly=120.
x0 = 30.
y0 = Ly/2.
YA, XA = np.mgrid[0:Ly, 0:Lx]
Order0 = 0*np.ones((YA.shape[0], YA.shape[1]))
plt.imshow(Order0,aspect='auto',cmap=plt.cm.hot,origin="lower")
plt.colorbar()
plt.show()
fig, ax = plt.subplots()
cax = ax.imshow(Order0, interpolation='nearest', cmap=cm.hot)
ax.set_title(r"\ test",fontsize=20)
plt.axis('off')
cbar = fig.colorbar(cax, ticks=[-1, -0.5, 0, 0.5, 1])
cbar.ax.set_yticklabels(['-1', '-0.5', '0','0.5' ' 1'])# vertically oriented colorbar
plt.show()
I would like to get an image like this in order to respect other results :
I suspect you want to change the line:
plt.imshow(Order0,aspect='auto',cmap=plt.cm.hot,origin="lower")
to add vmin and vmax:
plt.imshow(Order0,aspect='auto',cmap=plt.cm.hot,origin="lower", vmin=-1, vmax=1)
giving: