4

I'm using matplotlib to display data that is constantly being updated (changes roughly 10 times per second). I'm using a 3D scatter plot, and I would like the axes to be fixed to a specific range, since the location of the data with respect to the edges of the plot is what is important.

Currently whenever I add new data, the axes will reset to being scaled by the data, rather than the size I want (when I have hold=False). If I set hold=True, the axes will remain the right size, but the new data will be overlayed on the old data, which is not what I want.

I can get it to work if I rescale the axes everytime I get new data, but this seems like an inefficient way to do this, especially since I need to do all other formatting again as well (adding titles, legends, etc)

Is there some way in which I can specify the properties of the plot just once, and this will remain fixed as I add new data?

Here is a rough outline of my code, to help explain what I mean:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

X_MAX = 50
Y_MAX = 50
Z_MAX = 50

fig = plt.figure(1)
ax = fig.add_subplot(111, projection='3d')
ax.set_title("My Title")
ax.set_xlim3d([0, X_MAX])
ax.set_ylim3d([0, Y_MAX])
ax.set_zlim3d([0, Z_MAX])
ax.set_autoscale_on(False)
# This is so the new data replaces the old data
# seems to be replacing the axis ranges as well, maybe a different method should be used?
ax.hold(False)

plt.ion()
plt.show()

a = 0
while a < 50:
  a += 1
  ax.scatter( a, a/2+1, 3, s=1 )
  # If I don't set the title and axes ranges again here, they will be reset each time
  # I want to know if there is a way to only set them once and have it persistent
  ax.set_title("My Title")
  ax.set_xlim3d([0, X_MAX])
  ax.set_ylim3d([0, Y_MAX])
  ax.set_zlim3d([0, Z_MAX])
  plt.pause(0.001)

EDIT: 1. I have also tried ax.set_autoscale_on(False), but with no success 2. I tried this with a regular 2D scatter plot, and the same issue still exists 3. Found a related question which also still doesn't have an answer

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Brent
  • 719
  • 2
  • 9
  • 18
  • 1
    Have you looked at [autoscale](http://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.axes3d.Axes3D.autoscale)? – BrenBarn Jun 10 '13 at 19:51
  • From what @BrenBarn commented, I would test `ax.set_autoscale_on(False)` just before the `while` loop (or perhaps before `plt.show()`). – heltonbiker Jun 10 '13 at 19:55
  • I've tried using ax.set_autoscale_on(False) in a few places, as well as ax.autoscale(False), but still no luck – Brent Jun 10 '13 at 20:04
  • 1
    Matplotlib has also a [module for animations](http://matplotlib.org/api/animation_api.html?highlight=animation#matplotlib.animation), have a look at [this example](http://matplotlib.org/examples/animation/simple_3danim.html), maybe that helps. – Harpe Jun 11 '13 at 09:18
  • Thanks for the suggestion. It looks like the animation module expects you to have all of your data available when you start the animation, but for my use case the data is streaming in at real time, and possibly not a constant rate. It could still be useful for replaying the data though. – Brent Jun 11 '13 at 22:05

1 Answers1

6

I would do something like this (note removal of hold(False) ):

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

X_MAX = 50
Y_MAX = 50
Z_MAX = 50
fig = plt.figure(1)
ax = fig.add_subplot(111, projection='3d')
ax.set_title("My Title")
ax.set_xlim3d([0, X_MAX])
ax.set_ylim3d([0, Y_MAX])
ax.set_zlim3d([0, Z_MAX])
ax.set_autoscale_on(False)
plt.ion()
plt.show()

a = 0
sct = None
while a < 50:
  a += 1
  if sct is not None:
      sct.remove()
  sct = ax.scatter( a, a/2+1, 3, s=1 )
  fig.canvas.draw()
  plt.pause(0.001)

Where you remove just the added scatter plot each time through the loop.

tacaswell
  • 84,579
  • 22
  • 210
  • 199
  • That seemed to do the trick, thanks! Also helpful to note that this won't work unless you remove the ax.hold(False), which isn't needed anymore since the old plot is removed anyway. At some point I'll get around to testing what the time difference is between these two methods – Brent Jun 11 '13 at 21:44
  • This did not work for me. I get the error message `TypeError: remove() takes exactly one argument (0 given)`. (matplotlib 1.4.3) – Stephen Bosch May 25 '15 at 21:25
  • @StephenBosch That is way too cryptic, please open a new question with a minimal (not) working example. I just tested this (and updated answer with missing lines) and it works for me (on a version close to master). – tacaswell May 25 '15 at 21:30