I would like to make a moving plot where the area under the curve gets colored while the curve is getting plotted.
I have googled a bit and found that I should somehow create a patch. However, I do not understand any of the examples they give, so let me ask it here with my specific example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import pylab as p
data = np.loadtext('datafile.dat', delimiter=',')
A = data[:,1]
B = data[:,2]
fig = plt.figure(figsize=(25,5), dpi=80)
ax = plt.axes(xlim=(0, 3.428), ylim=(-1,1))
line, = ax.plot([], [], lw=5)
def init():
line.set_data([], [])
return line,
def animate(i):
x = A[0:(i-1)*400]
y = B[0:(i-1)*400]
line.set_data(x,y)
# Here is the problem. I would now like to add the following line
# p.fill_between(x, 0, y, facecolor = 'C0', alpha = 0.2)
return line,
anim = animation.FuncAnimation(fig,animate, init_func=init, frames = 857, interval=20, blit=True)
I hope someone can give me a solution for my problem or at least point me in the correct direction.
So my question would be: how can I add the commented part without any errors?
Assuming you want blit = True, you will need to return the patch produced by fill_between as well.
p = plt.fill_between(x, y, 0)
return line, p,
Complete working example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
X = np.linspace(0,3.428, num=250)
Y = np.sin(X*3)
fig = plt.figure(figsize=(13,5), dpi=80)
ax = plt.axes(xlim=(0, 3.428), ylim=(-1,1))
line, = ax.plot([], [], lw=5)
def init():
line.set_data([], [])
return line,
def animate(i):
x = X[0:(i-1)]
y = Y[0:(i-1)]
line.set_data(x,y)
p = plt.fill_between(x, y, 0, facecolor = 'C0', alpha = 0.2)
return line, p,
anim = animation.FuncAnimation(fig,animate, init_func=init,
frames = 250, interval=20, blit=True)
plt.show()
Related
I am new to python and am carrying out some little projects along side watching tutorials to enable me to learn.
I have recently been working with some APIs to collect data - I save this data in a CSV file and then open the CSV file to show the data as a graph.
I want the graph to show the data LIVE, but in doing so I only want 10 values on the screen at once, so when the 11th value is plotted, the 1st is no longer visible unless the scrolling function is used to look back at it..
I have managed to pull together the code that plots the live data from the CSV file, as well as some code that creates the graph in the desired format - but as I am quite new to python I am unsure of how I'd make them work together.. Any advice would be greatly appreciated.
Below is the code that I have created to read and plot from a CSV file:
import random
from itertools import count
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
index = count()
def animate(i):
data = pd.read_csv('x.csv')
x = data['Time']
y = data['R1Temp']
y1 = data['R2Temp']
y2 = data['R3Temp']
plt.cla()
plt.plot(x, y, marker = 'o', label='Room 1 Temp')
plt.plot(x, y1, marker = 'o', label='Room 2 Temp')
plt.plot(x, y2, marker = 'o', label='Room 3 Temp')
plt.xlabel("Time")
plt.ylabel("Temperature °C")
plt.title("Live temperature of Rooms")
plt.legend(loc='upper left')
plt.tight_layout()
ani = FuncAnimation(plt.gcf(), animate, interval=1000)
plt.tight_layout()
plt.show()
Below is the code that shows the way in which I'd like the graph to format the data plots:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def update(frame):
global x, y
start = x[max(frame-PAN//2, 0)]
start = x[max(frame-PAN+1, 0)]
end = start + PAN
ax.set_xlim(start, end)
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, TICK))
ax.figure.canvas.draw()
line1.set_data(x[0:frame+1], y[0:frame+1])
return (line1,)
# main
NUM = 100
TICK = 1
PAN = 10
x = np.arange(start=1, stop=NUM + 1, step=1)
for i in range(NUM):
y = np.random.rand(NUM) * 100
fig, ax = plt.subplots()
ax.set_xlim(0, PAN)
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, TICK))
ax.set_ylim(0, 100)
line1, = ax.plot([], [], color="r")
ani = animation.FuncAnimation(fig, update, frames=len(x), interval=1000, repeat=False)
plt.show()
I have tried many ways to merge them together, but I just cant seem to find the correct way to go about it.
Thanks in advance!!
Showing the last N time points is quite easy. Just use DataFrame.tail() to get the last N rows of your dataframe.
Note that when doing an animation, the recommended way is to create your axes and artists outside the animation code, and only update your artists' data inside the animate code.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
l1, = ax.plot([], [], marker='o', label='Room 1 Temp')
l2, = ax.plot([], [], marker='o', label='Room 2 Temp')
l3, = ax.plot([], [], marker='o', label='Room 3 Temp')
plt.xlabel("Time")
plt.ylabel("Temperature °C")
plt.title("Live temperature of Rooms")
plt.legend(loc='upper left')
plt.tight_layout()
def animate(i, N):
data = pd.read_csv('x.csv').tail(N)
l1.set_data(data['Time'], data['R1Temp'])
l2.set_data(data['Time'], data['R2Temp'])
l3.set_data(data['Time'], data['R3Temp'])
ax.relim()
ax.autoscale_view()
return l1, l2, l3
ani = FuncAnimation(fig, animate, interval=1000, frames=None, fargs=(10,))
plt.show()
i want to plot two animated functions on the same plot to compare between two functions , lets say for example exp(-x2) and exp(x2)
i know how to animate a function here is the code i used to animate the function
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib qt
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'r', animated=True)
f = np.linspace(-3, 3, 200)
def init():
ax.set_xlim(-3, 3)
ax.set_ylim(-0.25, 2)
ln.set_data(xdata,ydata)
return ln,
def update(frame):
xdata.append(frame)
ydata.append(np.exp(-frame**2))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=f,enter code here
init_func=init, blit=True, interval = 2.5,repeat=False)
plt.show()
enter code here
and by the same method we can plot the other function but how do we show them on the same plot
As mentioned in the comment, adding another line will work. Here is a working example with exp(-x^2) and exp(x^2), I also changed the limits to see both better:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata0, ydata1 = [], [], []
ln0, = plt.plot([], [], 'r', animated=True)
ln1, = plt.plot([], [], 'b', animated=True)
f = np.linspace(-3, 3, 200)
def init():
ax.set_xlim(-3, 3)
ax.set_ylim(-0.25, 10)
ln0.set_data(xdata,ydata0)
ln1.set_data(xdata,ydata1)
return ln0, ln1
def update(frame):
xdata.append(frame)
ydata0.append(np.exp(-frame**2))
ydata1.append(np.exp(frame**2))
ln0.set_data(xdata, ydata0)
ln1.set_data(xdata, ydata1)
return ln0, ln1,
ani = FuncAnimation(fig, update, frames=f,
init_func=init, blit=True, interval=2.5, repeat=False)
plt.show()
For the gif below I changed the plt.show() line to be ani.save('animated_exp.gif', writer='imagemagick') and changed the interval to be 25.
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
I'm producing a series of scatterplots, where I keep most of the plot (besides the scatter plot) between each plot. This is done like so: Keeping map overlay between plots in matplotlib
Now I want to add annotation to the plot:
for j in range(len(n)):
plt.annotate(n[j], xy = (x[j],y[j]), color = "#ecf0f1", fontsize = 4)
However, this annotation stays on the plot between plots. How can I clear the annotation after each figure is saved?
You can remove an artist using remove().
ann = plt.annotate (...)
ann.remove()
After removal it may be necessary to redraw the canvas.
Here is a complete example, removing several annotations within an animation:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
f = lambda x: np.sin(x)
line, = ax.plot(x, f(x))
scat = plt.scatter([], [], s=20, alpha=1, color="purple", edgecolors='none')
ann_list = []
def animate(j):
for i, a in enumerate(ann_list):
a.remove()
ann_list[:] = []
n = np.random.rand(5)*6
scat.set_offsets([(r, f(r)) for r in n])
for j in range(len(n)):
ann = plt.annotate("{:.2f}".format(n[j]), xy = (n[j],f(n[j])), color = "purple", fontsize = 12)
ann_list.append(ann)
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=20, interval=360)
ani.save(__file__+".gif",writer='imagemagick', fps=3)
plt.show()
I have a weird problem with Python Matplotlib animation. I want to produce a video which includes another small video in the corner: the bigger plot should animate some data, and the small inserted plot should indicate with a moving dashed line the time position on the global data plot. It all works fine when I use only plot.show(), just the way I want it. But when I comment plot.show() and try to save it with ani.save, all it saves is a big plot, and the inserted one just disappears!
If I try to uncomment both ani.save and plot.show() at the same time, I get an error message "AttributeError: draw_artist can only be used after an initial draw which caches the render", and the animation screen just stays blank.
Could you please help me how to solve it? I need a saved file with the full movie.
Here is the code:
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
Q_data = np.arange(100.).reshape((10,10))
age = np.arange(10.)
Q_total = np.arange(10.)
Q = Q_data[1:,:]
r_ax = np.arange(10.)
fig, ax = plt.subplots()
line, = ax.semilogy(r_ax,Q[0,:])
time_template = 'time = %.1f'
time_text = ax.text(0.58, 0.9, '', size=20, transform=ax.transAxes)
plt.title('Plot 1')
ax.set_xlabel('Axis Title')
ax.set_ylabel('Axis Title')
# this is another inset axes over the main axes
a = plt.axes([0.2, 0.5, .3, .3])
line2, = plt.semilogx(age,Q_total,color='r',linewidth=1)
plt.title('Plot 2', y=1.11)
a.set_xlabel('Small axis title')
#fig.canvas.draw()
def animate(i):
line.set_ydata(Q[i,:]) # update the data
time_text.set_text(time_template % (age[i]))
a.lines.pop()
a.semilogx(age,Q_total,color='r',linewidth=1)
a.axvline(age[i], color='k', linestyle='--', linewidth=1)
return line, a, time_text
# Init only required for blitting to give a clean slate.
def init():
line.set_ydata(np.ma.array(r_ax, mask=True))
time_text.set_text('')
return line, time_text
ani = animation.FuncAnimation(fig, animate, np.arange(1, Q.shape[0], 1), init_func=init, interval=25, blit=True)
ani.save('testanimation.avi', writer="ffmpeg", fps=15)
plt.show()
I made you a small example with big axis and small axis inside it.
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['animation.ffmpeg_path'] = r'D:\ffmpeg-20161029-1e660fe-win64-static\bin\ffmpeg.exe'
import matplotlib.animation as animation
r_ax = np.arange(1, 10., 0.1)
fig, ax = plt.subplots()
line, = ax.plot(r_ax, np.log(r_ax))
a = plt.axes([0.2, 0.5, .3, .3])
line2, = a.plot([], [],color='r',linewidth=1)
a.set_xlim([1, 10])
a.set_ylim([0, 3])
def animate(i):
line.set_data(r_ax[:i], np.log(r_ax[:i])) # update the data
line2.set_data(r_ax[:i], 3-np.log(r_ax[:i]))
return line, line2
ani = animation.FuncAnimation(fig, animate, frames=100, interval=35, blit=True)
ani.save('testanimation.avi', writer="ffmpeg", fps=15)
# plt.show()