Description
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:
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:
- 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. - 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:
- 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 fromAxes.plot
in a seemingly similar situation. - 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
andz
inputs can lead to the change reflected in only one of them is again additionally surprising. - I'm not sure that dedicated data replacement methods such as
Line2D.set_xdata
are available forLine3D
objects; as far as I can tellxdata
andydata
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.