Repository: napari/napari

Issue:
property in labels layer does not understand objects of different lengths
## 🐛 Bug

I am trying to use the properties attribute of `add_labels` to add a dictionary of properties that contains a self defined property that happens to be in the form of list but of unequal lengths. So every region has an extra property of different length. Similar to how image is a property that has different lengths for every region.

This is a silly example of what I have:

## To Reproduce

```python
import numpy as np
from skimage.measure import regionprops_table, label
import napari

def random_length_property(regionmask):
      return [0] * np.random.randint(10)

image = data.coins()
viewer = napari.view_image(image)

label_image = label(image > 110)
table_props = regionprops_table(
      label_image,
      intensity_image=image,
      extra_properties=(random_length_property,),
  )

label_layer = viewer.add_labels(
      label_image, name="segmentation", properties=table_props
  )

napari.run()
```

And this makes napari unhappy with:

```bash 
 label_layer = viewer.add_labels(
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\napari\components\viewer_model.py", line 4, in add_labels
    import itertools
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\napari\layers\labels\labels.py", line 328, in __init__
    self._feature_table = _FeatureTable.from_layer(
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\napari\layers\utils\layer_utils.py", line 956, in from_layer
    return cls(features, defaults=feature_defaults, num_data=num_data)
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\napari\layers\utils\layer_utils.py", line 788, in __init__
    self._defaults = _validate_feature_defaults(defaults, self._values)
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\napari\layers\utils\layer_utils.py", line 1015, in _validate_feature_defaults
    defaults = {c: _get_default_column(values[c]) for c in values.columns}
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\napari\layers\utils\layer_utils.py", line 1015, in <dictcomp>
    defaults = {c: _get_default_column(values[c]) for c in values.columns}
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\napari\layers\utils\layer_utils.py", line 975, in _get_default_column
    return pd.Series(data=value, dtype=column.dtype, index=range(1))
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\pandas\core\series.py", line 500, in __init__
    com.require_length_match(data, index)
  File "C:\Users\elena.pascal\AppData\Local\pypoetry\Cache\virtualenvs\image-process-zoTHhXWu-py3.8\lib\site-packages\pandas\core\common.py", line 576, in require_length_match
    raise ValueError(
ValueError: Length of values (8) does not match length of index (1)
```



## Environment

napari: 0.4.17rc4.dev505+g7c9ea89d
Platform: Windows-10-10.0.19045-SP0
Python: 3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)]
Qt: 5.15.2
PyQt5: 5.15.9
NumPy: 1.24.4
SciPy: 1.9.1
Dask: 2023.5.0
VisPy: 0.13.0
magicgui: 0.7.2
superqt: unknown
in-n-out: 0.1.8
app-model: 0.2.0
npe2: 0.7.2

OpenGL:
- GL version: 4.6.0 - Build 31.0.101.3959
- MAX_TEXTURE_SIZE: 16384

Screens:
- screen 1: resolution 2560x1440, scale 1.0
- screen 2: resolution 2560x1440, scale 1.0

Settings path:
- C:\Users\elena.pascal\AppData\Local\napari\illumion_033691400d65ecf164fea402e77f284e6b482050\settings.yaml

 - Any other relevant information:

## Additional context

@jni answered here: https://forum.image.sc/t/struggle-to-add-property-of-unequal-length-to-labels-layer/86201?u=elena_pascal



Discussion:

Resolving patch:
diff --git a/napari/layers/utils/layer_utils.py b/napari/layers/utils/layer_utils.py
--- a/napari/layers/utils/layer_utils.py
+++ b/napari/layers/utils/layer_utils.py
@@ -948,7 +948,7 @@ def _get_default_column(column: pd.Series) -> pd.Series:
         # store missing values, so passing None creates an np.float64 series
         # containing NaN. Therefore, use a default of 0 instead.
         value = 0
-    return pd.Series(data=value, dtype=column.dtype, index=range(1))
+    return pd.Series(data=[value], dtype=column.dtype, index=range(1))
 
 
 def _validate_features(


Test patch:
diff --git a/napari/layers/utils/_tests/test_layer_utils.py b/napari/layers/utils/_tests/test_layer_utils.py
--- a/napari/layers/utils/_tests/test_layer_utils.py
+++ b/napari/layers/utils/_tests/test_layer_utils.py
@@ -223,20 +223,31 @@ def test_feature_table_from_layer_with_properties_and_num_data():
     properties = {
         'class': np.array(['sky', 'person', 'building', 'person']),
         'confidence': np.array([0.2, 0.5, 1, 0.8]),
+        'varying_length_prop': np.array(
+            [[0], [0, 0, 0], [0, 0], [0]], dtype=object
+        ),
     }
 
     feature_table = _FeatureTable.from_layer(properties=properties, num_data=4)
 
     features = feature_table.values
-    assert features.shape == (4, 2)
+    assert features.shape == (4, 3)
     np.testing.assert_array_equal(features['class'], properties['class'])
     np.testing.assert_array_equal(
         features['confidence'], properties['confidence']
     )
+    np.testing.assert_array_equal(
+        features['varying_length_prop'], properties['varying_length_prop']
+    )
+
     defaults = feature_table.defaults
-    assert defaults.shape == (1, 2)
+    assert defaults.shape == (1, 3)
     assert defaults['class'][0] == properties['class'][-1]
     assert defaults['confidence'][0] == properties['confidence'][-1]
+    assert (
+        defaults['varying_length_prop'][0]
+        == properties['varying_length_prop'][-1]
+    )
 
 
 def test_feature_table_from_layer_with_properties_and_choices():