Proper way to erase all data in a layer?

I’ve been running into an issue, in which in previous versions of napari, I could reliably clear a shapes layer using
layer.data = []
or
layer.data = np.empty((0, 2))

This worked, although I’ve always felt a little uncomfortable with this, as it feels like I should be using a method as opposed to directly tampering with data. Is there a “correct” way to clear a layer? (I would prefer not to remove the layer entirely).

As of upgrading to napari 0.3.6, I’m getting a new error when I try to clear out data from a shapes layer (points layer seems okay). The relevant section of my code looks like:

for layer in viewer.layers:  # erase all annotation layers
    layer_type = layer.as_layer_data_tuple()[2]
    if layer_type == 'points':
        layer.data = np.empty((0, 2))
    elif layer_type == 'shapes':
        layer.data = []

which used to work, but now produces this error:

  File "my_prog.py", line 402, in annotate
    layer.data = []
  File "/home/nate/.pyenv/versions/histo_new/lib/python3.7/site-packages/napari/layers/shapes/shapes.py", line 512, in data
    self.add(data, shape_type=shape_type)
  File "/home/nate/.pyenv/versions/histo_new/lib/python3.7/site-packages/napari/layers/shapes/shapes.py", line 1432, in add
    if np.array(data[0]).ndim == 1:
IndexError: list index out of range

A minimal example would be something like this:

import napari
import numpy as np
viewer = napari.Viewer()
viewer.add_shapes(np.random.rand(10,5,2)*100, shape_type='polygon')
viewer.layers['Shapes'].data = []  # this used to work in 0.3.5!

Any suggestions as to 1) the appropriate way to clear data, and in lieu of that 2) ideas about why this is no longer working would be greatly appreciated. Thanks!

Interesting, this seems like an unintended regression! I don’t think we have any established best way to clear data from points/ shapes or unit tests for that behaviour, but we should add some!!

I think layer.data = [] is quite natural, particularly for Shapes, where data is a list of arrays. One awkward thing though concerns the “dimensionality” of the layer and whether that gets lost or preserved in these cases. Something like [np.empty((0, 2))] should work too, but it’s not so user friendly.

I’m about to sign off for the night but can think about this more tomorrow. Maybe @kevinyamauchi has suggestions here?

1 Like

Thanks so much for your super fast response!! At previous suggestions from @jni , I initialize all layers to the correct dimensionality that way with empty numpy arrays of defined dimensionality, and I agree, it makes more sense to clear them that way. However, I get different errors if I use either of the following:


viewer.layers['Shapes'].data = [np.empty((0,2))]
[]
Traceback (most recent call last):

  File "<ipython-input-29-9b336ebeff45>", line 1, in <module>
    viewer.layers['Shapes'].data = [np.empty((0,2))]

  File "/home/nate/.pyenv/versions/3.7.4/envs/histo_new/lib/python3.7/site-packages/napari/layers/shapes/shapes.py", line 512, in data
    self.add(data, shape_type=shape_type)

  File "/home/nate/.pyenv/versions/3.7.4/envs/histo_new/lib/python3.7/site-packages/napari/layers/shapes/shapes.py", line 1467, in add
    z_index=z_index,

  File "/home/nate/.pyenv/versions/3.7.4/envs/histo_new/lib/python3.7/site-packages/napari/layers/shapes/shapes.py", line 1664, in _add_shapes
    ndisplay=self.dims.ndisplay,

  File "/home/nate/.pyenv/versions/3.7.4/envs/histo_new/lib/python3.7/site-packages/napari/layers/shapes/_shapes_models/rectangle.py", line 36, in __init__
    self.data = data

  File "/home/nate/.pyenv/versions/3.7.4/envs/histo_new/lib/python3.7/site-packages/napari/layers/shapes/_shapes_models/rectangle.py", line 60, in data
    {len(data)} provided."""

ValueError: Data shape does not match a rectangle.
                             Rectangle expects four corner vertices,
                             0 provided.

or


viewer.layers['Shapes'].data = np.empty((0,0,2))
Traceback (most recent call last):

  File "<ipython-input-31-6e9a832b0c14>", line 1, in <module>
    viewer.layers['Shapes'].data = np.empty((0,0,2))

  File "/home/nate/.pyenv/versions/3.7.4/envs/histo_new/lib/python3.7/site-packages/napari/layers/shapes/shapes.py", line 512, in data
    self.add(data, shape_type=shape_type)

  File "/home/nate/.pyenv/versions/3.7.4/envs/histo_new/lib/python3.7/site-packages/napari/layers/shapes/shapes.py", line 1432, in add
    if np.array(data[0]).ndim == 1:

IndexError: index 0 is out of bounds for axis 0 with size 0

Any chance this will get fixed any time soon? Or that there will be any other way to erase all existing data from a Shapes layer? Thanks!

Hi, I’m so sorry this looks like it fell off our radar, and I just saw this, but thanks for asking us again. I’ve made an issue on our github so we don’t lose track of this again. Hopefully someone can look into this soon, and if not I’ll take another look myself.

Here’s a link to the github issue where you can follow progress:

Thanks for your patience!

2 Likes

Hi guys,
I ran into similar issue. For now this trick works in my case (not sure if applicable in yours; I’m also not sure if it leaves behind any unhandled layer changes):

viewer.layers[‘name’].selected_data = set(range( self.viewer.layers[‘name’].nshapes))
viewer.layers[‘name’].remove_selected()

(if you want to visualize selected data - call layer.refresh() )

Cheers,
Dmitry.

This will erase all the layers in the layer list as opposed to all the shape data in one Shapes layer (I was confused about this myself first time I read this), but we definitely want to make that easy for people too!!

[EDIT: I am wrong here, see followup posts for details, the above syntax does erase every shape in a layer, my apologies for only adding to the confusion!!]

Are you sure? Because it does exactly this: removes all shapes in one shape layer (as opposed to removing all the layers in the layer list).
And I also used layer.data = [] in older napari versions for the same effect.

I was wrong! I am so sorry, I read that too quickly yesterday and forgot that we had added the remove_selected syntax to the layer itself too (it’s also on the LayerList object which I was thinking of and had me confused). Thanks for providing the helpful answer.

I’ve gone ahead and made a small PR that makes the old syntax work again here https://github.com/napari/napari/pull/1718, it would be great if @dershoff and @naten7k you could let us know that things were fixed for you by that PR.

You should now be able to do the following and it should just work

viewer.layers['name'].data = []
# or
viewer.layers['name'].data = np.empty((0, 2))