2

I'm trying to make a 3d scatterplot with fading points over time, similar to this question. However, when I apply the colormap to the data, then the face of the marker changes color but the edge remains solid.

I've tried using the edgecolors='None', edgecolor='none' kwargs, the marker='o' kwarg as per this example, and the edgecolor='face' kwarg. I've also tried using post creation methods such as scat3D.set_edgecolor("None") etc. The first attempts throw an error, and the latter have no effect.

Error from setting kwargs:

Traceback (most recent call last):
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/animation.py", line 230, in saving
    yield self
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/animation.py", line 1156, in save
    writer.grab_frame(**savefig_kwargs)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/animation.py", line 384, in grab_frame
    dpi=self.dpi, **savefig_kwargs)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/figure.py", line 2180, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/backends/backend_qt5agg.py", line 88, in print_figure
    super().print_figure(*args, **kwargs)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2082, in print_figure
    **kwargs)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/backends/backend_agg.py", line 442, in print_raw
    FigureCanvasAgg.draw(self)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/backends/backend_agg.py", line 388, in draw
    self.figure.draw(self.renderer)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/artist.py", line 38, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/figure.py", line 1709, in draw
    renderer, self, artists, self.suppressComposite)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/image.py", line 135, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/max/.local/lib/python3.6/site-packages/matplotlib/artist.py", line 38, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/max/.local/lib/python3.6/site-packages/mpl_toolkits/mplot3d/axes3d.py", line 292, in draw
    reverse=True)):
  File "/home/max/.local/lib/python3.6/site-packages/mpl_toolkits/mplot3d/axes3d.py", line 291, in <lambda>
    key=lambda col: col.do_3d_projection(renderer),
  File "/home/max/.local/lib/python3.6/site-packages/mpl_toolkits/mplot3d/art3d.py", line 545, in do_3d_projection
    ecs = (_zalpha(self._edgecolor3d, vzs) if self._depthshade else
  File "/home/max/.local/lib/python3.6/site-packages/mpl_toolkits/mplot3d/art3d.py", line 847, in _zalpha
    rgba = np.broadcast_to(mcolors.to_rgba_array(colors), (len(zs), 4))
  File "<__array_function__ internals>", line 6, in broadcast_to
  File "/home/max/.local/lib/python3.6/site-packages/numpy/lib/stride_tricks.py", line 182, in broadcast_to
    return _broadcast_to(array, shape, subok=subok, readonly=True)
  File "/home/max/.local/lib/python3.6/site-packages/numpy/lib/stride_tricks.py", line 127, in _broadcast_to
    op_flags=['readonly'], itershape=shape, order='C')
ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (0,4) and requested shape (100,4)

(There's a during the handling the above exception another occured error after that, but my post was flagged as spam so I didn't include it)

Here's the code without the error (adding a kwarg listed above causes the error):

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.patches as mpatches

total = 10

num_whatever = 100

to_plot = [np.random.rand(num_whatever, 3) for i in range(total)]
colors = np.linspace(0, 1, num_whatever)


fig = plt.figure()
ax3d = Axes3D(fig)
scat3D = ax3d.scatter([],[],[], s=10, cmap="Blues", vmin=0, vmax=1)
scat3D.set_cmap("Blues") # cmap argument above is ignored, so set it manually
ttl = ax3d.text2D(0.05, 0.95, "", transform=ax3d.transAxes)

def update_plot(i):
    print( i, to_plot[i].shape)
    ttl.set_text('PCA on 3 components at step = {}'.format(i*20))
    scat3D._offsets3d = np.transpose(to_plot[i])
    scat3D.set_array(colors)
    return scat3D,

def init():
    scat3D.set_offsets([[],[],[]])
    plt.style.use('ggplot')

ani = animation.FuncAnimation(fig, update_plot, init_func=init, 
                              blit=False, interval=300, frames=range(total))

ani.save("ani.gif", writer="imagemagick")

plt.show()

Here's the plot with the marker edges not hidden:

Plot

You can see that the faded points still have the marker edges not colormapped. I would expect that setting edgecolor would be unnecessary in the first place, since the 2d animation clearly doesn't need it. But why would adding that kwarg cause me to get a operands could not be broadcast togeather error? I don't have any array with shape (0, 4), so it's not clear where this is coming from.

user27443
  • 424
  • 4
  • 10

1 Answers1

3

The problem is that if c (as the color argument) is not specified, it is not a priori clear if a colormapping should happen or not. This leads to some internal confusion about which arguments to obey to. But we can specify c as an empty list to get around that.

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D

total = 10

num_whatever = 100

to_plot = [np.random.rand(num_whatever, 3) for i in range(total)]
colors = np.linspace(0, 1, num_whatever)

fig = plt.figure()
ax3d = Axes3D(fig)
scat3D = ax3d.scatter([], [], [], s=10, c=[], cmap="Blues", vmin=0, vmax=1, 
                      depthshade=False)

ttl = ax3d.text2D(0.05, 0.95, "", transform=ax3d.transAxes)

def update_plot(i):
    print( i, to_plot[i].shape)
    ttl.set_text('PCA on 3 components at step = {}'.format(i*20))
    scat3D._offsets3d = np.transpose(to_plot[i])
    scat3D.set_array(colors)
    return scat3D,

def init():
    scat3D.set_offsets([[],[],[]])
    plt.style.use('ggplot')


ani = animation.FuncAnimation(fig, update_plot, init_func=init, 
                              blit=False, interval=300, frames=range(total))
ani.save("ani.gif", writer="imagemagick")

plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712