Reusing patch objects in matplotlib without them m

2019-05-01 15:34发布

问题:

I want to automatically generate a series of plots which are clipped to patches. If I try and reuse a patch object, it moves position across the canvas.

This script (based on an answer to a previous question by Yann) demonstrates what is happening.

import pylab as plt
import scipy as sp
import matplotlib.patches as patches

sp.random.seed(100)
x = sp.random.random(100)
y = sp.random.random(100)
patch = patches.Circle((.75,.75),radius=.25,fc='none')


def doplot(x,y,patch,count):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    im = ax.scatter(x,y)
    ax.add_patch(patch)
    im.set_clip_path(patch)
    plt.savefig(str(count) + '.png')


for count in xrange(4):
    doplot(x,y,patch,count)

The first plot looks like this:

But in the second '1.png', the patch has moved..

However replotting again doesn't move the patch. '2.png' and '3.png' look exactly the same as '1.png'.

Could anyone point me in the right direction of what I'm doing wrong??

In reality, the patches I'm using are relatively complex and take some time to generate - I'd prefer to not have to remake them every frame if possible.

回答1:

The problem can be avoided by using the same axes for each plot, with ax.cla() called to clear the plot after each iteration.

import pylab as plt
import scipy as sp
import matplotlib.patches as patches

sp.random.seed(100)
patch = patches.Circle((.75,.75),radius=.25,fc='none')

fig = plt.figure()
ax = fig.add_subplot(111)

def doplot(x,y,patch,count):
    ax.set_xlim(-0.2,1.2)
    ax.set_ylim(-0.2,1.2)
    x = sp.random.random(100)
    y = sp.random.random(100)
    im = ax.scatter(x,y)
    ax.add_patch(patch)
    im.set_clip_path(patch)
    plt.savefig(str(count) + '.png')
    ax.cla()

for count in xrange(4):
    doplot(x,y,patch,count)


回答2:

An alternative to unutbu's answer, is to use the copy package, which can copy objects. It is very hard to see how things are changing after one calls add_patch, but they are. The axes, figure, extents,clip_box,transform and window_extent properties of the patch are changed. Unfortantely the superficial printing of each of these properties results in the same string, so it looks like they are not changing. But the underlying attributes of some or all of these properties, eg extents is a Bbox, are probably changed.

The copy call will allow you to get a unique patch for each figure you make, without know what kind of patch it is. This still does not answer why this happens, but as I wrote above it's an alternative solution to the problem:

import copy 

def doplot(x,y,patch,count):
    newPatch = copy.copy(patch)
    fig = plt.figure(dpi=50)
    ax = fig.add_subplot(111)
    im = ax.scatter(x,y)
    ax.add_patch(newPatch)
    im.set_clip_path(newPatch)
    plt.savefig(str(count) + '.png')

Also you can use fig.savefig(str(count) + '.png'). This explicitly saves the figure fig where as the plt.savefig call saves the current figure, which happens to be the one you want.