Matplotlib animate subplots (2 hist and 2 scatter plots) - matplotlib

How can I animate the following 4 subplots? So far I've managed to make the animation not stop at all or to make the frames appear but not the dots/bars of the plots. I tried to follow this https://matplotlib.org/gallery/animation/subplots.html example but "subclassing" is far ahead of my skills.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# generate 4 random variables
x = np.random.normal(-2.5, 1, 10000)
y = np.random.gamma(2, 1.5, 10000)
a = np.random.exponential(2, 10000)+7
b = np.random.uniform(14,20, 10000)
n_bins = 100
n = 100
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2, sharex='col')
# plot
ax1.hist(x, bins=n_bins, facecolor='c')
ax1.set_title('normal')
ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)
ax1.spines['bottom'].set_visible(False)
ax1.spines['left'].set_visible(False)
ax1.tick_params(bottom=False, left=False)
ax2.hist(y, bins=n_bins, facecolor='c')
ax2.set_title('exponential')
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax2.spines['bottom'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax2.tick_params(bottom=False, left=False)
ax3.scatter(x, a, facecolor='c')
ax3.set_title('gamma')
ax3.spines['top'].set_visible(False)
ax3.spines['right'].set_visible(False)
ax3.spines['bottom'].set_visible(False)
ax3.spines['left'].set_visible(False)
ax3.tick_params(bottom=False, left=False)
ax4.scatter(y, b, facecolor='c')
ax4.set_title('uniform')
ax4.spines['top'].set_visible(False)
ax4.spines['right'].set_visible(False)
ax4.spines['bottom'].set_visible(False)
ax4.spines['left'].set_visible(False)
ax4.tick_params(bottom=False, left=False)
plt.show()

in this line
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2, sharex='col')
ax1, ax2, ax3, ax4, there are spaces for images
you need to create a image and put in ax1/2/3/4
i make a example that show how to work with a matplot

I understood after your comment about what you wanted to accomplish, to get a much more polished animation than what I am providing here would require planning out some math.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# some parameter to vary
vary = np.linspace(10, 120, 100, dtype=int)
n_bins = 100
n = 100
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, sharex='col')
def plot_prop():
# plot
ax1.set_ylim(0, 350)
ax1.set_title('normal')
ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)
ax1.spines['bottom'].set_visible(False)
ax1.spines['left'].set_visible(False)
ax1.tick_params(bottom=False, left=False)
ax2.set_ylim(0, 450)
ax2.set_title('exponential')
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax2.spines['bottom'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax2.tick_params(bottom=False, left=False)
ax3.set_xlim(-7, 2)
ax3.set_ylim(5, 25)
ax3.set_title('gamma')
ax3.spines['top'].set_visible(False)
ax3.spines['right'].set_visible(False)
ax3.spines['bottom'].set_visible(False)
ax3.spines['left'].set_visible(False)
ax3.tick_params(bottom=False, left=False)
ax4.set_xlim(0, 20)
ax4.set_title('uniform')
ax4.spines['top'].set_visible(False)
ax4.spines['right'].set_visible(False)
ax4.spines['bottom'].set_visible(False)
ax4.spines['left'].set_visible(False)
ax4.tick_params(bottom=False, left=False)
for i, p in enumerate(vary):
# generate 4 random variables
x = np.random.normal(-2.5, 1, p * 100)
y = np.random.gamma(2, 1.5, p * 100)
a = np.random.exponential(2, p * 100) + 7
b = np.random.uniform(14, 20, p * 100)
plot_prop()
ax1.hist(x, bins=n_bins, facecolor='c')
ax2.hist(y, bins=n_bins, facecolor='c')
ax3.scatter(x, a, facecolor='c')
ax4.scatter(y, b, facecolor='c')
plt.savefig('../imgs/img' + str(i) + '.png')
ax1.cla()
ax2.cla()
ax3.cla()
ax4.cla()
fig = plt.figure()
ims = []
for i in range(100):
im = plt.imshow(plt.imread('../imgs/img' + str(i) + '.png'), animated=True)
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=200, blit=True)
ani.save('../imgs/animation.mp4', writer=animation.FFMpegFileWriter(), dpi=300)
Here is a link to the animation I've got

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()

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?

getting only the last plot

when plotting by below code I am getting c,d,e plots but I am getting only the last plot for plt.plot
def normalize(x):
return (x - x.min(0)) / x.ptp(0)
c=sns.distplot(mk[0]['mass'], hist=True, label='p', rug=True)
d=sns.distplot(mk[1]['mass'], hist=True, label='q', rug=True)
e=sns.distplot(mk[2]['mass'], hist=True, label='r', rug=True)
datadist=[c,d,e]
xd=dict()
yd2=dict()
for i in datadist:
line = i.get_lines()[0]
xd[i] = line.get_xdata()
yd = line.get_ydata()
yd2[i] = normalize(yd)
plt.plot(xd[c], yd2[c],color='black')
plt.plot(xd[d], yd2[d],color='yellow')
plt.plot(xd[e], yd2[e],color='green')
sns.distplot() returns the ax (the subplot) on which the histogram was drawn. All 3 are drawn on the same subplot, so the return value is the same three times.
The array lines = ax1.get_lines() contains exactly 3 elements: one for each of the kde curves, so you can extract them as follows:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def normalize(x):
return (x - x.min(0)) / x.ptp(0)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 4))
sns.distplot(np.random.randn(30) + 10, hist=True, label='p', rug=True, ax=ax1, color='black')
sns.distplot(np.random.randn(30) + 15, hist=True, label='q', rug=True, ax=ax1, color='gold')
sns.distplot(np.random.randn(30) + 20, hist=True, label='r', rug=True, ax=ax1, color='green')
for line in ax1.get_lines():
ax2.plot(line.get_xdata(), normalize(line.get_ydata()), color=line.get_color())
plt.show()
Now, if you just want the kde-curves and "normalize" them, you could use scipy.stats import gaussian_kde:
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
def normalize(x):
return (x - x.min(0)) / x.ptp(0)
fig, ax = plt.subplots(figsize=(12, 4))
mk0mass = np.random.randn(30) + 10
mk1mass = np.random.randn(30) + 15
mk2mass = np.random.randn(30) + 20
all_mkmass = [mk0mass, mk1mass, mk2mass]
x = np.linspace(min([mki.min() for mki in all_mkmass]) - 2,
max([mki.max() for mki in all_mkmass]) + 2, 1000)
for mki, color in zip(all_mkmass, ['black', 'gold', 'green']):
kde = gaussian_kde(mki)
yd = normalize(kde(x))
ax.plot(x, yd, color=color)
ax.fill_between(x, 0, yd, color=color, alpha=0.3)
plt.show()

Fitting a multiline suptitle (not enough vertical space)

How do I create enough vertical space to contain a figure's title when using subplots?
See the code (and output) below,
import matplotlib.pylab as plt
import numpy as np
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(9, 3))
fig.suptitle("Figure 1: Some test title \n With a Second line \n And a Third line")
ax1 = fig.add_subplot(121, title="Model 1")
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
ax1.plot(x,y)
ax2 = fig.add_subplot(122, title="Model 2")
x = np.linspace(0, 2 * np.pi, 400)
y = np.cos(x ** 2)
ax2.plot(x,y)
fig.tight_layout()
fig.show()
You can pass the y argument in fig.suptitle():
import matplotlib.pylab as plt
import numpy as np
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(9, 3))
fig.suptitle("Figure 1: Some test title \n With a Second line \n And a Third line", y=1.2)
ax1 = fig.add_subplot(121, title="Model 1")
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
ax1.plot(x,y)
ax2 = fig.add_subplot(122, title="Model 2")
x = np.linspace(0, 2 * np.pi, 400)
y = np.cos(x ** 2)
ax2.plot(x,y)
fig.tight_layout()
fig.show()

How to share xaxis in contour-map-subplots which share a colorbar

I try to make 3 subplot which share one colorbar and the xaxis, as already explained by spinup in
Matplotlib 2 Subplots, 1 Colorbar
Using maps (with coastlines) in the subplots, it seems that a sharex is not supported.
However, is there a way, to apply a shared axis?
import cartopy.crs as ccrs
from cartopy.mpl.geoaxes import GeoAxes
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import AxesGrid
import numpy as np
def sample_data_3d(shape):
"""Returns `lons`, `lats`, `times` and fake `data`"""
ntimes, nlats, nlons = shape
lats = np.linspace(-np.pi / 2, np.pi / 2, nlats)
lons = np.linspace(0, 2 * np.pi, nlons)
lons, lats = np.meshgrid(lons, lats)
wave = 0.75 * (np.sin(2 * lats) ** 8) * np.cos(4 * lons)
mean = 0.5 * np.cos(2 * lats) * ((np.sin(2 * lats)) ** 2 + 2)
lats = np.rad2deg(lats)
lons = np.rad2deg(lons)
data = wave + mean
times = np.linspace(-1, 1, ntimes)
new_shape = data.shape + (ntimes, )
data = np.rollaxis(data.repeat(ntimes).reshape(new_shape), -1)
data *= times[:, np.newaxis, np.newaxis]
return lons, lats, times, data
def main():
projection = ccrs.PlateCarree()
axes_class = (GeoAxes,
dict(map_projection=projection))
lons, lats, times, data = sample_data_3d((6, 73, 145))
fig = plt.figure()
axgr = AxesGrid(fig, 111, axes_class=axes_class,
nrows_ncols=(3, 1),
axes_pad=0.6,
share_all=True, #doesn't change anything
cbar_location='bottom',
cbar_mode='single',
cbar_pad=0.2,
cbar_size='3%',
label_mode='') # note the empty label_mode
for i, ax in enumerate(axgr):
ax.coastlines()
ax.add_feature(cartopy.feature.LAND, zorder=100,
edgecolor='k',facecolor='w')
ax.set_xticks(np.linspace(-180, 180, 5), crs=projection)
ax.set_yticks(np.linspace(-90, 90, 5), crs=projection)
p = ax.contourf(lons, lats, data[i, ...],
transform=projection,
cmap='RdBu')
axgr.cbar_axes[0].colorbar(p)
plt.show()