Repository: scverse/scanpy

Issue:
Unable to plot embedding after Scanpy release 1.9.7
### Please make sure these conditions are met

- [X] I have checked that this issue has not already been reported.
- [X] I have confirmed this bug exists on the latest version of scanpy.
- [ ] (optional) I have confirmed this bug exists on the master branch of scanpy.

### What happened?

Hello,

With the new scanpy version `1.9.7` I'm not able to plot embeddings such as UMAPs when using numpy `1.23` (it's probably also true for other "old" versions of numpy).

Note that I can't update numpy to a newer version because of other packages I'm using, but `1.23` is more recent that what scanpy requires anyway (i.e., `numpy>=1.17.0`). For instance, the `spatialdata` library requires `numpy<=1.23.4` because of `xarray-spatial`: thus, it seems that the latest version of `scanpy` is not compatible with `spatialdata` (cc @LucaMarconato for information)

The error seems to be due to this commit in `_validate_palette`: https://github.com/scverse/scanpy/commit/d1fe8da28ab4865b6c2b3d9cd151a8186f148844 (@flying-sheep)

### Minimal code sample

```python
# Just plotting a dummy UMAP

import anndata
import pandas as pd
import numpy as np
import scanpy as sc

n_obs = 10

adata = anndata.AnnData(
    X=np.random.randint(0, 5, size=(n_obs, 8)),
    obs=pd.DataFrame({
            "cell_type": np.random.choice(["A", "B", "C"], size=n_obs)},
            index=[str(i) for i in range(n_obs)]
        ),
    )

sc.pp.neighbors(adata)
sc.tl.umap(adata)

sc.pl.umap(adata, color="cell_type")
```


### Error output

```pytb
---------------------------------------------------------------------------
UFuncTypeError                            Traceback (most recent call last)
Cell In[6], line 1
----> 1 sc.pl.umap(adata, color="cell_type")

File ~/mambaforge/envs/new/lib/python3.10/site-packages/scanpy/plotting/_tools/scatterplots.py:674, in umap(adata, **kwargs)
    615 @_wraps_plot_scatter
    616 @_doc_params(
    617     adata_color_etc=doc_adata_color_etc,
   (...)
    621 )
    622 def umap(adata, **kwargs) -> Union[Axes, List[Axes], None]:
    623     """\
    624     Scatter plot in UMAP basis.
    625 
   (...)
    672     tl.umap
    673     """
--> 674     return embedding(adata, 'umap', **kwargs)

File ~/mambaforge/envs/new/lib/python3.10/site-packages/scanpy/plotting/_tools/scatterplots.py:267, in embedding(adata, basis, color, gene_symbols, use_raw, sort_order, edges, edges_width, edges_color, neighbors_key, arrows, arrows_kwds, groups, components, dimensions, layer, projection, scale_factor, color_map, cmap, palette, na_color, na_in_legend, size, frameon, legend_fontsize, legend_fontweight, legend_loc, legend_fontoutline, colorbar_loc, vmax, vmin, vcenter, norm, add_outline, outline_width, outline_color, ncols, hspace, wspace, title, show, save, ax, return_fig, **kwargs)
    258 for count, (value_to_plot, dims) in enumerate(zip(color, dimensions)):
    259     color_source_vector = _get_color_source_vector(
    260         adata,
    261         value_to_plot,
   (...)
    265         groups=groups,
    266     )
--> 267     color_vector, categorical = _color_vector(
    268         adata,
    269         value_to_plot,
    270         color_source_vector,
    271         palette=palette,
    272         na_color=na_color,
    273     )
    275     # Order points
    276     order = slice(None)

File ~/mambaforge/envs/new/lib/python3.10/site-packages/scanpy/plotting/_tools/scatterplots.py:1230, in _color_vector(adata, values_key, values, palette, na_color)
   1226 if values.dtype == bool:
   1227     values = pd.Categorical(values.astype(str))
   1228 color_map = {
   1229     k: to_hex(v)
-> 1230     for k, v in _get_palette(adata, values_key, palette=palette).items()
   1231 }
   1232 # If color_map does not have unique values, this can be slow as the
   1233 # result is not categorical
   1234 color_vector = pd.Categorical(values.map(color_map))

File ~/mambaforge/envs/new/lib/python3.10/site-packages/scanpy/plotting/_tools/scatterplots.py:1197, in _get_palette(adata, values_key, palette)
   1195     _utils._set_default_colors_for_categorical_obs(adata, values_key)
   1196 else:
-> 1197     _utils._validate_palette(adata, values_key)
   1198 return dict(zip(values.categories, adata.uns[color_key]))

File ~/mambaforge/envs/new/lib/python3.10/site-packages/scanpy/plotting/_utils.py:357, in _validate_palette(adata, key)
    355     _palette.append(color)
    356 # Don't modify if nothing changed
--> 357 if _palette is None or np.equal(_palette, adata.uns[color_key]).all():
    358     return
    359 adata.uns[color_key] = _palette

UFuncTypeError: ufunc 'equal' did not contain a loop with signature matching types (<class 'numpy.dtype[str_]'>, <class 'numpy.dtype[str_]'>) -> None
```


### Versions

<details>

```
-----
anndata     0.10.4
scanpy      1.9.7
-----
PIL                 10.2.0
appnope             0.1.3
asttokens           NA
comm                0.2.1
cycler              0.12.1
cython_runtime      NA
dateutil            2.8.2
debugpy             1.8.0
decorator           5.1.1
exceptiongroup      1.2.0
executing           2.0.1
h5py                3.10.0
ipykernel           6.29.0
jedi                0.19.1
joblib              1.3.2
kiwisolver          1.4.5
llvmlite            0.41.1
matplotlib          3.8.2
matplotlib_inline   0.1.6
mpl_toolkits        NA
natsort             8.4.0
numba               0.58.1
numpy               1.23.0
packaging           23.2
pandas              2.2.0
parso               0.8.3
pexpect             4.9.0
platformdirs        4.1.0
prompt_toolkit      3.0.43
psutil              5.9.8
ptyprocess          0.7.0
pure_eval           0.2.2
pydev_ipython       NA
pydevconsole        NA
pydevd              2.9.5
pydevd_file_utils   NA
pydevd_plugins      NA
pydevd_tracing      NA
pygments            2.17.2
pynndescent         0.5.11
pyparsing           3.1.1
pytz                2023.3.post1
scipy               1.12.0
session_info        1.0.0
six                 1.16.0
sklearn             1.4.0
stack_data          0.6.3
threadpoolctl       3.2.0
tornado             6.4
tqdm                4.66.1
traitlets           5.14.1
umap                0.5.5
wcwidth             0.2.13
zmq                 25.1.2
zoneinfo            NA
-----
IPython             8.20.0
jupyter_client      8.6.0
jupyter_core        5.7.1
-----
Python 3.10.13 | packaged by conda-forge | (main, Dec 23 2023, 15:35:25) [Clang 16.0.6 ]
macOS-14.1.1-arm64-arm-64bit
-----
Session information updated at 2024-01-25 16:44
```

</details>



Discussion:
Interesting! I was coming across this error in #2816 (where there is a fix), but only with older versions of dependencies. Probably worth back porting that.
I am having the same issue here using google colab


Resolving patch:
diff --git a/scanpy/plotting/_utils.py b/scanpy/plotting/_utils.py
--- a/scanpy/plotting/_utils.py
+++ b/scanpy/plotting/_utils.py
@@ -380,8 +380,8 @@ def _validate_palette(adata: AnnData, key: str) -> None:
                 _palette = None
                 break
         _palette.append(color)
-    # Don't modify if nothing changed
-    if _palette is None or np.equal(_palette, adata.uns[color_key]).all():
+    # Don’t modify if nothing changed
+    if _palette is None or np.array_equal(_palette, adata.uns[color_key]):
         return
     adata.uns[color_key] = _palette
 


Test patch: