multi animation whit subplot - matplotlib

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

Related

pyplot 3d z axis-log plot

In order to create a 3d plot using plot_surface and wireframe I wrote this (looking here around)
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import MultipleLocator
import matplotlib.ticker as mticker
import numpy as np
from matplotlib.ticker import FormatStrFormatter
def log_tick_formatter(val, pos=None):
return f"10$^{{{int(val)}}}$"
data=np.genfromtxt('jpdfomegal2_90.dat')
x_len= len(np.unique(data[:, 0]))
y_len= len(np.unique(data[:, 1]))
X = data[:, 0].reshape(x_len, y_len)
Y = data[:, 1].reshape(x_len, y_len)
Z = data[:, 2].reshape(x_len, y_len)
#identify lowest non-negative Z value Zmin>0
Zmin = np.where(Z > 0, Z, np.inf).min()
Zmax = Z.max()
#and substitute zero with a slightly lower value than Zmin
Z[Z==0] = 0.9 * Zmin
#log transformation because the conversion in 3D
#does not work well in matplotlib
Zlog = np.log10(Z)
rc('font',family='palatino')
rc('font',size=18)
fig = plt.figure(figsize=(12,8))
#ax = fig.add_subplot(projection='3d')
ax = Axes3D(fig)
ax.set_xlim3d(0,15)
ax.set_zlim3d(np.floor(np.log10(Zmin))-1, np.ceil(np.log10(10)))
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
rc('font',family='palatino')
rc('font',size=18)
tmp_planes = ax.zaxis._PLANES
ax.zaxis._PLANES = ( tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
ax.set_xlabel('$\omega^2 /<\omega^2>$')
ax.xaxis.labelpad = 10
ax.yaxis.labelpad = 10
ax.set_ylabel('cos$(\omega,\lambda^2)$')
ax.zaxis.set_rotate_label(False) # disable automatic rotation
ax.zaxis.labelpad = 10
ax.set_zlabel('')
ax.view_init(elev=17, azim=-60)
ax.grid(False)
ax.xaxis.pane.set_edgecolor('black')
ax.yaxis.pane.set_edgecolor('black')
ax.zaxis.pane.set_edgecolor('black')
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.set_major_locator(MultipleLocator(2))
ax.yaxis.set_major_locator(MultipleLocator(0.2))
ax.zaxis.set_major_locator(MultipleLocator(1))
#not sure this axis scaling routine is really necessary
scale_x = 1
scale_y = 1
scale_z = 0.8
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([scale_x, scale_y, scale_z, 1]))
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="--", offset=np.floor(np.log10(Zmin))-1)#-7)
surf = ax.plot_surface(X, Y, np.log10(Z), cmap="binary", lw=0.1,alpha=0.5)
ax.plot_wireframe(X, Y, np.log10(Z),linewidth=1,color='k')
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="solid")
fig.colorbar(surf, shrink=0.5, aspect=20)
plt.tight_layout()
plt.savefig('jpdf_lambda2_90.png', bbox_inches='tight')
plt.show()
the problem is related to the "minorticks" along zaxis .. I obtain this :
but I would have this format and ticks in the axis
Does somebody clarify how to obtain it and as well I did not find a way to use the log scale in pyplot 3d
There's an open bug on log-scaling in 3D plots, and it looks like there won't be a fix any time soon.
You can use a matplotlib.ticker.FixedLocator to add the z-axis minor ticks, as shown below.
I didn't have your data, so I've plotted an arbitrary surface.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import MultipleLocator, FixedLocator
import matplotlib.ticker as mticker
import numpy as np
from matplotlib.ticker import FormatStrFormatter
def log_tick_formatter(val, pos=None):
return f"10$^{{{int(val)}}}$"
x = np.linspace(1,15,15)
y = np.linspace(0,1,15)
X, Y = np.meshgrid(x, y)
Z = 1 + X**2 * Y**2
#identify lowest non-negative Z value Zmin>0
Zmin = np.where(Z > 0, Z, np.inf).min()
Zmax = Z.max()
#and substitute zero with a slightly lower value than Zmin
Z[Z==0] = 0.9 * Zmin
rc('font',family='palatino')
rc('font',size=18)
fig = plt.figure(figsize=(12,8))
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
ax.set_xlim3d(0,15)
ax.set_zlim3d(np.floor(np.log10(Zmin))-1, np.ceil(np.log10(Zmax)))
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
tmp_planes = ax.zaxis._PLANES
ax.zaxis._PLANES = ( tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
ax.set_xlabel('$\omega^2 /<\omega^2>$')
ax.xaxis.labelpad = 10
ax.yaxis.labelpad = 10
ax.set_ylabel('cos$(\omega,\lambda^2)$')
ax.zaxis.set_rotate_label(False) # disable automatic rotation
ax.zaxis.labelpad = 10
ax.set_zlabel('')
ax.view_init(elev=17, azim=-60)
ax.grid(False)
ax.xaxis.pane.set_edgecolor('black')
ax.yaxis.pane.set_edgecolor('black')
ax.zaxis.pane.set_edgecolor('black')
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.set_major_locator(MultipleLocator(2))
ax.yaxis.set_major_locator(MultipleLocator(0.2))
ax.zaxis.set_major_locator(MultipleLocator(1))
# Z minor ticks
zminorticks = []
zaxmin, zaxmax = ax.get_zlim()
for zorder in np.arange(np.floor(zaxmin),
np.ceil(zaxmax)):
zminorticks.extend(np.log10(np.linspace(2,9,8)) + zorder)
ax.zaxis.set_minor_locator(FixedLocator(zminorticks))
#not sure this axis scaling routine is really necessary
scale_x = 1
scale_y = 1
scale_z = 0.8
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([scale_x, scale_y, scale_z, 1]))
ax.contour(X, Y, np.log10(Z), 4, colors="k", linestyles="--", offset=np.floor(np.log10(Zmin))-1)#-7)
surf = ax.plot_surface(X, Y, np.log10(Z), cmap="binary", lw=0.1,alpha=0.5)
ax.plot_wireframe(X, Y, np.log10(Z),linewidth=1,color='k')
ax.contour(X, Y, np.log10(Z), 4, colors="k", linestyles="solid")
fig.colorbar(surf, shrink=0.5, aspect=20)
# get a warning that Axes3D is incompatible with tight_layout()
# plt.tight_layout()
# for saving
# fig.savefig('log3d.png')
plt.show()

Refreshing plot in matplotlib

As part of displaying the progression of a linear regression model fit, I need to be able to update/refresh an xy plot. Below is a simple script for 3 sets of y data, which need to be shown sequentially. However, they are piled up on top of each other. When fig.canvas.flush_events() is substituted with fig.clear() or fig.clf() the result is a blank plot. What am I - as a newbie -missing?
import torch as tc
import matplotlib.pyplot as plt
tc.manual_seed(1)
X=tc.linspace(-3,3,30)
y0=X.pow(2)+0.5*tc.randn(X.shape[0])
y1=y0/1.3
y2=y0/1.6
y=[y0,y1,y2]
fig=plt.figure()
ax=fig.add_subplot()
ax.set_xlim(-3.3,3.3)
ax.set_ylim(-0.5,9.5)
for i in range(3):
y_new=y[i]
ax.plot(X,y_new,'db')
fig.canvas.draw()
fig.canvas.flush_events()
plt.pause(1)
fig.show()
In your loop, you are creating a new line every time you call ax.plot. The better way is to create a Line2D artist and to update the coordinates of the point in the loop:
(NB i've converted your example to using numpy instead of torch)
import matplotlib.pyplot as plt
import numpy as np
X = np.linspace(-3, 3, 30)
y0 = np.power(X, 2) + 0.5 * np.random.randn(X.shape[0])
y1 = y0 / 1.3
y2 = y0 / 1.6
y = [y0, y1, y2]
fig = plt.figure()
ax = fig.add_subplot()
l, = ax.plot(X, y0, 'db')
ax.set_xlim(-3.3, 3.3)
ax.set_ylim(-0.5, 9.5)
for i in range(3):
y_new = y[i]
l.set_ydata(y_new)
fig.canvas.draw()
plt.pause(1)
plt.show()
For this kind of things, you'd be better off using the FuncAnimation module provided by maptlotlib though:
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
X = np.linspace(-3, 3, 30)
y0 = np.power(X, 2) + 0.5 * np.random.randn(X.shape[0])
y1 = y0 / 1.3
y2 = y0 / 1.6
y = [y0, y1, y2]
fig = plt.figure()
ax = fig.add_subplot()
l, = ax.plot(X, y0, 'db')
ax.set_xlim(-3.3, 3.3)
ax.set_ylim(-0.5, 9.5)
def animate(y_new):
l.set_ydata(y_new)
return l,
ani = animation.FuncAnimation(fig, func=animate, frames=y, interval=1000)
fig.show()

Real-time plotting of a custom turning marker

Is it somehow possible to plot a custom marker (like this) interactively, but have it turn in real-time? It seems that the scatter graph does not grant any access to the markers.
You can create a custom marker with a FancyArrowPatch. Many styles and options are possible. Such a patch is not easy to update, but you could just remove the patch and create it again to create an animation.
The easiest way to create an animation is via plt.pause(), but that doesn't work in all environments. Another way is via FuncAnimation, which involves a few more lines, but makes controlling the animation easier.
Here is some example code to show the concepts:
import matplotlib.pyplot as plt
from matplotlib import patches
from matplotlib.collections import PatchCollection
from matplotlib import animation
import numpy as np
fig, ax = plt.subplots()
N = 50
x = np.random.uniform(-20, 20, (N, 2))
dx = np.random.uniform(-1, 1, (N, 2))
dx /= np.linalg.norm(dx, axis=1, keepdims=True)
colors = plt.cm.magma(np.random.uniform(0, 1, N))
arrow_style = "Simple,head_length=2,head_width=3,tail_width=1"
ax.set_xlim(-40, 40)
ax.set_ylim(-30, 30)
ax.set_aspect('equal')
old_arrows = None
def animate(i):
global old_arrows, x, dx
if old_arrows is not None:
old_arrows.remove()
x += dx
dx += np.random.uniform(-.1, .1, (N, 2))
dx /= np.linalg.norm(dx, axis=1, keepdims=True)
arrows = [patches.FancyArrowPatch((xi, yi), (xi + dxi * 10, yi + dyi * 10), arrowstyle=arrow_style)
for (xi, yi), (dxi, dyi) in zip(x, dx)]
old_arrows = ax.add_collection(PatchCollection(arrows, facecolors=colors))
return old_arrows,
ani = animation.FuncAnimation(fig, animate, np.arange(1, 200),
interval=25, repeat=False, blit=True)
plt.show()
I solved it by remove() and static variables like this:
class pltMarker:
def __init__(self, angle=None, pathString=None):
self.angle = angle or []
self.pathString = pathString or """simply make and svg, open in a text editor and copy the path XML string in here"""
self.path = parse_path( self.pathString )
self.path.vertices -= self.path.vertices.mean( axis=0 )
self.marker = mpl.markers.MarkerStyle( marker=self.path )
self.marker._transform = self.marker.get_transform().rotate_deg(angle)
def rotate(self, angle=0):
self.marker._transform = self.marker.get_transform().rotate_deg(angle)
def animate(k):
angle = ... # new angle
myPltMarker.rotate(angle)
animate.Scatter.remove()
animate.Scatter = plt.scatter(1, 0, marker=myPltMarker.marker, s=100)
return animate.Scatter,
angle = ...
myPltMarker = pltMarker(angle=angle)
animatePlt.Scatter = plt.scatter(1, 0, marker=myPltMarker.marker, s=100)
anm = animation.FuncAnimation(fig, animate, blit=False, interval=1)
plt.show()

how to plot gradient fill on the 3d bars in matplotlib

Right now there're some statistics plotted in 3d bar over (x, y). each bar height represents the density of the points in side the square grid of (x,y) plane. Right now, i can put different color on each bar. However, I want to put progressive color on the 3d bar, similar as the cmap, so the bar will be gradient filled depending on the density.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# height of the bars
z = np.ones((4, 4)) * np.arange(4)
# position of the bars
xpos, ypos = np.meshgrid(np.arange(4), np.arange(4))
xpos = xpos.flatten('F')
ypos = ypos.flatten('F')
zpos = np.zeros_like(xpos)
dx = 0.5 * np.ones_like(zpos)
dy = dx.copy()
dz = z.flatten()
ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', zsort='average')
plt.show()
Output the above code:
Let me first say that matplotlib may not be the tool of choice when it comes to sophisticated 3D plots.
That said, there is no built-in method to produce bar plots with differing colors over the extend of the bar.
We therefore need to mimic the bar somehow. A possible solution can be found below. Here, we use a plot_surface plot to create a bar that contains a gradient.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection= Axes3D.name)
def make_bar(ax, x0=0, y0=0, width = 0.5, height=1 , cmap="viridis",
norm=matplotlib.colors.Normalize(vmin=0, vmax=1), **kwargs ):
# Make data
u = np.linspace(0, 2*np.pi, 4+1)+np.pi/4.
v_ = np.linspace(np.pi/4., 3./4*np.pi, 100)
v = np.linspace(0, np.pi, len(v_)+2 )
v[0] = 0 ; v[-1] = np.pi; v[1:-1] = v_
x = np.outer(np.cos(u), np.sin(v))
y = np.outer(np.sin(u), np.sin(v))
z = np.outer(np.ones(np.size(u)), np.cos(v))
xthr = np.sin(np.pi/4.)**2 ; zthr = np.sin(np.pi/4.)
x[x > xthr] = xthr; x[x < -xthr] = -xthr
y[y > xthr] = xthr; y[y < -xthr] = -xthr
z[z > zthr] = zthr ; z[z < -zthr] = -zthr
x *= 1./xthr*width; y *= 1./xthr*width
z += zthr
z *= height/(2.*zthr)
#translate
x += x0; y += y0
#plot
ax.plot_surface(x, y, z, cmap=cmap, norm=norm, **kwargs)
def make_bars(ax, x, y, height, width=1):
widths = np.array(width)*np.ones_like(x)
x = np.array(x).flatten()
y = np.array(y).flatten()
h = np.array(height).flatten()
w = np.array(widths).flatten()
norm = matplotlib.colors.Normalize(vmin=0, vmax=h.max())
for i in range(len(x.flatten())):
make_bar(ax, x0=x[i], y0=y[i], width = w[i] , height=h[i], norm=norm)
X, Y = np.meshgrid([1,2,3], [2,3,4])
Z = np.sin(X*Y)+1.5
make_bars(ax, X,Y,Z, width=0.2, )
plt.show()

matplotlib polar 2d histogram

I am trying to plot some histogrammed data on a polar axis but it wont seem to work properly. An example is below, I use the custom projection found How to make the angles in a matplotlib polar plot go clockwise with 0° at the top? it works for a scatter plot so I think my problem is with the histogram function. This has been driving me nuts all day, does anyone know what I am doing wrong...........
import random
import numpy as np
import matplotlib.pyplot as plt
baz = np.zeros((20))
freq = np.zeros((20))
pwr = np.zeros((20))
for x in range(20):
baz[x] = random.randint(20,25)*10
freq[x] = random.randint(1,10)*10
pwr[x] = random.randint(-10,-1)*10
baz = baz*np.pi/180.
abins = np.linspace(0,2*np.pi,360) # 0 to 360 in steps of 360/N.
sbins = np.linspace(1, 100)
H, xedges, yedges = np.histogram2d(baz, freq, bins=(abins,sbins), weights=pwr)
plt.figure(figsize=(14,14))
plt.subplot(1, 1, 1, projection='northpolar')
#plt.scatter(baz, freq)
plt.pcolormesh(H)
plt.show()
Your code works if you explicitly pass a mgrid (with similar characteristics than your a bins and sbins) to the pcolormesh command.
Below is an example inspired by your code:
import matplotlib.pyplot as plt
import numpy as np
#Generate the data
size = 200
baz = 10*np.random.randint(20, 25, size)*np.pi/180.
freq = 10*np.random.randint(1, 10, size)
pwr = 10*np.random.randint(-10, -1, size)
abins = np.linspace(0, 2*np.pi, 360) # 0 to 360 in steps of 360/N.
sbins = np.linspace(1, 100, 50)
H, xedges, yedges = np.histogram2d(baz, freq, bins=(abins,sbins), weights=pwr)
#Grid to plot your data on using pcolormesh
theta, r = np.mgrid[0:2*np.pi:360j, 1:100:50j]
fig, ax = plt.subplots(figsize=(14,14), subplot_kw=dict(projection='northpolar'))
ax.pcolormesh(theta, r, H)
ax.set_yticklabels([]) #remove yticklabels
plt.show()