I have been trying to create an animation in matplotlib from the graph tripcolor. Let's say I have
field = ax.tripcolor(tri, C)
How do I change the value of C after each iteration of the animation?
Many thanks,
field is guaranteed to be an instance of the matplotlib.collections.Collection base class, which helpfully defines a set_array() method for just such occasions.
In each iteration of your animation, simply pass the new value of C to the field.set_array() method. Assuming you use the FuncAnimation class for animations, as you probably want to, this reduces to:
fig = plt.figure()
ax = plt.subplot(111)
field = ax.tripcolor(tri, C)
def update_tripcolor(frame_number):
# Do something here to update "C"!
C **= frame_number # ...just not this.
# Update the face colors of the previously plotted triangle mesh.
field.set_array(C)
# To triangular infinity and beyond! (Wherever that is. It's probably scary.)
FuncAnimation(fig, update_tripcolor, frames=10)
Updating tri, on the other hand, is considerably more difficult. While this question doesn't attempt to do so, the perspicacious reader may be curious to learn that you basically have to remove, recreate, and re-add the entire triangle mesh (i.e., field) onto this figure's axes. This is both inefficient and painful, of course. (Welcome to Matplotlib. Population: you.)
May the field.set_array() be with you.
Related
I am working on Radar sensor. I plot the data every time I get it from the sensor. I want to annotate each point with its characteristics e.g x.y.z. How can I do it?
I know about ax.annotate but I can do 1 point at one time. If I loop this command it slows my program. This is what I'm trying to accomplish:
I got the answer of my own question.
We need to use .set_text() command in a loop which updates the text(characteristics of points). Here is the abstract code:
fig1=plt.figure(figsize=(15,32))
ax1=fig1.add_subplot(111, aspect='equal')
ax1.grid(True)
Ln, = ax1.plot(plot_x,plot_y,'ro') #plot_X,plot_y are lists of points
plt.ion() # to make figure interactive
plt.show()
......
......
......
Ln.set_data(plot_x,plot_y) #to updates points in the lists
for i in range(len(plot_ann)): #each time plot_ann number will change
if i >= len(ann_objs): #ann_objs is annotation object list
ann_objs.append(ax1.annotate("", xy=(0,0)))
ann_objs[i].set_text(plot_ann[i])
ann_objs[i].xy = (plot_x[i], plot_y[i])
ann_objs[i].xyann = (plot_x[i]+0.2, plot_y[i]+0.2)
I have written a complicated code. The code produces a set of numbers which I want to plot them. The problem is that I cannot put those numbers in a list since there are 2 700 000 000 of them.
So I need to plot one point then produce second point (the first point is replaced by second point so the first one is erased because I cannot store them). These numbers are generated in different sections of the code so I need to hold (MATLAB code) the figure.
For making it more conceivable to you, I write a simple code here and I want you to show me how to plot it.
import matplotlib.pyplot as plt
i=0
j=10
while i<2700000000:
plt.stem(i, j, '-')
i = i + 1
j = j + 2
plt.show()
Suppose I have billions of i and j!
Hmm I'm not sure if I understood you correctly but this:
import matplotlib.pyplot as plt
i=0
j=10
fig=plt.figure()
ax=fig.gca()
while i<10000: # Fewer points for speed.
ax.stem([i], [j]) # Need to provide iterable arguments to ax.stem
i = i + 1
j = j + 2
fig.show()
generates the following figure:
Isn't this what you're trying to achieve? After all the input numbers aren't stored anywhere, just added to the figure as soon as they are generated. You don't really need Matlab's hold equivalent, the figure won't be shown until you call fig.show() or plt.show() to show the current figure.
Or are you trying to overcome the problem that you can' hold the matplotlib.figure in your RAM? In which case my answer doesn't answer your question. Then you either have to save partial figures (only parts of the data) as pictures and combine them, as suggested in the comments, or think about an alternative way to show the data, as suggested in the other answer.
I'm not sure if binning is the correct term, but I want to implement the following for a project I am working on:
I have an array or maybe a dict describing boundaries and/or regions, for example:
boundaries = OrderedDict([(10,'red'),(20,'blue'),(55,'purple')])
The areas are indexed from 0 to 100 (for example). I want to classify each area into a color (that is less than the key in the dict) and then plot it. For example, if it is less than 10, it is red.
So far, I have:
boundaries = OrderedDict([(10,'red'),(20,'blue'),(55,'purple')])
areas = range(0,101)
binned = []
for area in areas:
for border in boundaries.keys():
if area < border:
binned.append(boundaries[border])
break
Also, I need to figure out a way to define the colors and find a package to plot it. So if you have any ideas how can I plot a 2-D color plot (the actual project will be in 2-D). Maybe matplotlib or PIL? I have used matplotlib before but never for this type of data.
Also, is there a scipy/numpy function that already does what I'm trying to do? It would be nice if the code is short and fast. This is not for an assignment of any sort (it's for a little experiment / data project of mine), so I don't want to reinvent the wheel here.
import matplotlib.pyplot as plt
boundaries = collections.OrderedDict([(10,'red'),(20,'blue'),(55,'purple')])
areas = range(0,101)
n, bins, patches = plt.hist(areas, [0]+list(boundaries), histtype='bar', rwidth=1.0)
for (patch,color) in zip(patches,boundaries.values()):
patch.set_color(color)
plt.show()
Let's say I have two histograms and I set the opacity using the parameter of hist: 'alpha=0.5'
I have plotted two histograms yet I get three colors! I understand this makes sense from an opacity point of view.
But! It makes is very confusing to show someone a graph of two things with three colors. Can I just somehow set the smallest bar for each bin to be in front with no opacity?
Example graph
The usual way this issue is handled is to have the plots with some small separation. This is done by default when plt.hist is given multiple sets of data:
import pylab as plt
x = 200 + 25*plt.randn(1000)
y = 150 + 25*plt.randn(1000)
n, bins, patches = plt.hist([x, y])
You instead which to stack them (this could be done above using the argument histtype='barstacked') but notice that the ordering is incorrect.
This can be fixed by individually checking each pair of points to see which is larger and then using zorder to set which one comes first. For simplicity I am using the output of the code above (e.g n is two stacked arrays of the number of points in each bin for x and y):
n_x = n[0]
n_y = n[1]
for i in range(len(n[0])):
if n_x[i] > n_y[i]:
zorder=1
else:
zorder=0
plt.bar(bins[:-1][i], n_x[i], width=10)
plt.bar(bins[:-1][i], n_y[i], width=10, color="g", zorder=zorder)
Here is the resulting image:
By changing the ordering like this the image looks very weird indeed, this is probably why it is not implemented and needs a hack to do it. I would stick with the small separation method, anyone used to these plots assumes they take the same x-value.
I would like to create a stack of line plots using a LineCollection. The following code draws two identical sine curves offset from one another by (0, 0.2):
import matplotlib.pyplot as plt
import matplotlib.collections
import numpy as np
x=np.arange(1000)
y=np.sin(x/50.)
l=zip(x,y)
f=plt.figure()
a=f.add_subplot(111)
lines=matplotlib.collections.LineCollection((l,l), offsets=(0,0.2))
a.add_collection(lines)
a.autoscale_view(True, True, True)
plt.show()
So far so good. The problem is that I'd like to be able to adjust that offset after creation. Using set_offsets doesn't seem to behave as I expect it to. The following, for instance, has no effect on the graph
a.collections[0].set_offsets((0, 0.5))
BTW, the other set commands (e.g. set_color) work as I expect. How do I change the spacing between curves after they have been created?
I think you found a bug in matplotlib, but I have a couple work arounds. It looks like lines._paths gets generated in LineCollection().__init__ using the offsets you provide. lines._paths is not property updated when you call lines.set_offsets(). In your simple example, you can re-generate the paths since you still have the originals laying around.
lines.set_offsets( (0., 0.2))
lines.set_segments( (l,l) )
You can also manually apply your offsets. Remember that you're modifying the offset points. So to get an offset of 0.2, you add 0.1 to your pre-existing offset of 0.1.
lines._paths[1].vertices[:,1] += 1
Thanks #matt for your suggestion. Based on that I've hacked together the following which shifts the curves according to new offset values, but takes into account the old offset values. This means I don't have to retain the original curve data. Something similar might be done to correct the set_offsets method of LineCollection but I don't understand the details of the class well enough to risk it.
def set_offsets(newoffsets, ax=None, c_num=0):
'''
Modifies the offsets between curves of a LineCollection
'''
if ax is None:
ax=plt.gca()
lcoll=ax.collections[c_num]
oldoffsets=lcoll.get_offsets()
if len(newoffsets)==1:
newoffsets=[i*np.array(newoffsets[0]) for\
(i,j) in enumerate(lcoll.get_paths())]
if len(oldoffsets)==1:
oldoffsets=[i*oldoffsets[0] for (i,j) in enumerate(newoffsets)]
verts=[path.vertices for path in lcoll.get_paths()]
for (oset, nset, vert) in zip(oldoffsets, newoffsets, verts):
vert[:,0]+=(-oset[0]+nset[0])
vert[:,1]+=(-oset[1]+nset[1])
lcoll.set_offsets(newoffsets)
lcoll.set_paths(verts)