Matplotlib - Animations: How do I do two coupled animations within two subplots? - python

from here:
Animation using matplotlib with subplots and ArtistAnimation
I got some hints on my question - however not enough. My problem:
I have two animations both somehow coupled and want to show each of them in a different subplot.
the first animation in the first subplot works fine - however the second (coupled to the first) animation in the second subplot just influences the first animation ...
So how do I decouple the subplots in a way that the second subplot doew NOT influence the first one:
here is the code of the example:
import math
from ClimateUtilities import *
import phys
import numpy as nm
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from matplotlib import patches
#from matplotlib import animation
#------------Constants and file data------------
#
printswitch = True
printswitch = False
printswitch2 = True
#printswitch2 = False
ECCabsoluteMax = 0.9
ECCmax = 0.067 # maximum value for this run -
# should not be greater than
# ECCabsoluteMax
#ECCmax = 0.9 # maximum value for this run - should not be greater
# than
# ECCabsoluteMax
if ECCmax >= ECCabsoluteMax:
ECCmax = ECCabsoluteMax
ECCdelta = 0.001 # interval for graph
eccentricity = nm.arange(0., ECCmax, ECCdelta, dtype=float)
semimajorA = 1.0 # astronomical unit =~ 150.000.000 km mean
# distance Sun Earth
totalRadN0 = 1370. # radiation of Sun at TOA in Watt/m**2
albedoEarth = 0.3 # presently albedo of Earth, geographically
# constant
T = 365.25 # duration of one orbit around central celestial
# body in days
# here: duration of one orbit of Earth around Sun
R = 6378100.0 # radius of Earth in meters
TOIdim = ECCmax/ECCdelta
TOI = nm.arange(0., TOIdim, dtype=float )
# total insolation at location of Earth summed over 1
# year
deltaT = 500 # ms interval of moving
# now define various "functions" like:
def computeTOI( ee, semimajorAxis, radiationAtStar, alpha ):
aa = semimajorAxis # semimajor axis of orbital ellipse
N0 = radiationAtStar# radiation of start at position of star (r = 0)
resultTOI = 2.*nm.pi*T*R**2*N0*alpha/(aa**2*math.sqrt(1 - ee**2))
return resultTOI
#
#####################################################################
#
print "start of ellipticity and absorbed insolation"
#
#
# Start of programme here
#
#####################################################################
# compute the various TOIs dependant on eccentricity "ecc"
#
ii = 0
for ecc in eccentricity:
if printswitch: print 'TOI = ', computeTOI( ecc, semimajorA,
totalRadN0, albedoEarth ), '\n'
TOI[ii] = computeTOI( ecc, semimajorA, totalRadN0, 1. - albedoEarth
)/10.0**19
ii = ii + 1
# TOI is an array consisting of TOIs depending on eccemtricity "ecc"
x = eccentricity
if printswitch: print 'TOI = ', TOI
##########################################################################
# almost the whole screen is filled with this plot ... :)
##########################################################################
Main = plt.figure(figsize=(15.0,15.0))
Main.subplots_adjust(top=0.95, left=0.09, right=0.95, hspace=0.20)
##########################################################################
axFigTOI = Main.add_subplot(211) # first subplot
# Plot ... TOI over ECC:
if ECCmax < 0.07:
plt.axis([0,0.07,8.9,9.0])
plt.title( 'Absorbed Irradiation and Orbital Eccentricity for Planet
Earth\n' )
plt.ylabel( 'Absorbed total \nsolar irradiation \n[Watt] *10**19' )
plt.xlabel( 'Eccentricity "e"' )
plt.plot( x, TOI, 'r-' ) # 'x' and 'TOI' are also center of "mini-
# ellipse"
# Now enter an ellipse here on Subplot 211 (first subplot) which slides
# along curve:
xcenter, ycenter = x[1],TOI[1] # center of ellipse to start with
width = 0.0025 # width of small ellipse
height = 0.01 # height of small ellipse
def init(): # in order to initialize animation
e1 = patches.Ellipse((xcenter, ycenter), width, height,\
angle=0.0, linewidth=2, fill=False )
axFigTOI.add_patch(e1)
e1.set_visible( False ) # do not show (if True then ellipse
# stays here
return [e1]
def animateEllipse(i):
xcenter = x[i]
ycenter = TOI[i]
e1 = patches.Ellipse( ( xcenter, ycenter ), width, height,\
angle = 0.0, linewidth = 2, fill = True )
if i == 1:
e1.set_visible( True )
axFigTOI.add_patch(e1)
if printswitch: print 'i = ', i
return [e1]
anim = animation.FuncAnimation( Main,
animateEllipse,
init_func=init,
frames= int( TOIdim ),
interval=deltaT,
blit=True )
#########################################################################
# the second subplot in the first figure for size of ellipse depending on
# ECC
#########################################################################
# we still have a problem to get the "patch" (Ellipse) into the 2nd
# subplot ...
axFigEllipse = Main.add_subplot(212)
plt.title( 'Shape of an Ellipse due to eccentricity' )
plt.ylabel( 'Height of Ellipse' )
plt.xlabel( 'Constant Semi-major Axis' )
"""
#
# create an ellipse with following parameters - to be changed later for
# curve
# values
#
xcenter2 = x[40]
ycenter2 = TOI[40] # center of ellipse 2 to start with
width2 = 0.0125
height2 = 0.0115
ell2 = patches.Ellipse( ( xcenter2, ycenter2 ), width2, height2,\
angle=0.0, linewidth=2, fill=False )
ell2.set_visible(True)
axFigEllipse.add_patch(ell2)
#"""
"""
def init212(): # in order to initialize animation
ell2 = patches.Ellipse((xcenter2, ycenter2), width2, height2,\
angle=0.0, linewidth=2, fill=False )
axFigEllipse.add_patch(ell2)
ell2.set_visible( False ) # do not show (if True then ellipse
# stays here
return [ell2]
def animateEllipse(jj):
#xcenter2 = xcenter2 + jj/10**4
#ycenter2 = ycenter2 + jj/10**4
ell2 = patches.Ellipse((xcenter2, ycenter2), width2, height2,\
angle=0.0, linewidth=2, fill=True, zorder=2)
if jj == 1:
ell2.set_visible(True)
axFigEllipse.add_patch(ell2)
return [ell2]
anim = animation.FuncAnimation( Main, animateEllipse,
init_func=init212,
frames=360,
interval=20,
blit=True )
#anim = animation.FuncAnimation(figEllipse, animateEllipse,
init_func=init_Ellipse, interval=1, blit=True)
#"""
plt.show()
Now when I remove the """ then there is only the red line visible ... no activity ...

In your code, you essentially redefine animateEllipse later. You should only call a single animate in a script which should update both ellipses (return both handles from the objects). For resizing you can just use the existing ellipse handle but moving appears to need a new ellipse to be added. I couldn;t get your code working but as a minimal example a function to update all subplots (these can each be in their own functions).
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
from matplotlib import animation
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
e1 = Ellipse(xy=(0.5, 0.5), width=0.5, height=0.5, angle=0)
e2 = Ellipse(xy=(0.5, 0.5), width=0.5, height=0.5, angle=0)
ax1.add_patch(e1)
ax2.add_patch(e2)
def init():
e1.set_visible(False)
e2.set_visible(False)
return e1,e2
def animateEllipse211(i):
e1 = Ellipse(xy=(0.5+0.2*np.sin(i/200.), 0.5+0.2*np.sin(i/200.)), width=0.5, height=0.5, angle=0)
ax1.add_patch(e1)
if i==0:
e1.set_visible(True)
return e1
def animateEllipse212(i):
if i==0:
e2.set_visible(True)
e2.width = 0.5*np.sin(i/200.)
e2.height = 0.5*np.sin(i/200.)
return e2
def animate(i):
e1 = animateEllipse211(i)
e2 = animateEllipse212(i)
return e1,e2
anim = animation.FuncAnimation(fig, animate, init_func=init, interval=1, blit=True)
plt.show()
UPDATE: I'm not sure why this strange init problem occurs but think it has been noted on a few other posts (this and this) to be due to using blit=True. The animation on matplotlib is a little rough around the edges and certainly isn't very intuitive. Worse than that, the choice of backend (i.e. what plots the actual data) makes a difference to the way it works. Personally I normally run a loop, use interactive mode and save figures if I need a video.
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
import numpy as np
#Setup figure, add subplots and ellipses
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
e1 = Ellipse(xy=(0.5, 0.5), width=0.5, height=0.5, angle=0)
e2 = Ellipse(xy=(0.5, 0.5), width=0.5, height=0.5, angle=0)
ax1.add_patch(e1)
ax2.add_patch(e2)
#Plot Red line
ax1.plot(np.linspace(.3,.7,100),np.linspace(.3,.7,100),'r-')
#Turn on interactive plot
plt.ion()
plt.show()
#Define a loop and update various
for i in range(0, 10000, 10):
print(i)
#Update ellipse 1
e1.remove()
e1 = Ellipse(xy=(0.5+0.2*np.sin(i/200.),
0.5+0.2*np.sin(i/200.)),
width=0.5, height=0.5, angle=0)
ax1.add_patch(e1)
#Update ellipse 2
e2.width = 0.5*np.sin(i/200.)
e2.height = 0.5*np.sin(i/200.)
plt.draw()
plt.pause(0.0001)

Related

Is there an easy way to print a Matplotlib figure (Saros Dial) to an exact size on a DIN A4 jpeg?

As part of a COVID/Lockdown/Geek project making a 3D Antikythera model I need to print a Saros Dial to be exactly 8.9 cm in width. I have muddled my way through playing around with the print scale. This works for the office printer. However I want to have it etched on plexiglas and I need to upload a DIN A4 jpeg or pdf with the (0,0) coordinate right in the centre
Code for the dial below:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# define parameters
a = 2 # determines the width of the turns - set to 1 means width is 2 Pi
thetaMin, thetaMax = 26*np.pi, 34*np.pi # Dial starts at the 13th turn and finishes at the 17th turn
steps = 223+1 # Number of Saros periods (+1 for end marker)
# Generate plotting values
# Main spiral
theta = np.linspace(thetaMin, thetaMax, steps)
r = theta * a
x = r*np.cos(theta)
y = r*np.sin(-theta)
# Main spiral outer rim completion
thetaMinComp, thetaMaxComp = thetaMax, thetaMax+(2*np.pi)
thetaComp= np.linspace(thetaMinComp, thetaMaxComp,steps)
rComp = thetaComp * a
xComp = rComp*np.cos(thetaComp)
yComp = rComp*np.sin(-thetaComp)
# Seperator lines
# Need to plot between the period startpoint accros the turn(out from the center (0,0))
l=np.sqrt(np.square(x)+np.square(y)+1.2) # calculate length from center, just shortening it makes it not overshoot
xnew= x*1/l*(l+2*np.pi*a) # get the x for the line extendeaccros the turn
ynew= y*1/l*(l+2*np.pi*a) # get the y for the line extendeaccros the turn
#start plotting
fig, ax = plt.subplots(figsize=(20,20))
plt.axis('off')
for i in range(len(x)):
plt.plot([x[i],xnew[i]],[y[i],ynew[i]], c='r', lw=2)
# plt.scatter(x[i], y[i], c = 'g') # Plots dial points for reference
steps = 223+1 # Number of Saros periods (+1 for end marker)
a = 2 # determines the width of the turns - set to 1 means width is 2 Pi
thetaMin, thetaMax = (26*np.pi+(2*np.pi)/(.5*steps)), (34*np.pi+(2*np.pi)/(.5*steps)) # Dial starts at the 13th turn and finishes at the 17th turn
theta = np.linspace(thetaMin, thetaMax, steps)
r = theta * a
xx = r*np.cos(theta)
yy = r*np.sin(-theta)
xxnew= xx*1/l*(l+2*np.pi*(a*.5)) # get the x for the line extendeaccros the turn
yynew= yy*1/l*(l+2*np.pi*(a*.5)) # get the y for the line extendeaccros the turn
plt.plot(x, y, c='r', lw=2)
plt.plot(xComp, yComp,c='r', lw=2)
plt.scatter(0,0)
plt.text(0,75,'Saros Dial', {'fontname': 'Herculanum',
'fontsize': '100',
'fontweight' : 'bold',
'verticalalignment': 'baseline',
'horizontalalignment': 'center'})
plt.show()
I have found Using matplotlib, how can I print something "actual size"? but is still fiddling. Is there an easier way?
This proved to be frustrating. It is printer dependent...and online printer specifications for the margins may not match the actual margins when printing. I have a hunch it may even be OS or driver specific.
import matplotlib.pyplot as plt
import matplotlib as mpl
# This example fits DIN A4 paper on a HP Laserjet Pro 200 MFP
# figure settings
left_margin = 0.2
right_margin = 0.9
top_margin = 0.6
bottom_margin = 0.6
left_right_margin = left_margin+right_margin
top_bottom_margin = top_margin+bottom_margin
figure_width = 21-left_right_margin # cm
figure_height = 29.7-top_bottom_margin
# Don't change
left = left_right_margin / figure_width # Percentage from height
bottom = top_bottom_margin / figure_height # Percentage from height
width = 1 - left*2
height = 1 - bottom*2
cm2inch = 1/2.54 # inch per cm
h_corr = 1.017
v_corr = 1.011
# specifying the width and the height of the box in inches
fig = plt.figure(figsize=(figure_width*cm2inch*h_corr,figure_height*cm2inch*v_corr))
ax = fig.add_axes((left, bottom, width, height))
# limits settings (important)
# plt.xlim(0, figure_width * width)#(0,0) at left bottom
# plt.ylim(0, figure_height * height)#(0,0) at left bottom
plt.xlim(-figure_width * width/2, figure_width * width/2) #centers (0,0)
plt.ylim(-figure_height * height/2, figure_height * height/2)#centers (0,0)
# your Plot (consider above limits)
# # define parameters
a = .03538 # determines the width of the turns - set to 1 means width is 2 Pi
thetaMin, thetaMax = 26*np.pi, 34*np.pi # Dial starts at the 13th turn and finishes at the 17th turn
steps = 223+1 # Number of Saros periods (+1 for end marker)
# Generate plotting values
# Main spiral
theta = np.linspace(thetaMin, thetaMax, steps)
r = theta * a
x = r*np.cos(theta)
y = r*np.sin(-theta)
# Main spiral outer rim completion
thetaMinComp, thetaMaxComp = thetaMax, thetaMax+(2*np.pi)
thetaComp= np.linspace(thetaMinComp, thetaMaxComp,steps)
rComp = thetaComp * a
xComp = rComp*np.cos(thetaComp)
yComp = rComp*np.sin(-thetaComp)
# Seperator lines
# Need to plot between the period startpoint accros the turn(out from the center (0,0))
l=np.sqrt(np.square(x)+np.square(y)+1.2) # calculate length from center, just shortening it makes it not overshoot
xnew= x*1/l*(l+2*np.pi*a) # get the x for the line extendeaccros the turn
ynew= y*1/l*(l+2*np.pi*a) # get the y for the line extendeaccros the turn
for i in range(len(x)):
plt.plot([x[i],xnew[i]],[y[i],ynew[i]], c='r', lw=1)
xx = r*np.cos(theta)
yy = r*np.sin(-theta)
xxnew= xx*1/l*(l+2*np.pi*(a*.5)) # get the x for the line extendeaccros the turn
yynew= yy*1/l*(l+2*np.pi*(a*.5)) # get the y for the line extendeaccros the turn
plt.plot(x, y, c='b', lw=1)
plt.plot(xComp, yComp,c='g', lw=1)
plt.scatter(0,0)
plt.text(0,4.75,'Saros Dial',
{'fontname': 'Herculanum',
'fontsize': '30',
'fontweight' : 'bold',
'verticalalignment': 'baseline',
'horizontalalignment': 'center'})
plt.plot([-8,-8],[-10,10])
plt.text(-7,0, ' 20 centimeters' , rotation=90, ha='center', va='center', fontsize=30, color='magenta')
plt.show()
plt.show()
# save figure ( printing png file had better resolution, pdf was lighter and better on screen)
fig.savefig('A4_grid_cm.png', dpi=1000)
fig.savefig('tA4_grid_cm.pdf')

Why does the animation display under the plotted curve in matplotlib FuncAnimation function when I save the video?

I would like to plot a phase diagram with some spots moving along individual phase diagram trajectories, but it turns out that those spots are moving behind trajectories after I save it (it looks good if I just use plt.show()). At first, I thought if it was because I use plot() after calling FuncAnimation, but it didn't make any differences when I change it, and it doesn't seem to due to scatter() I used (it is the same when I use plot() for animation)
As is shown in the picture, spots moving along the trajectories are behind those curves, which is not observable when curves are dense.
Here is the code
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.rcParams['animation.ffmpeg_path'] = r'E:\ffmpeg\bin\ffmpeg.exe'
from matplotlib.animation import FuncAnimation
UIdata = np.loadtxt('data_omega_bar_uncoupled.txt')
# UIdata = UIdata[:,15000:20001]
n = int(np.shape(UIdata)[0]/2)
length = int(np.shape(UIdata)[1])
data = []
for k in range(n):
data_sep = np.zeros((2,length))
for i in range(length):
for j in range(2):
if j == 0:
#set values for Re
data_sep[j][i] = UIdata[k][i]
else:
#set values for Im
data_sep[j][i] = UIdata[k+n][i]
data_sep = data_sep[:,35000:40000]
data.append(data_sep)
N = np.shape(data_sep)[1]
c = np.array([0.0,0.1,0.9,0.7,0.5,0.55,0.3,0.2])
mpl.rcParams['axes.linewidth']=1.0
mpl.rcParams['xtick.labelsize']='20.0'
mpl.rcParams['ytick.labelsize']='20.0'
mpl.rcParams['axes.titlesize']='30.0'
mpl.rcParams['axes.titlepad']='15.0'
mpl.rcParams['axes.labelsize']='25.0'
mpl.rcParams['axes.labelpad']='12.0'
plt.rcParams['font.family'] = 'Times New Roman'
fig = plt.figure(figsize=(20,20))
xmax = 65
ymax = 65
ax = fig.add_subplot(111, xlim=(-xmax,xmax), ylim=(-ymax,ymax))
ax.set_xlabel('X ')
ax.set_ylabel('Y ')
ax.set_title('Phase diagram in rotational frame uncoupled')
yy0, xx0 = UIdata[8][15000:35001], UIdata[0][15000:35001]
yy1, xx1 = UIdata[9][15000:35001], UIdata[1][15000:35001]
yy2, xx2 = UIdata[10][15000:35001], UIdata[2][15000:35001]
yy3, xx3 = UIdata[11][15000:35001], UIdata[3][15000:35001]
yy4, xx4 = UIdata[12][15000:35001], UIdata[4][15000:35001]
yy5, xx5 = UIdata[13][15000:35001], UIdata[5][15000:35001]
yy6, xx6 = UIdata[14][15000:35001], UIdata[6][15000:35001]
yy7, xx7 = UIdata[15][15000:35001], UIdata[7][15000:35001]
ax.plot(xx7,yy7,color='yellow',linewidth=1,label='67.4kHz')
ax.plot(xx6,yy6,color='green',linewidth=1,label='61.8kHz')
ax.plot(xx5,yy5,color='tab:blue',linewidth=1,label='56.1kHz')
ax.plot(xx4,yy4,color='cyan',linewidth=1,label='50.5kHz')
ax.plot(xx3,yy3,color='blue',linewidth=1,label='44.9kHz')
ax.plot(xx2,yy2,color='magenta',linewidth=1,label='39.3kHz')
ax.plot(xx1,yy1,color='wheat',linewidth=1,label='33.6kHz')
ax.plot(xx0,yy0,color="lightcoral",linewidth=1,label='28.0kHz')
ax.legend()
oscillators = ax.scatter([], [], c=[], cmap="hsv", s=25, vmin=0, vmax=1)
def animate(i):
thisx = [data[0][0][i],data[1][0][i],data[2][0][i],data[3][0][i],data[4][0][i],
data[5][0][i],data[6][0][i],data[7][0][i]]
# thisx = [1,2,3,4,5,6,7,8]
thisy = [data[0][1][i],data[1][1][i],data[2][1][i],data[3][1][i],data[4][1][i],
data[5][1][i],data[6][1][i],data[7][1][i]]
this = [thisx,thisy]
this = np.array(this).T
oscillators.set_offsets(this)
oscillators.set_array(c)
return oscillators,
anim = FuncAnimation(fig, animate, N, interval=20, blit=True)
plt.show()
anim.save('phase diagram in rotating frame uncoupled.mp4',writer='ffmpeg')

Changing animation parameters interactively in matplotlib

I need to animate something rather simple but I do need as well a slider to let the user interactively changes the parameters of the animation. I'd like it to happen online; i.e. if the user changes a parameter while the animation is playing, the animation should transit smoothly from its old dynamics to its new one.
So far I've written a function that takes arguments and makes an animation. But it's not interactive in the sense I mentioned before. There are no sliders or anything really interactive in my code. Nevertheless, the animation part is running smoothly at least.
Here is a simplified version of my code: A point revolves around the center with a specified distance r from the center and an angular velocity w. User can give them as arguments to see the animation. (If you saw something in the code that is never used don't bother yourself with it, it is probably because I forgot to trim it from the original code with is much longer.)
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def simplified_code(w,r):
fps = 36
M = int(.75*fps*2*np.pi/w)
T_final = 2*np.pi/w*16
def positions(t):
x = r*np.cos(w*t)
y = r*np.sin(w*t)
return x,y
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, )
ax.grid()
# position
x_e, y_e = [], []
# trajectory and center
traj_e, = plt.plot([],[],'-g',lw=1)
e, = plt.plot([], [], 'ok')
# time
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes)
def init():
ax.set_xlim(-(r+0.5), (r+0.5))
ax.set_ylim(-(r+0.5), (r+0.5))
ax.plot([0], ms=7, c='k',marker='o')
return d,e,traj_e
def update(frame):
x,y = positions(frame)
x_e.append(x)
y_e.append(y)
traj_e.set_data(x_e[-M:], y_e[-M:])
e.set_data(x, y)
time_text.set_text('time = %.1f' % frame)
return traj_d,traj_e,d,e, orb
return FuncAnimation(fig, update, frames=np.linspace(0, T_final, T_final*fps),
init_func=init, blit=True, interval=1./36*1000)
Note that it's possible to stop the animation, change the parameters via a slider and rerun it. I want to avoid this pause in the animation. I'd appreciate any help.
Thanks to #ImportanceOfBeingErnest I managed to combine update function of the animation and the one with the sliders:
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider
fig = plt.figure(figsize=(6,7))
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, position=[.15,.15,.75,.75] )
ax.grid()
w = 2
r = 1
fps = 36
M= 1024#
T_final = 256
x_e, y_e = [], []
orb_x, orb_y = [], []
# trajectories
traj_e, = ax.plot(x_e,y_e,'-g',lw=1)
# center
e, = ax.plot([], [], 'ok')
# orbit
orb, = ax.plot([], [], '.r',ms=1)
# time
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes)
def positions(t):
x = r*np.cos(w*t) # epicycle
y = r*np.sin(w*t) # epicycle
return x,y
def orbit(r):
phi = np.linspace(0, 2*np.pi, 360)
orb_x = r*np.cos(phi)
orb_y = r*np.sin(phi)
return orb_x,orb_y
def init():
ax.plot([0], ms=7, c='k',marker='o')
return e,traj_e
def update(t):
global r, w
w = s_w.val
r = s_r.val
ax.set_xlim(-(r)*1.1, (r)*1.1)
ax.set_ylim(-(r)*1.1, (r)*1.1)
x,y = positions(t)
x_e.append(x)
y_e.append(y)
traj_e.set_data(x_e[-M:-1], y_e[-M:-1])
orb.set_data(orbit(r))
e.set_data(x, y)
time_text.set_text('time = %.1f' % t)
return traj_e,e, orb
ax_w = plt.axes([0.1, 0.05, 0.35, 0.03])#, facecolor=axcolor)
ax_r = plt.axes([0.55, 0.05, 0.35, 0.03])#, facecolor=axcolor)
s_w = Slider(ax_w, r'$\omega$', -20, 20, valinit=w, valstep=0.2)
s_r = Slider(ax_r, r'r', 0, 5, valinit=r, valstep=0.2)
s_w.on_changed(update)
s_r.on_changed(update)
def anim():
fig.canvas.draw_idle()
return FuncAnimation(fig, update, frames=np.linspace(0, T_final, T_final*fps),
init_func=init, blit=True, interval=30)
anim()
Using this piece of code I can change the values of r and w without pausing or restarting the animation from scratch. Another problem appears with this code though which is that the point jumps to some random position on the circle and then jumps back the expected trajectory. I'd address it in another question.

Saving Animation in matplotlib of Progress Line

Objective: To save a plotted graph(x,y) with a moving a vertical line over the graph w.r.t to timer.
I've built a simple graph with a moving vertical progress line basically identical to this example, but I now need to save it. I do not need the functionality to relocate the progress bar on-click, only to have the progress bar move along the data. I'm not sure where to call the save function because this code does not rely on FuncAnimation.
import sys
import matplotlib.pyplot as plt
import time
import numpy
fig = plt.figure()
ax = fig.add_subplot(111)
max_height = 100
n_pts = 100
y1 = [0, max_height]
x1 = [0, 0]
y = numpy.random.randn(n_pts) * max_height
x = numpy.arange(0, n_pts)
# draw the data
line1, = ax.plot(x, y, color='black')
# fix the limits of the plot
ax.set_ylim(0, max_height)
ax.set_xlim(0, n_pts)
# draw the plot so that we can capture the background and then use blitting
plt.show(block=False)
# get the canvas object
canvas = ax.figure.canvas
background = canvas.copy_from_bbox(ax.bbox)
# add the progress line.
# XXX consider using axvline
line, = ax.plot(x1, y1, color='r', animated=True)
starttime=time.time()
mytimer=0
mytimer_ref=0
def update(canvas, line, ax):
# revert the canvas to the state before any progress line was drawn
canvas.restore_region(background)
# compute the distance that the progress line has made (based on running time)
t = time.time() - starttime
mytimer = t + mytimer_ref
x1 = [mytimer,mytimer]
# update the progress line with its new position
line.set_xdata(x1)
# draw the line, and blit the axes
ax.draw_artist(line)
canvas.blit(ax.bbox)
timer=fig.canvas.new_timer(interval=100)
args=[canvas,line,ax]
timer.add_callback(update,*args) # every 100ms it calls update function
timer.start()
plt.show()

Custom scale for radial contour plot in matplotlib

I have a sample script to generate a polar contour plot in matplotlib:
import os
import math
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.projections import PolarAxes
from mpl_toolkits.axisartist.grid_finder import FixedLocator, MaxNLocator, DictFormatter
import random
# ------------------------------------ #
def setup_arc_radial_axes(fig, rect, angle_ticks, radius_ticks, min_rad, max_rad):
tr = PolarAxes.PolarTransform()
pi = np.pi
grid_locator1 = FixedLocator([v for v, s in angle_ticks])
tick_formatter1 = DictFormatter(dict(angle_ticks))
grid_locator2 = FixedLocator([a for a, b in radius_ticks])
tick_formatter2 = DictFormatter(dict(radius_ticks))
grid_helper = floating_axes.GridHelperCurveLinear(tr,
extremes=((370.0*(pi/180.0)), (170.0*(pi/180.0)), max_rad, min_rad),
grid_locator1=grid_locator1,
grid_locator2=grid_locator2,
tick_formatter1=tick_formatter1,
tick_formatter2=tick_formatter2,
)
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
ax1.grid(True)
# create a parasite axes whose transData in RA, cz
aux_ax = ax1.get_aux_axes(tr)
aux_ax.patch = ax1.patch
ax1.patch.zorder=0.9
#ax1.axis["left"].set_ticklabel_direction("+")
return ax1, aux_ax
# ------------------------------------ #
# write angle values to the plotting array
angles = []
for mic_num in range(38):
angle = float(mic_num)*(180.0/36.0)*(math.pi/180.0)+math.pi
angles.append(angle)
# ------------------------------------ #
### these are merely the ticks that appear on the plot axis
### these don't actually get plotted
angle_ticks = range(0,190,10)
angle_ticks_rads = [a*math.pi/180.0 for a in angle_ticks]
angle_ticks_rads_plus_offset = [a+math.pi for a in angle_ticks_rads]
angle_ticks_for_plot = []
for i in range(len(angle_ticks)):
angle_ticks_for_plot.append((angle_ticks_rads_plus_offset[i],r"$"+str(angle_ticks[i])+"$"))
# ------------------------------------ #
scale = 1.0
aspect = 1.50
height = 8.0
fig = plt.figure(1, figsize=(height*aspect*scale, height*scale))
fig.subplots_adjust(wspace=0.3, left=0.05, right=0.95, top=0.84)
fig.subplots_adjust()
plot_real_min = 30.0
plot_real_max = 100.0
plot_fake_min = 0.0
plot_fake_max = 5000.0
rad_tick_increment = 500.0
radius_ticks = []
for i in range(int(plot_fake_min),int(plot_fake_max)+int(rad_tick_increment),int(rad_tick_increment)):
plot_fake_val = ((i-plot_fake_min)/(plot_fake_max-plot_fake_min))*(plot_real_max-plot_real_min)+plot_real_min
radius_ticks.append((plot_fake_val, r"$"+str(i)+"$"))
ax2, aux_ax2 = setup_arc_radial_axes(fig, 111, angle_ticks_for_plot, radius_ticks, plot_real_min, plot_real_max)
azimuths = np.radians(np.linspace(0, 180, 91))
azimuths_adjusted = [ (x + math.pi) for x in azimuths ]
zeniths = np.arange(0, 5050, 50)
zeniths_adjusted = [((x-plot_fake_min)/(plot_fake_max-plot_fake_min))*(plot_real_max-plot_real_min)+plot_real_min for x in zeniths]
r, theta = np.meshgrid(zeniths_adjusted, azimuths_adjusted)
values = 90.0+5.0*np.random.random((len(azimuths), len(zeniths)))
aux_ax2.contourf(theta, r, values)
cbar = plt.colorbar(aux_ax2.contourf(theta, r, values), orientation='vertical')
cbar.ax.set_ylabel('Contour Value [Unit]', fontsize = 16)
plt.suptitle('Plot Title ', fontsize = 24, weight="bold")
plt.legend(loc=3,prop={'size':20})
plt.xlabel('Angle [deg]', fontsize=20, weight="bold")
plt.ylabel('Frequency [Hz]', fontsize=20, weight="bold")
# plt.show()
plt.savefig('test.png', dpi=100)
plt.close()
This script will generate a plot that looks something like:
My question is how can I plot with an alternate color bar scale? Is it possible to define a custom scale?
Something like a blue-white-red scale where deltas around a central value can easily be shown would be the best, something like:
You can create a custom scale, but matplotlib already has what you want. All you have to do is add an argument to contourf:
aux_ax2.contourf(theta, r, values, cmap = 'bwr')
If you don't like bwr, coolwarm and seismic are also blue to red. If you need to reverse the scale, just add _r to the colormap name. You can find more colormaps here: http://matplotlib.org/examples/color/colormaps_reference.html
I can't run your code, but I think you could solve your problem this way:
from matplotlib import pyplot as plt
import matplotlib as mpl
f = plt.figure(figsize=(5,10))
ax = f.add_axes([0.01, 0.01, 0.4, 0.95])
#here we create custom colors
cmap = mpl.colors.LinearSegmentedColormap.from_list(name='Some Data',colors=['b', 'w','w', 'r'])
cb = mpl.colorbar.ColorbarBase(ax, cmap=cmap, orientation='vertical')
cb.set_label('Some Data')
plt.show()
And if linear way is not what you are looking for here is some other types:
http://matplotlib.org/api/colors_api.html#module-matplotlib.colors

Categories

Resources