I'm using Panda and matplotlib to draw graphs in Python.
I would like a live updating gaph. Here is my code:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
import numpy as np
import MySQLdb
import pandas
def animate():
conn = MySQLdb.connect(host="localhost", user="root", passwd="", db="sentiment_index", use_unicode=True, charset="utf8")
c = conn.cursor()
query = """ SELECT t_date , score FROM mytable where t_date BETWEEN Date_SUB(NOW(), Interval 2 DAY) AND NOW()"""
c.execute(query)
rows=c.fetchall()
df = pandas.read_sql(query, conn, index_col=['t_date'])
df.plot()
plt.show()
animate()
I thought about using FuncAnimation but didn't get the right result. Any help please?
The documentation is a bit light on explanation of how to use
FuncAnimation. However, there are examples in the
gallery and blog
tutorials, such as Jake Vanderplas's and Sam Dolan's PDF.
This example from Jake Vanderplas's tutorial is perhaps the "Hello World" of
matplotlib animation:
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def init():
return [line]
def animate(i, ax, line):
x = np.linspace(0, 2*np.pi, N) + i/(N*2)
ax.set_xlim(x.min(), x.max())
line.set_data(x, np.sin(x))
return [line]
N = 100
fig, ax = plt.subplots()
line, = ax.plot([], [])
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
ani = animation.FuncAnimation(
fig, animate, init_func=init, interval=0, frames=int(4*np.pi*N),
repeat=True, blit=True, fargs=[ax, line])
plt.show()
Change various values or lines of code and see what happens. See what happens if
you change return [line] to something else. If you study and play with these
examples, you can learn how the pieces fit together.
Once you understand this example, you should be able to modify it to fit your
goal.
If you have trouble, post your code and describe what error message or
misbehavior you see.
Some tips:
Since animation requires calling line.set_data, I don't think you
can use Pandas' df.plot(). In fact, I'm not sure if the Pandas DataFrame is
useful here. You might be better off sucking the data into lists or NumPy arrays
and passing those to line.set as above, without getting Pandas involved.
Opening a connection to the database should be done once. animate gets
called many times. So it is better to define conn and c and query -- anything that does not change with each call to animate --
outside of animate, and pass them back as arguments to animate via the
fargs parameter.
Related
My task is to plot a numpy array in real time using matplotlib. Please note that I don't want to use animation function to do this.
import numpy as np
import time
from matplotlib.lines import Line2D
import matplotlib
class Plot:
def __init__(self,f,axis,data):
self.fig = f
self.axis = axis
self.data = data
def plotting(self,i):
xs = [self.data[i,0],self.data[i+1,0]]
ys = [self.data[i,1],self.data[i+1,1]]
line, = self.axis.plot(xs,ys,'g-')
self.fig.canvas.draw()
data = np.random.rand(10,2) #numpy array
f = plt.figure()
axis = f.add_axes([0,0,0.9,0.9])
plotData = Plot(f,axis,data)
for i in range(len(data)-1):
plotData.plotting(i)
time.sleep(1)
plt.show()
But everytime I run this code it returns me one empty figure. How do I rectify it?
import matplotlib.pyplot as plt
import numpy as np
# use ggplot style for more sophisticated visuals
plt.style.use('ggplot')
def live_plotter(x_vec,y1_data,line1,identifier='',pause_time=0.1):
if line1==[]:
# this is the call to matplotlib that allows dynamic plotting
plt.ion()
fig = plt.figure(figsize=(13,6))
ax = fig.add_subplot(111)
# create a variable for the line so we can later update it
line1, = ax.plot(x_vec,y1_data,'-o',alpha=0.8)
#update plot label/title
plt.ylabel('Y Label')
plt.title('Title: {}'.format(identifier))
plt.show()
# after the figure, axis, and line are created, we only need to update the y-data
line1.set_ydata(y1_data)
# adjust limits if new data goes beyond bounds
if np.min(y1_data)<=line1.axes.get_ylim()[0] or np.max(y1_data)>=line1.axes.get_ylim()[1]:
plt.ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])
# this pauses the data so the figure/axis can catch up - the amount of pause can be altered above
plt.pause(pause_time)
# return line so we can update it again in the next iteration
return line1
A few notes on the function above:
line1.set_ydata(y1_data) can also be switched to line1.set_data(x_vec,y1_data) to change both x and y data on the plots.
plt.pause() is necessary to allow the plotter to catch up - I've been able to use a pause time of 0.01s without any issues
The user will need to return line1 to control the line as it is updated and sent back to the function
The user can also customize the function to allow dynamic changes of title, x-label, y-label, x-limits, etc.
I recently wrote this to scrape a log and show a matplotlib.pyplot.bar plot of the most used words in it
import re
from datetime import datetime
from collections import Counter
import matplotlib.pyplot as plt
from matplotlib import animation
def read_log(path, index, separator=chr(9)):
data = []
my_file = open(path,"r+")
rows = my_file.readlines()
for row in rows:
line = re.sub(r'\r\n|\r|\n','',row, flags=re.M)
if line != '':
data.append(line.split(separator)[index])
my_file.close()
return Counter(data)
def set_plot(counter_data):
plt.title('This is a title')
plt.bar(range(len(counter_data)), list(counter_data.values()), align='center')
plt.xticks(range(len(counter_data)), list(counter_data.keys()))
plt.tight_layout()
plt.show()
counter_data = read_log(r'logfile.txt',2)
print(counter_data)
set_plot(counter_data)
I would love to animate said plot, however, I can't grasp animation.FuncAnimation()
Can you help me out?
I added these lines:
fig = plt.Figure()
animation.FuncAnimation(fig, set_plot(counter_data), frames=20)
and deleted plt.show()
So I could give FuncAnimation an empty figure (fig) and the function. But it doesn't work. EDIT: And it doesn't print an error either.
It seems your data is static (you get it from file once and it doesn't change), so I don't really understand what you are trying to animate. But, your code contains errors that need to be fixed, so for demonstration purposes I will add increment each of the heights in each step of animation.
The first mistake is in the way you pass arguments to your function. For arguments you have to use fargs parameter, otherwise in your version you are passing the result of function not the function itself.
You must have a function (animate in my version, set_plot in yours) that updates the plot for each step of your animation. (in your case you just put the same data every time)
That function needs to accept at least one parameter (val) which is used my FuncAnimation which passes values got from iterator passed to its frames parameter.
The final code looks like this
import re
from datetime import datetime
from collections import Counter
import matplotlib.pyplot as plt
from matplotlib import animation
# uncomment if using in jupyter notebook
# %matplotlib nbagg
def read_log(path, index, separator=chr(9)):
data = []
my_file = open(path,"r+")
rows = my_file.readlines()
for row in rows:
line = re.sub(r'\r\n|\r|\n','',row, flags=re.M)
if line != '':
data.append(line.split(separator)[index])
my_file.close()
return Counter(data)
fig = plt.figure()
ax = fig.add_subplot()
counter_data = read_log(r'tmp.csv',2)
plt.title('This is a title')
bar = ax.bar(range(len(counter_data)), list(counter_data.values()), align='center')
plt.xticks(range(len(counter_data)), list(counter_data.keys()))
plt.tight_layout()
plt.ylim((0, 30))
def animate(val, counter_data):
data = list(counter_data.values())
for i in range(len(data)):
bar[i].set_height(data[i]+val)
animation.FuncAnimation(fig, func=animate, frames=20, fargs=[counter_data], save_count=10)
and we get the following animation:
Edit:
For errors you can try to save your animation to gif, and the errors will show up
anim = animation.FuncAnimation(fig, func=animate, frames=20, fargs=[counter_data], save_count=10)
anim.save('anim.gif', 'imagemagick')
The main problem is that FuncAnimation expects a callable which returns artist objects. The callable will be called repeatedly with a frame argument.
In your example, set_plot() is called once. It's return value (None) is passed to FuncAnimation. Instead you should have a method, e.g. update_plot(), which loads the data from the file, updates the bar plot and returns the bar plot. This function (the function itself) should be passed to FuncAnimation
animation.FuncAnimation(fig, update_plot, frames=20)
without calling it! Note the missing parenthesis after update_plot. The animitation documentation shows examples how this can be done.
I've started to learn about matplotlib functions because i wanted to visualize data i was receiving via websocket. For that i made a dummy program that mimics the behaviour of my main program but has added the functionality of mathplotlib. what i noticed is the program takes more and more time to finish each loop and eventually 'freezes'. i managed to extend it life by changing the interval in animation.FuncAnimation from 1000 to 10000. But that just the program to plot sometimes up to 9s for 1 new peace of data. I believe the problem lays in a inappropriate way of cleaning old plots. But i don't know where exactly i did the mistake
import time
import datetime
import timeit
import queue
import os
import random
import copy
import matplotlib.pyplot as plt
import matplotlib.animation as animation
q = queue.Queue()
beta=[0,]
b=False
czas=[]
produkty=["primo"]
cena=[[] for _ in range(len(produkty))]
fig=plt.figure()
#ax1=fig.add_subplot(1,1,1)
#ax2=fig.add_subplot(1,1,1)
ax1=plt.subplot(1,1,1)
ax2=plt.subplot(1,1,1)
def animate(i):
ax1.clear()
ax2.clear()
ax1.plot(czas,cena[0])
ax2.plot(czas,beta)
while True:
time.sleep(1)
alpfa=time.time()
#input('press enter')
rand_produkt=random.choice(produkty)
rand_price=random.randint(1,10)
rand_czas=time.ctime()
alfa={'type':'ticker','price':rand_price,'product_id':rand_produkt,'time':rand_czas}
q.put(alfa)
if q.not_empty:
dane=q.get()
typ=dane.get('type',None)
if typ=='ticker':
price=dane.get('price', None)
pair=dane.get('product_id',None)
t=dane.get('time', None)
b=True
if b==True:
b=False
produkt_id=produkty.index(pair)
cena[produkt_id].append(float(price))
czas.append(t)
plt.ion()
ani=animation.FuncAnimation(fig,animate,interval=1000)#, blit=True)repeat=True)
plt.show()
plt.pause(0.001)
#fig.clf()
beta.append(time.time()-alpfa)
print(beta[-1])
The problem with your code is that you call a new animation in you while loop. Hence this will cause slow down down the line. It is better to initiate your plot ones. One trick may be to update the object data directly as such:
from matplotlib.pyplot import subplots, pause, show
from numpy import sin, pi
fig, ax = subplots()
x = [0]
y = [sin(2 * pi * x[-1])]
p1, = ax.plot(x, y)
show(block = False)
while True:
# update data
x.append(x[-1] + .1)
y.append(sin(2 * pi * x[-1]))
p1.set_data(x, y) # update data
ax.relim() # rescale axis
ax.autoscale_view()# update view
pause(1e-3)
I have a python animation script (using matplotlib's funcAnimation), which runs in Spyder but not in Jupyter. I have tried following various suggestions such as adding "%matplotlib inline" and changing the matplotlib backend to "Qt4agg", all without success. I have also tried running several example animations (from Jupyter tutorials), none of which have worked. Sometimes I get an error message and sometimes the plot appears, but does not animate. Incidentally, I have gotten pyplot.plot() to work using "%matplotlib inline".
Does anyone know of a working Jupyter notebook with a simple inline animation example that uses funcAnimation.
[Note: I am on Windows 7]
notebook backend
'Inline' means that the plots are shown as png graphics. Those png images cannot be animated. While in principle one could build an animation by successively replacing the png images, this is probably undesired.
A solution is to use the notebook backend, which is fully compatible with FuncAnimation as it renders the matplotlib figure itself:
%matplotlib notebook
jsanimation
From matplotlib 2.1 on, we can create an animation using JavaScript. This is similar to the ani.to_html5() solution, except that it does not require any video codecs.
from IPython.display import HTML
HTML(ani.to_jshtml())
Some complete example:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
t = np.linspace(0,2*np.pi)
x = np.sin(t)
fig, ax = plt.subplots()
ax.axis([0,2*np.pi,-1,1])
l, = ax.plot([],[])
def animate(i):
l.set_data(t[:i], x[:i])
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=len(t))
from IPython.display import HTML
HTML(ani.to_jshtml())
Alternatively, make the jsanimation the default for showing animations,
plt.rcParams["animation.html"] = "jshtml"
Then at the end simply state ani to obtain the animation.
Also see this answer for a complete overview.
There is a simple example within this tutorial here: http://louistiao.me/posts/notebooks/embedding-matplotlib-animations-in-jupyter-notebooks/
To summarise the tutorial above, you basically need something like this:
from matplotlib import animation
from IPython.display import HTML
# <insert animation setup code here>
anim = animation.FuncAnimation() # With arguments of course!
HTML(anim.to_html5_video())
However...
I had a lot of trouble getting that to work. Essentially, the problem was that the above uses (by default) ffmpeg and the x264 codec in the background but these were not configured correctly on my machine. The solution was to uninstall them and rebuild them from source with the correct configuration. For more details, see the question I asked about it with a working answer from Andrew Heusser: Animations in ipython (jupyter) notebook - ValueError: I/O operation on closed file
So, try the to_html5_video solution above first, and if it doesn't work then also try the uninstall / rebuild of ffmpeg and x264.
Another option:
import matplotlib.animation
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
plt.ioff()
fig, ax = plt.subplots()
x= np.linspace(0,10,100)
def animate(t):
plt.cla()
plt.plot(x-t,x)
plt.xlim(0,10)
matplotlib.animation.FuncAnimation(fig, animate, frames=10)
Here is the answer that I put together from multiple sources including the official examples. I tested with the latest versions of Jupyter and Python.
Download FFmpeg ( http://ffmpeg.zeranoe.com/builds/ )
Install FFmpeg making sure that you update the environmental variable ( http://www.wikihow.com/Install-FFmpeg-on-Windows ).
Run this script in Jupyter below. The variable imageList is the only thing that you need to modify. It is an list of images (your input).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
#=========================================
# Create Fake Images using Numpy
# You don't need this in your code as you have your own imageList.
# This is used as an example.
imageList = []
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
for i in range(60):
x += np.pi / 15.
y += np.pi / 20.
imageList.append(np.sin(x) + np.cos(y))
#=========================================
# Animate Fake Images (in Jupyter)
def getImageFromList(x):
return imageList[x]
fig = plt.figure(figsize=(10, 10))
ims = []
for i in range(len(imageList)):
im = plt.imshow(getImageFromList(i), animated=True)
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000)
plt.close()
# Show the animation
HTML(ani.to_html5_video())
#=========================================
# Save animation as video (if required)
# ani.save('dynamic_images.mp4')
If you have a list of images and want to animate through them, you can use something like this:
from keras.preprocessing.image import load_img, img_to_array
from matplotlib import animation
from IPython.display import HTML
import glob
%matplotlib inline
def plot_images(img_list):
def init():
img.set_data(img_list[0])
return (img,)
def animate(i):
img.set_data(img_list[i])
return (img,)
fig = figure()
ax = fig.gca()
img = ax.imshow(img_list[0])
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=len(img_list), interval=20, blit=True)
return anim
imgs = [img_to_array(load_img(i)) for i in glob.glob('*.jpg')]
HTML(plot_images(imgs).to_html5_video())
Thank to Kolibril. I finally can run animation on Jupyter and Google Colab.
I modify some code which will generate animation of drawing random line instead.
import matplotlib.animation
import matplotlib.pyplot as plt
from itertools import count
import random
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
fig, ax = plt.subplots()
x_value = []
y_value = []
index = count();
def animate(t):
x_value.append(next(index))
y_value.append(random.randint(0,10))
ax.cla()
ax.plot(x_value,y_value)
ax.set_xlim(0,10)
matplotlib.animation.FuncAnimation(fig, animate, frames=10, interval = 500)
enter image description here
I'm trying to plot the movement of particles with pyplot.
The problem is that I can't figure out how to create the animation.
Here is the notebook : http://nbviewer.ipython.org/gist/lhk/949c7bf7007445033fd9
Apparently the update function doesn't work properly, but the error message is too cryptic for me. What do I need to change ?
Do you have a good tutorial on animation with pyplot ?
As #s0upa1t comments you should have figure handle as the first argument to animation. The criptic error results from animation expecting a fig object, which has attribute canvas but instead gets scatter, a PathCollection object, which does not. As a minimal example of animation in the form you want, consider,
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
dt = 0.005
n=20
L = 1
particles=np.zeros(n,dtype=[("position", float , 2),
("velocity", float ,2),
("force", float ,2),
("size", float , 1)])
particles["position"]=np.random.uniform(0,L,(n,2));
particles["velocity"]=np.zeros((n,2));
particles["size"]=0.5*np.ones(n);
fig = plt.figure(figsize=(7,7))
ax = plt.axes(xlim=(0,L),ylim=(0,L))
scatter=ax.scatter(particles["position"][:,0], particles["position"][:,1])
def update(frame_number):
particles["force"]=np.random.uniform(-2,2.,(n,2));
particles["velocity"] = particles["velocity"] + particles["force"]*dt
particles["position"] = particles["position"] + particles["velocity"]*dt
particles["position"] = particles["position"]%L
scatter.set_offsets(particles["position"])
return scatter,
anim = FuncAnimation(fig, update, interval=10)
plt.show()
There are many good animation tutorials, however the answer here is particularly nice.