matplotlib subplots with respect to inset axes - matplotlib

I am trying to align multiple subplots with a zoomed inset plot. Something like this:
|‾ ‾ ‾ ‾| |‾ ‾| |‾ ‾| |‾ ‾|
| | |_ _| |_ _| |_ _|
| |
|_ _ _ _|
I have tried using subplot2grid but I am not sure how to specify this kind of alignment. I am using a modified version of the Zoom region inset axes example in the matplotlib gallery: https://matplotlib.org/stable/gallery/subplots_axes_and_figures/zoom_inset_axes.html
Here is the reproducible example:
from matplotlib import cbook
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
def get_demo_image():
z = cbook.get_sample_data("axes_grid/bivariate_normal.npy", np_load=True)
# z is a numpy array of 15x15
return z, (-3, 4, -4, 3)
ax1 = plt.subplot2grid((2, 2), (0, 0), rowspan=2)
# make data
Z, extent = get_demo_image()
Z2 = np.zeros((150, 150))
ny, nx = Z.shape
Z2[30:30+ny, 30:30+nx] = Z
ax1.imshow(Z2, extent=extent, origin="lower")
# inset axes....
axins = ax1.inset_axes([1.1, 0.5, 0.5, 0.5])
axins.imshow(Z2, extent=extent, origin="lower")
# sub region of the original image
x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9
axins.set_xlim(x1, x2)
axins.set_ylim(y1, y2)
axins.set_axis_off()
ax1.indicate_inset_zoom(axins, edgecolor="black")
ax2 = plt.subplot2grid((2, 2), (0, 1))
plt.tight_layout()
plt.show()
output:
I want ax2 to be aligned with axins - i.e. it should be the same size as the zoomed inset plot and placed side-by-side. How do I achieve this with matplotlib?

I've been able to achieve that, but I fear that it only works as long as you use an equal aspect ratio on the second axis (maybe you are plotting another image with height=width):
from matplotlib import cbook
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
def get_demo_image():
z = cbook.get_sample_data("axes_grid/bivariate_normal.npy", np_load=True)
# z is a numpy array of 15x15
return z, (-3, 4, -4, 3)
gs = GridSpec(1, 2)
fig = plt.figure()
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
# make data
Z, extent = get_demo_image()
Z2 = np.zeros((150, 150))
ny, nx = Z.shape
Z2[30:30+ny, 30:30+nx] = Z
ax1.imshow(Z2, extent=extent, origin="lower")
# inset axes....
axins = ax1.inset_axes([1.1, 0.5, 0.5, 0.5])
axins.imshow(Z2, extent=extent, origin="lower")
# sub region of the original image
x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9
axins.set_xlim(x1, x2)
axins.set_ylim(y1, y2)
axins.set_axis_off()
ax1.indicate_inset_zoom(axins, edgecolor="black")
ax2.set_aspect("equal")
plt.tight_layout()
plt.show()

Inspired by Davide_sd's answer it is possible to specify the following GridSpec, which seems to (almost) give my desired results in a hacky way:
gs = GridSpec(2, 4, hspace=-0.5)
fig = plt.figure()
ax1 = fig.add_subplot(gs[:2, :2])
ax2 = fig.add_subplot(gs[0, 3])
output:

Related

multi animation whit subplot

I got some sort of a problem with a pendulum animation, I tried to display my animation (the pendulum's movement) next to a graph in two separate axes, but when I try my code, it barely works displaying two axes that overlap on one another... Here is what I tried:
PS: best would be that the circles I was intended to add at the end of my pendulum appear on the final animation, but I really have no idea how to put them only on a particular ax
from numpy import sin, cos, pi, array
import numpy as np
import scipy.integrate
import matplotlib.pyplot as plt
import matplotlib.animation as animation
g = 10
y0 = np.array([np.pi / 2.0, 0]) # angle, vitesse
j = 0.2
def f(y, t):
return np.array([y[1], -g * np.sin(y[0])-j*y[1]])
t = np.linspace(0, 100, 10000)
y = scipy.integrate.odeint(f, y0, t)
theta, thetadot = y[:, 0], y[:, 1]
fig, axs = plt.subplots(1,2)
axs[0] = fig.add_subplot(xlim=(-1.5, 1.5), ylim=(-1.5, 1.5))
axs[0].grid()
axs[0].set_box_aspect(1)
# anchor = plt.Circle((0, 0), 0.01, color='black')
# mass = plt.Circle((sin(y0[0]),-cos(y0[0])), 0.2, color='black')
pendulums = axs[0].plot((0, sin(y0[0])), (0, -cos(y0[0])), 'o-', color = 'black')
# plt.gca().add_patch(weight) # adding circles
# plt.gca().add_patch(attach)
phase = axs[1].plot(theta,thetadot)
def animate(i):
angle = theta[i]
x = (0, sin(angle))
y = (0, -cos(angle))
#mass.center = (x[1],y[1])
pendulums[0].set_data(x, y)
anim = animation.FuncAnimation(fig, animate, interval=10)
plt.show()

How to change font parameters in Z axis?

I'm trying to personalize some of my graphs so I'd like to change the font of all axis. I wrote the code below for that. The problem is that I can't change the font of the z axis. Somebody have a recommendation?
Thanks in advance.
import matplotlib.pyplot as plt
from matplotlib import cm
font = {'size' : 12}
cm=1/2.54
size_x = 10*cm
size_y = 8*cm
min_limx = 4
max_limx = 10
min_limy = 0
max_limy = 50
min_limz = 0
max_limz = 8
#%%
fig, ax = plt.subplots(figsize=(size_x, size_y), subplot_kw={"projection": "3d"})
X, Y = np.meshgrid(xnew, ynew, indexing='ij')
Z1 = m_fill
Z2 = m_nofill
surf = ax.plot_surface(X, Y, Z1, cmap=cm.viridis, linewidth=0, antialiased=False)
surf = ax.plot_surface(X, Y, Z2, color='w', linewidth=0, antialiased=False)
ax.set_xlabel('$\lambda$', **font)
ax.set_ylabel('$\chi$', **font)
ax.set_zlabel('$\it{m_{0}, кг}$', **font)
lin_x = np.arange(min_limx, max_limx, step=2)
lin_y = np.arange(min_limy, max_limy, step=15)
plt.xticks(lin_x, **font)
plt.yticks(lin_y, **font)
#plt.zticks(lin_z, **font) # AttributeError: module 'matplotlib.pyplot' has no attribute 'zticks'
ax.set_zlim(0, 8, )
#plt.gca().set_aspect('equal', adjustable='box')
ax.view_init(45, -45)
#plt.tight_layout()
plt.grid()
plt.show()
fig.savefig('p0_V_m0.png', format='png', dpi=300)
Ps: This is how the figure looks like right now.
The fontsize of the tick labels is set by the labelsize parameter:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
font = {'size': 20}
ax.tick_params('z', labelsize=font['size'])
(To set the label size for all three axes you can use ax.tick_params(labelsize=20).)

Adding patch distorts alignment

I am working with the following image:
from matplotlib import cbook
import matplotlib.patches as mpatches
from matplotlib.axes._base import _TransformedBoundsLocator
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
# a numpy array of 15x15
Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy", np_load=True)
gs = GridSpec(2, 3)
fig = plt.figure(figsize=(3*3,2*3))
ax1 = fig.add_subplot(gs[:2, :2])
ax2 = fig.add_subplot(gs[1, 2])
Z2 = np.zeros((150, 150))
ny, nx = Z.shape
Z2[30:30+ny, 30:30+nx] = Z
ax1.imshow(Z2)
ax1.set_aspect("equal")
ax2.set_aspect("equal")
plt.tight_layout()
plt.show()
output:
As shown in the image, the x-axis of both plots are aligned. However, when I am adding a patch to the first plot the alignment becomes distorted:
Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy", np_load=True)
gs = GridSpec(2, 3)
fig = plt.figure(figsize=(3*3,2*3))
ax1 = fig.add_subplot(gs[:2, :2])
ax2 = fig.add_subplot(gs[1, 2])
Z2 = np.zeros((150, 150))
ny, nx = Z.shape
Z2[30:30+ny, 30:30+nx] = Z
ax1.imshow(Z2)
x, y, width, height = 30, 30, 15, 15
ex, ey = (0,1)
xy_data = x + ex * width, y + ey * height
p = mpatches.ConnectionPatch(
xyA=(0,1), coordsA=ax2.transAxes,
xyB=xy_data, coordsB=ax1.transData)
ax1.add_patch(p)
ax1.set_aspect("equal")
ax2.set_aspect("equal")
plt.tight_layout()
plt.show()
output:
Why is this? How can I add a patch whilst retaining the original layout?

Matplotlib Interpolate empty pixels

I have a file 'mydata.tmp' which contains 3 colums like this:
3.81107 0.624698 0.000331622
3.86505 0.624698 0.000131237
3.91903 0.624698 5.15136e-05
3.97301 0.624698 1.93627e-05
1.32802 0.874721 1.59245
1.382 0.874721 1.542
1.43598 0.874721 1.572
1.48996 0.874721 4.27933
etc.
Then I want to make a heatmap color plot where the first two columns are coordinates, and the third column are the values of that coordinates.
Also, I would like to set the third column in log scale.
I have done this
import pandas as pd
import matplotlib.pyplot as plt
import scipy.interpolate
import numpy as np
import matplotlib.colors as colors
# import data
df = pd.read_csv('mydata.tmp', delim_whitespace=True,
comment='#',header=None,
names=['1','2','3'])
x = df['1']
y = df['2']
z = df['3']
spacing = 500
xi, yi = np.linspace(x.min(), x.max(), spacing), np.linspace(y.min(),
y.max(), spacing)
XI, YI = np.meshgrid(xi, yi)
rbf = scipy.interpolate.Rbf(x, y, z, function='linear')
ZI = rbf(XI, YI)
fig, ax = plt.subplots()
sc = ax.imshow(ZI, vmin=z.min(), vmax=z.max(), origin='lower',
extent=[x.min(), x.max(), y.min(),
y.max()], cmap="GnBu", norm=colors.LogNorm(vmin=ZI.min(),
vmax=ZI.max()))
fig.colorbar(sc, ax=ax, fraction=0.05, pad=0.01)
plt.show()
And I get this Image
which has all these empty pixels.
I am looking for something like this instead (I have done this other picture with GNUplot):
How can I do it?
You could use cmap.set_bad to define a color for the NaN values:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
import matplotlib.colors as colors
from matplotlib import cm
import copy
# Some data
x = np.array([0, 1, 3, 0, 2, 4])
y = np.array([0, 0, 0, 1, 1, 1])
z = np.array([2, 2, 3, 2, 3, 4])
# Interpolation on a grid:
nrb_points = 101
xi = np.linspace(-.5, 4.5, nrb_points)
yi = np.linspace(-.5, 1.5, nrb_points)
XI, YI = np.meshgrid(xi, yi)
xy = np.vstack((x, y)).T
XY = (XI.ravel(), YI.ravel())
ZI = griddata(points, z, XY,
method='linear',
fill_value=np.nan) # Value used [for] points
# outside of the convex hull
# of the input points.
ZI = ZI.reshape(XI.shape)
# Color map:
cmap = copy.copy(cm.jet)
cmap.set_bad('grey', 1.)
# Graph:
plt.pcolormesh(xi, yi, ZI,
#norm=colors.LogNorm(),
cmap=cmap);
plt.colorbar(label='z');
plt.plot(x, y, 'ko');
plt.xlabel('x'); plt.ylabel('y');
the result is:
I would also use griddata instead of RBF method for the interpolation. Then, point outside the input data area (i.e. the convex hull) can be set to NaN.

Custom background sections for matplotlib figure

I would like to define colors sections (blue: [0-15000], green: [15000-23000], red[23000,]) that should be used for y-values. Is it somehow possible in matplotlib?
You can color regions on a matplotlib plot using collections.BrokenBarHCollection:
import matplotlib.pyplot as plt
import matplotlib.collections as collections
fig = plt.figure()
ax = fig.add_subplot(111)
# Plot your own data here
x = range(0, 30000)
y = range(0, 30000)
ax.plot(x, y)
xrange = [(0, 30000)]
yrange1 = (0, 15000)
yrange2 = (15000, 23000)
yrange3 = (23000, 30000)
c1 = collections.BrokenBarHCollection(xrange, yrange1, facecolor='blue', alpha=0.5)
c2 = collections.BrokenBarHCollection(xrange, yrange2, facecolor='green', alpha=0.5)
c3 = collections.BrokenBarHCollection(xrange, yrange3, facecolor='red', alpha=0.5)
ax.add_collection(c1)
ax.add_collection(c2)
ax.add_collection(c3)
plt.show()