Search code examples
pythonmatplotlibmplot3dmatplotlib-3d

How do you keep the axes constant while adding new data?


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


Solution

  • 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.