Related
I'm animating a scatter plot with a code below. It reads data from a .csv file "sample.csv" and animates it by using np.roll in update function.
import numpy as np
import matplotlib.pyplot as plt
from numpy import genfromtxt
from matplotlib.animation import FuncAnimation
my_data = genfromtxt("sample.csv", delimiter=",", skip_header=1) # reading data
fig, ax = plt.subplots()
xdata, ydata = [], []
line, = ax.plot([], [], ".", markersize=14)
plt.grid()
def init():
ax.set_xlim(0, 50)
ax.set_ylim(0, 60)
return line,
def update(frame):
my_data[:,0] = np.roll(my_data[:,0],1) # moving graph
gap_loc = [19,20,21] # location of a gap
my_data[gap_loc, 1] = np.nan # creating a gap in graph
xdata.append(my_data[:,0])
ydata.append(my_data[:,1])
line.set_data(xdata, ydata)
return line,
ani = FuncAnimation(fig, update, frames=np.arange(0,50,1), init_func=init, blit=True)
plt.show()
The result looks like that:
As you see there is a gap which moves together with the remaining points. However, what I want to achieve is that the gap was stationary at the locations on the horizontal axis: 19,20,21.
How can I achieve this effect?
Below please find a dataset, I'm using for this animation.
Day,Var 1,Var 2
1,2,12
2,4,19
3,6,20
4,8,25
5,10,25
6,12,33
7,14,40
8,16,47
9,18,49
10,20,50
11,22,52
12,24,55
13,26,65
14,28,82
15,30,100
16,32,100
17,34,110
18,36,117
19,38,140
20,40,145
21,42,164
22,44,170
23,46,198
24,48,200
25,50,210
26,48,210
27,46,211
28,44,216
29,42,267
30,40,317
31,38,325
32,36,335
33,34,337
34,32,347
35,30,356
36,28,402
37,26,410
38,24,448
39,22,449
40,20,457
41,18,463
42,16,494
43,14,500
44,12,501
45,10,502
46,8,514
47,6,551
48,4,551
49,2,558
50,0,628
Define the gap when you load the data, and do so in the x column rather than the y:
# imports
my_data = genfromtxt("sample.csv", delimiter=",", skip_header=1) # reading data
gap_loc = [19,20,21] # location of a gap
my_data[gap_loc, 0] = np.nan # creating a gap in graph
# plotting code
So now when you roll the x column, there will always be np.nan at the x values [19, 20, 21], regardless of what the y coordinate is. You can use print(my_data) within the update function to make clear what was going on each iteration.
Here is the result:
Also, I think you are over-plotting because you continually expand xdata and ydata using append. I ended up just removing the xdata and ydata and doing:
def update(frame):
my_data[:,0] = np.roll(my_data[:,0],1) # moving graph
line.set_data(my_data[:,0], my_data[:,1])
return line,
I have a dictionary which is updated by a for loop , I am trying to plot the key value pairs in a plot which moves or slides as the number of key value pairs keep updating and the plot just shows the 50 current values from the dictionary.
so far I have made :
for k,v in plot.items():
plt.barh(k,v , color='blue')
plt.pause(0.3)
plt.show()
The problem is that the values keep appending and the plots keeps growing. Is there any way to show just last 50 k,v pairs from a dictionary and and keep on updating the plot. I have also tried a function :
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, = plt.plot(x_vec,y1_data,'-o', alpha=0.8)
#update plot label/title
plt.ylabel('Cross-correlation')
plt.title('Title: {}'.format(identifier))
line1.set_data(x_vec, y1_data)
plt.pause(pause_time)
return line1
But this also doesn't update the plot and just appends to the plot like the code above.
I have also tried the animation function :
fig = plt.figure()
def animate():
for k, v in plot:
print(k, v)
plt.barh(k, v, color='blue')
plt.pause(0.3)
ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
Reproducible example:
import matplotlib.pyplot as plt
import matplotlib.animation
plot_base={'00002200': 986.11152642551519,
'00002201': 989.90915858383369,
'00002202': 992.87095144990781,
'00002203': 994.89971071876084,
'00002204': 992.92424216660561,
'00002205': 993.19845750930426,
'00002206': 988.88001766153957,
'00002207': 988.95062195981848,
'00002208': 993.17138084871465,
'00002209': 993.9863425202193,
'00002210': 993.43551440410283,
'00002211': 993.04540624076844,
'00002212': 991.40048097057559,
'00002213': 993.16124311517319,
'00002214': 994.06785011666204,
'00002215': 985.24294182260996,
'00002216': 990.5369409623512,
'00002217': 991.83512034302737,
'00002218': 993.43756392913269,
'00002219': 989.77919409784511,
'00002220': 988.09683378239572,
'00002221': 992.19961090836773,
'00002222': 992.69477342507912,
'00002223': 992.37890673842412,
'00002224': 991.55520651752556,
'00002225': 992.15414070360941,
'00002226': 991.49292821478309,
'00002227': 994.75013161999084,
'00002228': 991.54858727670728,
'00002229': 993.22846583401292,
'00002230': 993.88719133150084,
'00002231': 992.89934842358855,
'00002232': 991.10582582918869,
'00002233': 993.24750746833467,
'00002234': 992.6478137931806,
'00002235': 991.2614284514957,
'00002236': 994.38800336488725}
plot={}
for k,v in plot_base.items():
plot.update({k: v})
for k,v in plot.items():
bar, = plt.bar(k, v, color='blue')
plt.pause(0.3)
plt.plot()
'''
def animate(i):
bars = []
for k, v in plot.items():
bar, = plt.bar(k, v, color='blue')
bars.append(bar)
return bars
fig=plt.figure(figsize=(12 ,7))
ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000, blit=True)
plt.show()
'''
When we run this code the values from the dictionary keep appending to the x-axis , that's why I want to make it scroll able(auto-scroll) , The triple quoted code shows the animation part, this make the whole graph appear at once.
I don't think I understand the data structure used here, so this focuses on the animating part. Suppose you have a function store.get_last_20_values from which to obtain the data you want to plot. Then the animation would look like:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
def animate(i):
cat, val = store.get_last_20_values()
ax.clear()
ax.barh(cat, val)
ani = FuncAnimation(fig, animate, interval=1000)
plt.show()
For completeness here is how that function could look like, to make the above runnable.
class Store():
x = []
y = []
def update(self):
n = np.random.randint(0,5)
x = np.random.randint(100000,999999, size=n).astype(str)
y = np.random.randint(50,999, size=n)
self.x.extend(x)
self.y.extend(y)
self.x = self.x[-20:]
self.y = self.y[-20:]
def get_last_20_values(self):
# Some function to return 20 values. Need to adapt to custom needs.
self.update()
return self.x, self.y
store = Store()
I've got an animation with lines and now I want to label the points.
I tried plt.annotate() and I tried plt.text() but the labes don't move.
This is my example code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def update_line(num, data, line):
newData = np.array([[1+num,2+num/2,3,4-num/4,5+num],[7,4,9+num/3,2,3]])
line.set_data(newData)
plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line,
fig1 = plt.figure()
data = np.array([[1,2,3,4,5],[7,4,9,2,3]])
l, = plt.plot([], [], 'r-')
plt.xlim(0, 20)
plt.ylim(0, 20)
plt.annotate('A0', xy=(data[0][0], data[1][0]))
# plt.text( data[0][0], data[1][0], 'A0')
line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l),
interval=200, blit=True)
plt.show()
Can you help me please?
My next step is:
I have vectors with origin in these Points. These vectors change their length and their direction in each animation step.
How can I animate these?
Without animation this works:
soa =np.array( [ [data[0][0],data[1][0],F_A0[i][0][0],F_A0[i][1][0]],
[data[0][1],data[1][1],F_B0[i][0][0],F_B0[i][1][0]],
[data[0][2],data[1][2],F_D[i][0][0],F_D[i][1][0]] ])
X,Y,U,V = zip(*soa)
ax = plt.gca()
ax.quiver(X,Y,U,V,angles='xy',scale_units='xy',scale=1)
First thanks a lot for your fast and very helpful answer!
My Vector animation problem I have solved with this:
annotation = ax.annotate("C0", xy=(data[0][2], data[1][2]), xycoords='data',
xytext=(data[0][2]+1, data[1][2]+1), textcoords='data',
arrowprops=dict(arrowstyle="->"))
and in the 'update-function' I write:
annotation.xytext = (newData[0][2], newData[1][2])
annotation.xy = (data[0][2]+num, data[1][2]+num)
to change the start and end position of the vectors (arrows).
But what is, wehn I have 100 vectors or more? It is not practicable to write:
annotation1 = ...
annotation2 = ...
.
:
annotation100 = ...
I tried with a list:
...
annotation = [annotation1, annotation2, ... , annotation100]
...
def update(num):
...
return line, annotation
and got this error:
AttributeError: 'list' object has no attribute 'axes'
What can I do? Have you any idea?
I'm coming here from this question, where an annotation should be updated that uses both xy and xytext. It appears that, in order to update the annotation correctly, one needs to set the attribute .xy of the annotation to set the position of the annotated point and to use the .set_position() method of the annotation to set the position of the annotation. Setting the .xytext attribute has no effect -- somewhat confusing in my opinion. Below a complete example:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig, ax = plt.subplots()
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
L = 50
theta = np.linspace(0,2*np.pi,L)
r = np.ones_like(theta)
x = r*np.cos(theta)
y = r*np.sin(theta)
line, = ax.plot(1,0, 'ro')
annotation = ax.annotate(
'annotation', xy=(1,0), xytext=(-1,0),
arrowprops = {'arrowstyle': "->"}
)
def update(i):
new_x = x[i%L]
new_y = y[i%L]
line.set_data(new_x,new_y)
##annotation.xytext = (-new_x,-new_y) <-- does not work
annotation.set_position((-new_x,-new_y))
annotation.xy = (new_x,new_y)
return line, annotation
ani = animation.FuncAnimation(
fig, update, interval = 500, blit = False
)
plt.show()
The result looks something like this:
In case that versions matter, this code has been tested on Python 2.7 and 3.6 with matplotlib version 2.1.1, and in both cases setting .xytext had no effect, while .set_position() and .xy worked as expected.
You have the return all objects that changed from your update function. So since your annotation changed it's position you should return it also:
line.set_data(newData)
annotation = plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line, annotation
You can read more about matplotlib animations in this tutorial
You should also specify the init function so that the FuncAnimation knows which elements to remove from the plot when redrawing on the first update. So the full example would be:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Create initial data
data = np.array([[1,2,3,4,5], [7,4,9,2,3]])
# Create figure and axes
fig = plt.figure()
ax = plt.axes(xlim=(0, 20), ylim=(0, 20))
# Create initial objects
line, = ax.plot([], [], 'r-')
annotation = ax.annotate('A0', xy=(data[0][0], data[1][0]))
annotation.set_animated(True)
# Create the init function that returns the objects
# that will change during the animation process
def init():
return line, annotation
# Create the update function that returns all the
# objects that have changed
def update(num):
newData = np.array([[1 + num, 2 + num / 2, 3, 4 - num / 4, 5 + num],
[7, 4, 9 + num / 3, 2, 3]])
line.set_data(newData)
# This is not working i 1.2.1
# annotation.set_position((newData[0][0], newData[1][0]))
annotation.xytext = (newData[0][0], newData[1][0])
return line, annotation
anim = animation.FuncAnimation(fig, update, frames=25, init_func=init,
interval=200, blit=True)
plt.show()
I think I figured out how to animate multiple annotations through a list. First you just create your annotations list:
for i in range(0,len(someMatrix)):
annotations.append(ax.annotate(str(i), xy=(someMatrix.item(0,i), someMatrix.item(1,i))))
Then in your "animate" function you do as you have already written:
for num, annot in enumerate(annotations):
annot.set_position((someMatrix.item((time,num)), someMatrix.item((time,num))))
(You can write it as a traditional for loop as well if you don't like the enumerate way). Don't forget to return the whole annotations list in your return statement.
Then the important thing is to set "blit=False" in your FuncAnimation:
animation.FuncAnimation(fig, animate, frames="yourframecount",
interval="yourpreferredinterval", blit=False, init_func=init)
It is good to point out that blit=False might slow things down. But its unfortunately the only way I could get animation of annotations in lists to work...
I am plotting a confusion matrix with matplotlib with the following code:
from numpy import *
import matplotlib.pyplot as plt
from pylab import *
conf_arr = [[33,2,0,0,0,0,0,0,0,1,3], [3,31,0,0,0,0,0,0,0,0,0], [0,4,41,0,0,0,0,0,0,0,1], [0,1,0,30,0,6,0,0,0,0,1], [0,0,0,0,38,10,0,0,0,0,0], [0,0,0,3,1,39,0,0,0,0,4], [0,2,2,0,4,1,31,0,0,0,2], [0,1,0,0,0,0,0,36,0,2,0], [0,0,0,0,0,0,1,5,37,5,1], [3,0,0,0,0,0,0,0,0,39,0], [0,0,0,0,0,0,0,0,0,0,38] ]
norm_conf = []
for i in conf_arr:
a = 0
tmp_arr = []
a = sum(i,0)
for j in i:
tmp_arr.append(float(j)/float(a))
norm_conf.append(tmp_arr)
plt.clf()
fig = plt.figure()
ax = fig.add_subplot(111)
res = ax.imshow(array(norm_conf), cmap=cm.jet, interpolation='nearest')
cb = fig.colorbar(res)
savefig("confmat.png", format="png")
But I want to the confusion matrix to show the numbers on it like this graphic (the right one). How can I plot the conf_arr on the graphic?
You can use text to put arbitrary text in your plot. For example, inserting the following lines into your code will write the numbers (note the first and last lines are from your code to show you where to insert my lines):
res = ax.imshow(array(norm_conf), cmap=cm.jet, interpolation='nearest')
for i, cas in enumerate(conf_arr):
for j, c in enumerate(cas):
if c>0:
plt.text(j-.2, i+.2, c, fontsize=14)
cb = fig.colorbar(res)
The only way I could really see of doing it was to use annotations. Try these lines:
for i,j in ((x,y) for x in xrange(len(conf_arr))
for y in xrange(len(conf_arr[0]))):
ax.annotate(str(conf_arr[i][j]),xy=(i,j))
before saving the figure. It adds the numbers, but I'll let you figure out how to get the sizes of the numbers how you want them.
I have a simple plot with several sets of points and lines connecting each set. I want the points to be plotted on top of the lines (so that the line doesn't show inside the point). Regardless of order of the plot and scatter calls, this plot comes out the same, and not as I'd like. Is there a simple way to do it?
import math
import matplotlib.pyplot as plt
def poisson(m):
def f(k):
e = math.e**(-m)
f = math.factorial(k)
g = m**k
return g*e/f
return f
R = range(20)
L = list()
means = (1,4,10)
for m in means:
f = poisson(m)
L.append([f(k) for k in R])
colors = ['r','b','purple']
for c,P in zip(colors,L):
plt.plot(R,P,color='0.2',lw=1.5)
plt.scatter(R,P,s=150,color=c)
ax = plt.axes()
ax.set_xlim(-0.5,20)
ax.set_ylim(-0.01,0.4)
plt.savefig('example.png')
You need to set the Z-order.
plt.plot(R,P,color='0.2',lw=1.5, zorder=1)
plt.scatter(R,P,s=150,color=c, zorder=2)
Check out this example.
http://matplotlib.sourceforge.net/examples/pylab_examples/zorder_demo.html