Matplotlib animate fill_between shape

2020-07-02 10:36发布

I am trying to animate a fill_between shape inside matplotlib and I don't know how to update the data of the PolyCollection. Take this simple example: I have two lines and I am always filling between them. Of course, the lines change and are animated.

Here is a dummy example:

import matplotlib.pyplot as plt

# Init plot:
f_dummy = plt.figure(num=None, figsize=(6, 6));
axes_dummy = f_dummy.add_subplot(111);

# Plotting:
line1, = axes_dummy.plot(X, line1_data, color = 'k', linestyle = '--', linewidth=2.0, animated=True);
line2, = axes_dummy.plot(X, line2_data, color = 'Grey', linestyle = '--', linewidth=2.0, animated=True);
fill_lines = axes_dummy.fill_between(X, line1_data, line2_data, color = '0.2', alpha = 0.5, animated=True);

f_dummy.show();
f_dummy.canvas.draw();
dummy_background = f_dummy.canvas.copy_from_bbox(axes_dummy.bbox);

# [...]    

# Update plot data:
def update_data():
   line1_data = # Do something with data
   line2_data = # Do something with data
   f_dummy.canvas.restore_region( dummy_background );
   line1.set_ydata(line1_data);
   line2.set_ydata(line2_data);

   # Update fill data too

   axes_dummy.draw_artist(line1);
   axes_dummy.draw_artist(line2);

   # Draw fill too

   f_dummy.canvas.blit( axes_dummy.bbox );

The question is how to update the fill_between Poly data based on line1_data and line2_data each time update_data() is called and draw them before blit ("# Update fill data too" & "# Draw fill too"). I tried fill_lines.set_verts() without success and could not find an example...

Thanks!

5条回答
趁早两清
2楼-- · 2020-07-02 10:50

Ok, as someone pointed out, we are dealing with a collection here, so we will have to delete and redraw. So somewhere in the update_data function, delete all collections associated with it:

axes_dummy.collections.clear()

and draw the new "fill_between" PolyCollection:

axes_dummy.fill_between(x, y-sigma, y+sigma, facecolor='yellow', alpha=0.5)

A similar trick is required to overlay an unfilled contour plot on top of a filled one, since an unfilled contour plot is a Collection as well (of lines I suppose?).

查看更多
倾城 Initia
3楼-- · 2020-07-02 10:53

another idiom which will work is too keep a list of your plotted objects; this method seems to work with any type of plotted object.

# plot interactive mode on
plt.ion()

# create a dict to store "fills" 
# perhaps some other subclass of plots 
# "yellow lines" etc. 
plots = {"fills":[]}

# begin the animation
while 1: 

    # cycle through previously plotted objects
    # attempt to kill them; else remember they exist
    fills = []
    for fill in plots["fills"]:
        try:
            # remove and destroy reference
            fill.remove()
            del fill
        except:
            # and if not try again next time
            fills.append(fill)
            pass
    plots["fills"] = fills   

    # transformation of data for next frame   
    x, y1, y2 = your_function(x, y1, y2)

    # fill between plot is appended to stored fills list
    plots["fills"].append(
        plt.fill_between(
            x,
            y1,
            y2,
            color="red",
        )
    )

    # frame rate
    plt.pause(1)
查看更多
男人必须洒脱
4楼-- · 2020-07-02 11:10

initialize pyplot interactive mode

import matplotlib.pyplot as plt

plt.ion()

use the optional label argument when plotting the fill:

plt.fill_between(
    x, 
    y1, 
    y2, 
    color="yellow", 
    label="cone"
)

plt.pause(0.001) # refresh the animation

later in our script we can select by label to delete that specific fill or a list of fills, thus animating on a object by object basis.

axis = plt.gca()

fills = ["cone", "sideways", "market"]   

for collection in axis.collections:
    if str(collection.get_label()) in fills:
        collection.remove()
        del collection

plt.pause(0.001)

you can use the same label for groups of objects you would like to delete; or otherwise encode the labels with tags as needed to suit needs

for example if we had fills labelled:

"cone1" "cone2" "sideways1"

if "cone" in str(collection.get_label()):

would sort to delete both those prefixed with "cone".

You can also animate lines in the same manner

for line in axis.lines:
查看更多
Melony?
5楼-- · 2020-07-02 11:13

this is not my answer, but I found it most useful:

http://matplotlib.1069221.n5.nabble.com/animation-of-a-fill-between-region-td42814.html

Hi Mauricio, Patch objects are a bit more difficult to work with than line objects, because unlike line objects are a step removed from the input data supplied by the user. There is an example similar to what you want to do here: http://matplotlib.org/examples/animation/histogram.html

Basically, you need to modify the vertices of the path at each frame. It might look something like this:

from matplotlib import animation
import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.set_xlim([0,10000])

x = np.linspace(6000.,7000., 5)
y = np.ones_like(x)

collection = plt.fill_between(x, y)

def animate(i):
    path = collection.get_paths()[0]
    path.vertices[:, 1] *= 0.9

animation.FuncAnimation(fig, animate,
                        frames=25, interval=30)

Take a look at path.vertices to see how they're laid out. Hope that helps, Jake

查看更多
爱情/是我丢掉的垃圾
6楼-- · 2020-07-02 11:15

If you don't want to use anitmation, or to remove everything from your figure to update only filling, you could use my way :

call fill_lines.remove() and then call again axes_dummy.fill_between() to draw new ones. Worked for me.

查看更多
登录 后发表回答