Matplotlib Animation: how to dynamically extend x limits? - python

I have a simple animation plot like so:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 100), ylim=(0, 100))
line, = ax.plot([], [], lw=2)
x = []
y = []
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
x.append(i + 1)
y.append(10)
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
Now, this works okay, but I want it to expand like one of the subplots in here http://www.roboticslab.ca/matplotlib-animation/ where the x-axis dynamically extends to accommodate the incoming data points.
How do I accomplish this?

I came across this problem (but for set_ylim) and I had some trial and error with the comment of #ImportanceOfBeingErnest and this is what I got, adapted to #nz_21 question.
def animate(i):
x.append(i + 1)
y.append(10)
ax.set_xlim(min(x), max(x)) #added ax attribute here
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=500, interval=20)
Actually in the web quoted by #nz_21 there is a similar solution.

There is a workaround for this even when blit=True if you send a resize event to the canvas, forcing MPL to flush the canvas. To be clear, the fact that the code in #fffff answer doesn't work with blit is a bug in the MPL codebase, but if you add fig.canvas.resize_event() after setting the new x-axis, it will work in MPL 3.1.3. Of course, you should only do this sporadically to actually get any benefit from blitting --- if you're doing it every frame, you're just performing the non-blit procedure anyway, but with extra steps.

Related

Create animation for multilayer array in python [duplicate]

I have a 3D matrix of shape (100,50,50), e.g.
import numpy as np
data = np.random.random(100,50,50)
And I want to create an animation that shows each of the 2D slices of size (50,50) as a heatmap or imshow
e.g.:
import matplotlib.pyplot as plt
plt.imshow(data[0,:,:])
plt.show()
would display the 1st 'frame' of this animation. I'd like to also have this display in a Jupyter Notebook. I am currently following this tutorial for inline notebook animations being displayed as a html video, but I can't figure out how to replace the 1D line data with a slice of my 2D array.
I know I need to create a plot element, an initialisation function and an animate function. Following that example, I've tried:
fig, ax = plt.subplots()
ax.set_xlim((0, 50))
ax.set_ylim((0, 50))
im, = ax.imshow([])
def init():
im.set_data([])
return (im,)
# animation function. This is called sequentially
def animate(i):
data_slice = data[i,:,:]
im.set_data(i)
return (im,)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
HTML(anim.to_html5_video())
But I'm getting various errors whatever I try, mostly associated with the line im, = ax.imshow([])
Any help appreciated!
Several issues:
You have a lot of missing imports.
numpy.random.random takes a tuple as input, not 3 arguments
imshow needs an array as input, not an empty list.
imshow returns an AxesImage, which cannot be unpacked. Hence no , on the assignment.
.set_data() expects the data, not the framenumber as input.
Complete code:
from IPython.display import HTML
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
data = np.random.rand(100,50,50)
fig, ax = plt.subplots()
ax.set_xlim((0, 50))
ax.set_ylim((0, 50))
im = ax.imshow(data[0,:,:])
def init():
im.set_data(data[0,:,:])
return (im,)
# animation function. This is called sequentially
def animate(i):
data_slice = data[i,:,:]
im.set_data(data_slice)
return (im,)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
HTML(anim.to_html5_video())

Trying to make a gif in Python

I'm approximating functions with Fourier Series and I would like to make, hopefully in a simple way, an animated gif of the different approximations. With a for and plt.savefig commands I generate the different frames for the gif but I haven't been able to animate them. The idea is to obtain something like this
This can be done using matplotlib.animation.
EDIT
I'm going to show you how to plot even powers of x like x^2, x^4 and so on. take a look at following example :
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# Setting up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(-10, 10), ylim=(-10, 1000))
line, = ax.plot([], [], lw=2)
# initialization method: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation method. This method will be called sequentially
pow_ = 2
def animate(i):
global pow_
x = np.linspace(-10, 10, 1000)
y = x**pow_
pow_+=2
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=10, interval=200, blit=True)
# if you want to save this animation as an mp4 file, uncomment the line bellow
# anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()

Time dependent plot matplotlib

I would like to plot point-by-point a sine wave in Python via Matplotlib, for which, each point, is added every x milliseconds, in order to obtain a smooth animation of the drawing.
This is my attempt:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from math import sin
fig, ax = plt.subplots()
x = [0]
line, = ax.plot(x, np.asarray(0))
def animate(i):
x.append(x[-1]+0.04)
line.set_xdata(np.asarray(x)*2*np.pi/5)
line.set_ydata(np.sin(np.asarray(x)*2*np.pi/5))
plt.draw()
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, 10, init_func=init, interval=40, blit=True)
plt.show()
Which raises:
RuntimeError: The animation function must return a sequence of Artist objects.
What did I mistaken? What is, in your opinion, the most efficient way to obtain this effect?
PS The time axis should stay fixed and not move, so it should be wider than the plot
Firstly you are getting the error because your animate(i) is not returning anything. You need to return line,. Secondly you are not using the iin animate(i) aswell.
Here is a simple sine curve animation from https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# First set up the figure, the axis, and the plot element we want to
animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have
changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
There is additional inspiration in the link which might help you further.
Animate should return a sequence of artist objects:
You should add:
return line, to the end of the animate function
def animate(i):
x.append(x[-1]+0.04)
line.set_xdata(np.asarray(x)*2*np.pi/5)
line.set_ydata(np.sin(np.asarray(x)*2*np.pi/5))
return line,
Source:
Another answer
Simple Example

Matplotlib Animation showing up empty

There was a similar question here but I'm not having the same issue. Below is a snapshot of my dataset:
Essentially, I'd like to animate the drop off coordinates over time. As you can see the dates are sorted by dropoff_datetime. Here is my code (very similar to the question above).
fig = plt.figure(figsize=(10,10))
ax = plt.axes(xlim=xlim, ylim=ylim)
points, = ax.plot([], [],'.',alpha = 0.4, markersize = 0.05)
def init():
points.set_data([], [])
return points,
# animation function. This is called sequentially
def animate(i):
x = test["dropoff_longitude"]
y = test["dropoff_latitude"]
points.set_data(x, y)
return points,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
plt.show()
Similar to the issue in the problem linked above, my plot is just showing up empty. I believe i'm coding it properly and unlike the link above, I do see the that coordinates are changing over time. I'm not sure why the plot is empty.
Per default one pixel is either 1 point or 0.72 points (depending on whether you run the code in a jupyter notebook or as a standalone plot). If you create a plot with a markersize of 0.05, each marker will thus have a size of 0.05 pixels or 0.07 pixels, respectively. Since it is already quite hard to see 1 pixel on a screen, especially if the alpha is set to 0.4, observing one twentieth of a pixel is simply not possible.
Solution: Set markersize = 5 or higher.
A full working example:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import pandas as pd
test = pd.DataFrame({"dropoff_longitude": [-72,-73,-74,-74],
"dropoff_latitude": [40,41,42,43]})
xlim=(-71,-75)
ylim=(39,44)
fig = plt.figure(figsize=(10,10))
ax = plt.axes(xlim=xlim, ylim=ylim)
points, = ax.plot([], [],'.',alpha = 1, markersize =5)
def init():
points.set_data([], [])
return points,
# animation function. This is called sequentially
def animate(i):
x = test["dropoff_longitude"]
y = test["dropoff_latitude"]
points.set_data(x, y)
return points,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
plt.show()

Matplotlib animation not working

I cannot run matplotlib animations using anaconda. I’m trying to run this on Spyder:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
But it gives me a blank plot. I'm using this directly off of the documentation website as an example and it is not working.
I think you're using Pycharm which have some bug.
you need to toggle off scientific mode off in preference->tools->python scientific-> show plots in tool window.
and add
%matplotlib qt5
before drawing

Categories