Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I re-draw only one set of axes on a figure in python?

I have several plots in one figure using subplot. Each axes instance is used to refrence a specific set of axes. Like so:

ax[0] = fig.add_subplot(2, 2, 1)
ax[1] = fig.add_subplot(2, 1, 2)
ax[2] = fig.add_subplot(2, 2, 2,projection='3d')

As you can see, one of my sets of axes is a 3d plot. I make a change to one of the properties of the other plots like so:

plt.setp(zh, xdata=event.xdata,ydata=event.ydata)

and re-draw like so:

zh.figure.canvas.draw();

However, this is re-drawing the ENTIRE figure with all suplots, including the 3D projected one, which is slowing things down quite significantly.

I've tried this:

ax[0].draw_artist(zh)

Which I thought had promise, but the axes isn't being updated. I'm not getting an error, it's just not re-drawing. I also tried:

zh.axes.draw(zh,ax[0])

but that gives the error:

AttributeError: 'Line2D' object has no attribute 'open_group'

Any ideas as to why this is happening, and how I can just re-draw the axes instance that I'm changing instead of the entire figure?

EDIT:

zh is a Line2D object:

zh, = plt.plot(z.real, z.imag, 'x', ms=10)
like image 426
stanri Avatar asked Jun 12 '12 18:06

stanri


People also ask

How do you limit the range of values on each of the axes in MatPlotLib?

To set the limits for X-axis only, We could use xlim() and set_xlim() methods. Similarly to set the limits for Y-axis, we could use ylim() and set_ylim() methods. We can also use axis() method which can control the range of both axes.


1 Answers

You can't draw a single axes, but you can update only a single axes.

Basically, you need to blit things. If the range (and therefore the ticks, etc) of the axes are changing, this gets more complicated. For the moment, I'll assume that's not the case.

As an over-simplified example:

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
x = np.linspace(0, 4*np.pi, 100)
fig, axes = plt.subplots(nrows=3)

fig.canvas.draw()
background = fig.canvas.copy_from_bbox(axes[0].bbox)

lines = [ax.plot(x, np.cos(x))[0] for ax in axes]
fig.canvas.draw()

for phase in range(1000):
    fig.canvas.restore_region(background)
    lines[0].set_ydata(np.cos(x + phase / 5.0))
    axes[0].draw_artist(lines[0])
    fig.canvas.blit(axes[0].bbox)

However, the new matplotlib.animations will handle doing this for you, and will only blit the axes of the artists you specify.

Here's the same example written using matplotlib.animations.FuncAnimation:

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

x = np.linspace(0, 4*np.pi, 100)
fig, axes = plt.subplots(nrows=3)

lines = [axes[0].plot(x, np.cos(x), animated=True)[0]]
lines += [ax.plot(x, np.cos(x)) for ax in axes[1:]]

class Update(object):
    def __init__(self, line):
        self.phase = 0
        self.line = line
    def __call__(self, _):
        self.line.set_ydata(np.cos(x + self.phase / 5.0))
        self.phase += 1.0
        return [self.line]

anim = FuncAnimation(fig, Update(lines[0]), interval=0, blit=True) 
plt.show()

Which artists are animated (and which axes are updated) is controlled by the artists the function (or callable object, as in this case) returns.

like image 182
Joe Kington Avatar answered Oct 13 '22 00:10

Joe Kington