Skip to content

Surprising behaviour of mutating input arrays to Axes.plot vs Axes3D.plot #8990

Closed
@adeak

Description

@adeak

I found an interesting question on Stack Overflow that I tracked down to how mutating input arrays to Axes3D.plot behaves, i.e. changes in input x/y/z arrays is reflected in the resulting figure to varying degree.

Consider the following example:

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

fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')

# first set data to zero; use lists
x = [1,2,3]
y = [0.0,0.0,0.0]
z = [0.0,0.0,0.0]

# plot the flat line
pl, = ax.plot(x,y,z,'o-')

# change the axes limits to see the result; not actually relevant
ax.set_ylim([0,4])
ax.set_zlim([0,4])

# mutate y,z to get a nontrivial line
y[:] = [1,2,3]
z[:] = [1,2,3]

plt.show()

The y and z inputs are originally zero-valued lists, and these are mutated after the call to Axes3D.plot. The result is the following:

demonstration

It turns out that the result is affected by mutating the inputs, but from y and z only the latter gets mutated. The underlying data of the plot is

print(pl._verts3d)
# (array([1, 2, 3]), array([ 0.,  0.,  0.]), [1, 2, 3])

As I understand it, the initial call to Axes.plot that creates the line objects convert the x and y lists to arrays, and so these inputs get decoupled from the original objects. z on the other hand is handled differently by Line3D.set_3d_properties via art3d.line_2d_to_3d. If I change the definition of y to be a numpy.ndarray, y will also be changed in the resulting plot, which is in agreement with my interpretation.

Now, I find it likely that this is not a bug but rather by design, but I'm not sure.
Reasons why this might not be a bug:

  1. This closed issue discussed how it's by design that Line2D objects don't copy their input, and I expect this to be the case for 3D too; memory is still memory.
  2. Mutating input sources like this is quite weird, and this should probably never be done on purpose. If I had to, I'd probably try using dedicated methods such as Line2D.set_xdata instead. This is actually a pretty strong point in my opinion.

Reasons why this might be a bug:

  1. Probably due to the caching described in the aforementioned issue, mutating the input source of regular 2d plots does not affect the resulting plot. It adds an additional level of surprise that Axes3D.plot behaves this differently from Axes.plot in a seemingly similar situation.
  2. Again, I believe I understand why passing in lists instead of arrays and mutating them gets handled differently, but the fact that mutating both y and z inputs can lead to the change reflected in only one of them is again additionally surprising.
  3. I'm not sure that dedicated data replacement methods such as Line2D.set_xdata are available for Line3D objects; as far as I can tell xdata and ydata for these are redefined in the 3d coordinate system (and they also depend on the current view point). If such methods are indeed missing, then one might argue that mutating the inputs can be necessary after all.

I'm using matplotlib 2.0.2.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions