How to add customized ticks in Heatmap? - python

I plotted an array in seaborn heatmap, and I want to add tick limits to the axis.
My code:
# plot
eixoz = numpy.linspace(0, Z)
eixor = numpy.linspace(ra, R, nr)
eixox = D
numpy.meshgrid(eixoz, eixor)
ax = seaborn.heatmap(eixox)
ax.invert_yaxis()
plt.xlabel("Eixo z", fontsize=20)
plt.ylabel("Eixo r", fontsize=20)
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
ax.collections[0].colorbar.set_label("Celsius", fontsize=20)
plt.show()
How can I add those limit ticks in blue? And also, how can I resize the color bar numbers?

The size of the colorbar tick labels can be changed via ax.collections[0].colorbar.ax.tick_params(labelsize=20).
Text at the start and end of the axes can be place using the axes transform, where 0 is the left (or bottom) and 1 is the right (or top) of the axes. Negative values (or values larger than 1) are proportionall outside the axes area. Horizontal and vertical lines can use the same transform, but unlike text need clip_on=False to be drawn outside the axes area.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
len_eixoz = 20
eixox = np.repeat(np.arange(37.55, 37.66, 0.02), len_eixoz).reshape(-1, len_eixoz)
ax = sns.heatmap(eixox)
ax.invert_yaxis()
ax.set_xlabel("Eixo z", fontsize=20)
ax.set_ylabel("Eixo r", fontsize=20)
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
ax.collections[0].colorbar.set_label("Celsius", fontsize=20)
cbar = ax.collections[0].colorbar.ax.tick_params(labelsize=20)
x0, x1 = 1, 2
y0, y1 = 0, 1
ax.text(0, -0.07, x0, ha='center', va='top', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.text(1, -0.07, x1, ha='center', va='top', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.text(-0.05, 0, y0, ha='right', va='center', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.text(-0.05, 1, y1, ha='right', va='center', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.vlines([0, 1], [0, 0], [-0.06, -0.06], color='crimson', clip_on=False, transform=ax.transAxes)
ax.hlines([0, 1], [0, 0], [-0.04, -0.04], color='crimson', clip_on=False, transform=ax.transAxes)
plt.tight_layout()
plt.show()
Note that calling sns.set(font_scale=1.8) at the start would scale all fonts.

Related

matplotlib: horizontal spacing of subplots with equal aspect ratio

I'm trying to draw two rows with three columns of pcolormesh plots and a combined colorbar for all plots. So far it seems to work. However, I'm sure I'm not using the most elegant way...
The only problem I have, is that I can't decrease the horizontal spacing any further. The following line should set the horizontal spacing to zero:
fig.subplots_adjust(left=0.05, right=0.98, top=0.93, bottom=0.00, wspace=0, hspace=0.03)
But this does not work in conjunction with
ax.set_aspect('equal')
I've attached a small code snippet that creates the following figure:
Example figure
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
rows = 2
columns = 3
fig = plt.figure()
gs = gridspec.GridSpec(rows+1, columns)
lines = []
x = np.linspace(1,10,100)
y = x
X, Y = np.meshgrid(x,y)
Z = np.random.rand(100,100)
lines = []
for i in range(rows):
lines.append([])
for j in range(columns):
ax = fig.add_subplot(gs[i, j])
line = ax.pcolormesh(X, Y, Z, cmap=plt.cm.Reds)
lines[i].append(line)
ax.set_aspect('equal')
for tick in ax.get_xticklabels():
tick.set_rotation(45)
if i!=rows-1:
ax.set_xticklabels([])
if j!=0:
ax.set_yticklabels([])
#title
props = dict(boxstyle='round', facecolor='white', alpha=0.7)
ax.text(0.05, 0.95, "plot (%i, %i)" % (i,j), transform=ax.transAxes, fontsize=5,
verticalalignment='top', bbox=props)
ax.tick_params(labelsize=7)
cb_ax = fig.add_subplot(gs[-1,:])
cb_ax.set_aspect(0.05)
cbar = fig.colorbar(lines[0][0], cax=cb_ax, orientation='horizontal')
cb_ax.tick_params(labelsize=7)
fig.subplots_adjust(left=0.05, right=0.98, top=0.93, bottom=0.00, wspace=0, hspace=0.03)
#fig.tight_layout()
fig.text(0.5, 0.2, "x axis", ha='center', va='center')
fig.text(0.5, 0.97, "overall title", ha='center', va='center')
fig.text(0.02, 0.5, "y axis", ha='center', va='center', rotation='vertical')
fig.text(0.5, 0.02, "quantity [unit]", ha='center', va='center',)
plt.savefig("test.png", dpi=600)

Matplotlib: how to set a tick label above a plot

This is the sine and cosine plot I draw using matplotlib. But the tick labels are below the plot and can hardly seen.
My python code is:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6), dpi=96)
plt.subplot(111)
X = np.linspace(-np.pi, np.pi, 256, endpoint=True)
C,S = np.cos(X), np.sin(X)
plt.plot(X, C, color="blue", linewidth=2.5, linestyle="-", label="consine")
plt.plot(X, S, color="red", linewidth=2.5, linestyle="-", label="sine")
plt.xlim(X.min()*1.1, X.max()*1.1)
plt.ylim(C.min()*1.1, C.max()*1.1)
plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],
[r'$-\pi$', r'$-\frac{\pi}{2}$', r'$0$', r'$+\frac{\pi}{2}$', r'$+\pi$'])
plt.yticks([-1, 1],
[r'$-1$', r'$+1$'])
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
plt.legend(loc='upper left', frameon=False)
for label in ax.get_xticklabels()+ax.get_yticklabels():
label.set_fontsize(16)
label.set_bbox(dict(facecolor='green', edgecolor='None', alpha=0.2))
plt.savefig("figures/exercise10.png", dpi=120)
plt.show()
So, how should I set a tick label above a plot?
Thank you!
Possibly you want to set the labels and the axes spines on top of the lines. This can easily be achieved with the "axes.axisbelow" rcParam.
plt.rcParams["axes.axisbelow"] = False

python text to second y axis

I'm trying to make my first plots in python using matplotlib, but i would like the text in the plot to be "outside" the plot i.e. next to the line instead of above it or under it.
plt.figure()
plt.scatter(mean, diff, color='k')
plt.axhline(md, color='black', linestyle='-', lw=3)
plt.axhline(md + 1.96*sd, color='black', linestyle='--')
plt.axhline(md - 1.96*sd, color='black', linestyle='--')
plt.axhline(0, color='black', linestyle='--')
plt.ylim(-max(diff)*2, max(diff)*2)
plt.xlabel('Mean')
plt.ylabel('Difference')
plt.title('Bland altman plot for ' + variable)
txt1=('+1.96 SD')
txt2=('-1.96 SD')
txt3 =('Mean')
x = max(mean)
plt.text(x, md+1.96*sd, txt1, horizontalalignment='left', verticalalignment='bottom', fontweight='bold')
plt.text(x, md-1.96*sd, txt2, horizontalalignment='left', verticalalignment='top', fontweight='bold')
plt.text(x, 0.1, txt3)
The result is:
The main ingredient you still need to position the labels at the edge of the axes, is the coordinate of the edge of the axes. You can get those via plt.gca().get_xlim(). The upper limit can then be used as x position of the text label.
import matplotlib.pyplot as plt
import numpy as np
X = np.random.normal(size=(12))
Y = np.random.normal(size=(12))
plt.figure()
plt.scatter(X, Y, color='k')
plt.axhline(Y.mean(), color='black', linestyle='-', lw=3)
plt.axhline(Y.mean() + 1.96*Y.std(), color='black', linestyle='--')
plt.axhline(Y.mean() - 1.96*Y.std(), color='black', linestyle='--')
plt.axhline(0, color='black', linestyle='--')
txt1=(' +1.96 SD')
txt2=(' -1.96 SD')
txt3 =(' Mean')
x0,x1 = plt.gca().get_xlim()
plt.text(x1, Y.mean() + 1.96*Y.std(), txt1, ha='left', va='center', fontweight='bold')
plt.text(x1, Y.mean() - 1.96*Y.std(), txt2, ha='left', va='center', fontweight='bold')
plt.text(x1, 0, txt3, ha='left', va='center',)
# make more space on right side to host the labels
plt.subplots_adjust(right=0.8)
plt.show()
Note that this is the very basic (but also quite understandable) version.
If you're comfortable with transformations, you can use the technique in this question:
Add a label to y-axis to show the value of y for a horizontal line in matplotlib

Simple line with annotations

Is there any simpler way to have this line (with two annotations) using matplotlib?
The idea is just draw a line showing a interval ([0,T]) and some points with annotations. This code is too big for such a small thing.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ylim(-.3,.3)
plt.xlim(0, 1)
xmin, xmax = ax.get_xlim()
# removing the default axis on all sides:
for side in ['bottom','right','top','left']:
ax.spines[side].set_visible(False)
# removing the axis ticks
plt.xticks([])
plt.yticks([])
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
# draw x and y axis
ax.arrow(xmin, 0, xmax-xmin, 0, fc='k', ec='k',
length_includes_head= True, clip_on = False)
esp=0.05
ax.text(0.5, esp, r'$\Delta t$', ha='center')
ax.text(0.45, 0, r'$|$', ha='center')
ax.text(0.45, -esp, r'$t_i$', ha='center')
ax.text(0.55, 0, r'$|$', ha='center')
ax.text(0.55, -esp, r'$t_{i+1}$', ha='center')
ax.text(0, 0, r'$|$', ha='center')
ax.text(0, -esp, r'$0$', ha='center')
ax.text(1, 0, r'$|$', ha='center')
ax.text(1, -esp, r'$T$', ha='center')
plt.show()
One could use the xaxis as the line to draw the intervals. That would allow to use its ticks and ticklabels for the annotations.
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
plt.subplots_adjust(bottom=0.5, top=0.7)
for side in ['right','top','left']:
ax.spines[side].set_visible(False)
ax.set_yticks([])
ticks=[0,.45,.55,1]
ticklabels=[0,r'$t_i$', r'$t_{i+1}$',r'$T$']
ax.set_xticks(ticks)
ax.set_xticklabels(ticklabels)
ax.tick_params(direction='inout', length=10,pad=7)
ax.text(0.5, 0.2, r'$\Delta t$', ha='center')
plt.show()
I found similar solution using the x-axis but it is a little more compact
plt.ylim(0,.3)
plt.xticks([0, 0.45, 0.55, 1],
('$0$', '$t_i$', '$t_{i+1}$', '$T$'),
size=20,
verticalalignment='top')
plt.tick_params(axis='x',
direction='inout',
length=20,
pad=15,
bottom=True,
top=False)
Another approach is drawing the ticks as scatterplot and annotating each, i.e.
for x, label in [(0, '$0$'), (0.45, '$t_i$'), (0.55, '$t_{i+1}$'), (1, '$T$')]:
plt.scatter(x, 0, s=50, marker='|')
plt.annotate(label, [x,-0.05], size=20)

3D parametric curve in Matplotlib does not respect zorder. Workaround?

I am designing a three-dimensional illustration using Matplotlib. All is working nicely, except that the (red) parametric curve gets the wrong zorder while the (green) parametric surface is drawn completely correctly.
Output generated by code below:
I know that Matplotlib has limited capabilities for accurately computing the zorder of objects, but since it can do it for the parametric surface, it seems like a bug in Matplotlib.
That said, is there any way to force correct z-ordering just to get things to work quickly? It seems that all I have to be able to say is that the right transparent blue plane is on top of everything else. However, putting a zorder argument into PolyCollection does not seem to have any effect, and putting an explicit zorder argument into the plot function which draws the read line will mess up its ordering relative to the green surface.
Is there a way to force the right blue transparent surface on top of everything? Here is the code I have so far:
#!/bin/env python3
from pylab import *
from mpl_toolkits.mplot3d import *
from matplotlib.collections import PolyCollection
from matplotlib.colors import colorConverter
from matplotlib.patches import FancyArrowPatch
rc('text', usetex=True)
rc('font', size=20)
fig = figure(figsize=(11,6))
ax = fig.gca(projection='3d')
ax.set_axis_off()
def f(x,t):
return t/2 * 0.55*(sin(2*x)+0.4*x**2-0.65)
c_plane = colorConverter.to_rgba('b', alpha=0.15)
N = 50
y = linspace(-1,1,N)
t = linspace(0,2,N)
yy, tt = meshgrid(y, t)
zz = f(yy,tt)
ax.plot(0*ones(y.shape), y, f(y,0), '-g', linewidth=3)
ax.plot(2*ones(y.shape), y, f(y,2), '-g', linewidth=3)
yt = 0.7*y
zt = f(yt, t) + 0.2*t
ax.plot(t, yt, zt, '-r', linewidth=3)
ax.plot((0,2), (yt[0], yt[-1]), (zt[0], zt[-1]), 'or')
ax.plot([2,2,2], [-1,yt[-1],yt[-1]], [zt[-1],zt[-1],-1], 'k--')
ax.plot(2*ones(y.shape), yt, f(yt,2)+0.1*(y+1), 'g:', linewidth=2)
ax.plot((2,2),
(yt[0], yt[-1]),
(f(yt[0], 2), f(yt[-1], 2) + 0.1*(y[-1]+1)), 'og')
ax.plot((0,2,2),
(-1,-1,zt[-1]),
(0,yt[-1],-1), 'ok')
ax.text(0, -1.1, 0, r'$p(0)=0$', ha='right', va='center')
ax.text(2, -1.05, zt[-1], r'$p(T)$', ha='right', va='center')
ax.text(0, -1.0, 1, r'$p$', ha='right', va='bottom')
ax.text(0, 1, -1.1, r'$q$', ha='center', va='top')
ax.text(0, -1, -1.1, r'$t=0$', ha='right', va='top')
ax.text(2, -1, -1.1, r'$t=T$', ha='right', va='top')
ax.text(2, yt[-1]-0.05, -1.05, r'$q(T)=q^*$', ha='left', va='top')
ax.text(0, 0.5, 0.05, r'$\mathcal{M}(0)$', ha='center', va='bottom')
ax.text(2, 0.1, -0.8, r'$\mathcal{M}(T)$', ha='center', va='bottom')
arrowprops = dict(mutation_scale=20,
linewidth=2,
arrowstyle='-|>',
color='k')
# For arrows, see
# https://stackoverflow.com/questions/29188612/arrows-in-matplotlib-using-mplot3d
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
FancyArrowPatch.draw(self, renderer)
a = Arrow3D([0,2], [-1,-1], [-1,-1], **arrowprops)
ax.add_artist(a)
a = Arrow3D([0,0], [-1,-1], [-1,1], **arrowprops)
ax.add_artist(a)
a = Arrow3D([0,0], [-1,1], [-1,-1], **arrowprops)
ax.add_artist(a)
# For surface illumination, see
# http://physicalmodelingwithpython.blogspot.de/2015/08/illuminating-surface-plots.html
# Get lighting object for shading surface plots.
from matplotlib.colors import LightSource
# Get colormaps to use with lighting object.
from matplotlib import cm
# Create an instance of a LightSource and use it to illuminate the surface.
light = LightSource(70, -120)
white = np.ones((zz.shape[0], zz.shape[1], 3))
illuminated_surface = light.shade_rgb(white*(0,1,0), zz)
ax.plot_surface(tt, yy, zz,
cstride=1, rstride=1,
alpha=0.3, facecolors=illuminated_surface,
linewidth=0)
verts = [array([(-1,-1), (-1,1), (1,1), (1,-1), (-1,-1)])]
poly = PolyCollection(verts, facecolors=c_plane)
ax.add_collection3d(poly, zs=[0], zdir='x')
poly = PolyCollection(verts, facecolors=c_plane)
ax.add_collection3d(poly, zs=[2], zdir='x')
ax.set_xlim3d(0, 2)
ax.view_init(elev=18, azim=-54)
show()
A way of changing the drawing order for plots using axis3d was added in Matplotlib 3.5.0. Setting the parameter 'computed_zorder' False allows manual control of the drawing order
ax = plt.axes(projection='3d',computed_zorder=False)
ax.plot_surface(X1, Y1, Z1,zorder=4.4)
ax.plot_surface(X2, Y2, Z2,zorder=4.5)
Higher 'zorders' are plotted on top. Some common artist z-orders (so you don't plot over your legend):
Artist
Z-order
Images (AxesImage, FigureImage, BboxImage)
0
Patch, PatchCollection
1
Line2D, LineCollection (including minor ticks, grid lines)
2
Major ticks
2.01
Text (including axes labels and titles)
3
Legend
5
From:
https://matplotlib.org/stable/gallery/misc/zorder_demo.html
Source: https://github.com/matplotlib/matplotlib/commit/2db6a0429af47102456366f8d3a4df24352b252e (from: https://github.com/matplotlib/matplotlib/pull/14508)
Axes3D ignores zorder and draws all artists in the order it thinks they should be. However, you may set zorder=0 for red line and zorder=-1 for green surface (or vice-versa) to put they behind right blue panel.
My result:
You have to know:
The default drawing order for axes is patches, lines, text. This
order is determined by the zorder attribute. The following defaults
are set
Artist Z-order
Patch / PatchCollection 1
Line2D / LineCollection 2
Text 3
After some more trial and error, I found a solution. If the right plane is drawn using plot_surface and I change the zorder on the red curve, matplotlib gets the overall order of objects right. Funny enough, the color of the planes changes slightly whether I draw them via PolyCollection or plot_surface, so I need to draw both planes using the same function. So the zorder handling of mplot3d is rather inconsistent, but the final result looks pretty good. I post it here for reference
with final code here:
#!/bin/env python3
from pylab import *
from mpl_toolkits.mplot3d import *
from matplotlib.collections import PolyCollection
from matplotlib.colors import colorConverter
from matplotlib.patches import FancyArrowPatch
rc('text', usetex=True)
rc('font', size=20)
fig = figure(figsize=(11,6))
ax = fig.gca(projection='3d')
ax.set_axis_off()
def f(x,t):
return t/2 * 0.55*(sin(2*x)+0.4*x**2-0.65)
c_plane = colorConverter.to_rgba('b', alpha=0.15)
N = 50
y = linspace(-1,1,N)
t = linspace(0,2,N)
yy, tt = meshgrid(y, t)
zz = f(yy,tt)
ax.plot(0*ones(y.shape), y, f(y,0), '-g', linewidth=3)
ax.plot(2*ones(y.shape), y, f(y,2), '-g', linewidth=3)
yt = 0.7*y
zt = f(yt, t) + 0.2*t
ax.plot(t, yt, zt, '-r', linewidth=3, zorder = 1)
ax.plot([2,2,2], [-1,yt[-1],yt[-1]], [zt[-1],zt[-1],-1], 'k--')
ax.plot(2*ones(y.shape), yt, f(yt,2)+0.1*(y+1), 'g:', linewidth=2)
ax.plot((2,2),
(yt[0], yt[-1]),
(f(yt[0], 2), f(yt[-1], 2) + 0.1*(y[-1]+1)), 'og')
ax.plot((0,2,2),
(-1,-1,zt[-1]),
(0,yt[-1],-1), 'ok')
ax.text(0, -1.1, 0, r'$p(0)=0$', ha='right', va='center')
ax.text(2, -1.05, zt[-1], r'$p(T)$', ha='right', va='center')
ax.text(0, -1.0, 1, r'$p$', ha='right', va='bottom')
ax.text(0, 1, -1.1, r'$q$', ha='center', va='top')
ax.text(0, -1, -1.1, r'$t=0$', ha='right', va='top')
ax.text(2, -1, -1.1, r'$t=T$', ha='right', va='top')
ax.text(2, yt[-1]-0.05, -1.05, r'$q(T)=q^*$', ha='left', va='top')
ax.text(0, 0.5, 0.05, r'$\mathcal{M}(0)$', ha='center', va='bottom')
ax.text(2, 0.1, -0.8, r'$\mathcal{M}(T)$', ha='center', va='bottom')
arrowprops = dict(mutation_scale=20,
linewidth=2,
arrowstyle='-|>',
color='k')
# For arrows, see
# https://stackoverflow.com/questions/29188612/arrows-in-matplotlib-using-mplot3d
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
FancyArrowPatch.draw(self, renderer)
a = Arrow3D([0,2], [-1,-1], [-1,-1], **arrowprops)
ax.add_artist(a)
a = Arrow3D([0,0], [-1,-1], [-1,1], **arrowprops)
ax.add_artist(a)
a = Arrow3D([0,0], [-1,1], [-1,-1], **arrowprops)
ax.add_artist(a)
# For surface illumination, see
# http://physicalmodelingwithpython.blogspot.de/2015/08/illuminating-surface-plots.html
# Get lighting object for shading surface plots.
from matplotlib.colors import LightSource
# Get colormaps to use with lighting object.
from matplotlib import cm
# Create an instance of a LightSource and use it to illuminate the surface.
light = LightSource(70, -120)
white = ones((zz.shape[0], zz.shape[1], 3))
illuminated_surface = light.shade_rgb(white*(0,1,0), zz)
ax.plot_surface(tt, yy, zz,
cstride=1, rstride=1,
alpha=0.3, facecolors=illuminated_surface,
linewidth=0,
zorder=10)
verts = [array([(-1,-1), (-1,1), (1,1), (1,-1), (-1,-1)])]
ax.plot_surface(((0,0),(0,0)), ((-1,-1),(1,1)), ((-1,1),(-1,1)),
color=c_plane)
ax.plot_surface(((2,2),(2,2)), ((-1,-1),(1,1)), ((-1,1),(-1,1)),
color=c_plane)
ax.plot((0,2), (yt[0], yt[-1]), (zt[0], zt[-1]), 'or')
ax.set_xlim3d(0, 2)
ax.view_init(elev=18, azim=-54)
show()

Categories

Resources