NotImplementedError: Cannot create a view of an awkward array with __array__ parameter

Hi there,

Thanks for these wonderful packages and scverse ecosystem. This is part 1/2 of an error I’m facing. I’m a Python and scverse beginner so I’m struggling to understand the error messages generated in Python - thanks for your patience! :slight_smile:

Expected output:
A plot similar to the one in here

Code used:

ir.pl.vdj_usage(
    adata[
        adata.obs["cc_aa_identity"].isin(["25", "13", "1119"]), :
    ],
    max_ribbons=None,
    max_segments=100,
    fig_kws={"figsize": [4,4]},
)

Error:

---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
Cell In[297], line 2
      1 # doesn't work
----> 2 ir.pl.vdj_usage(
      3     adata[
      4         adata.obs["cc_aa_identity"].isin(["25", "13", "1119"]), :
      5     ],
      6     max_ribbons=None,
      7     max_segments=100,
      8     fig_kws={"figsize": [4,4]},
      9 )
     11 # ir.pl.vdj_usage(
     12 #     adata,
     13 #     full_combination=False,
   (...)
     16 #     fig_kws={"figsize": [4, 4]},
     17 # )

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/scirpy/pl/_vdj_usage.py:108, in vdj_usage(adata, vdj_cols, normalize_to, ax, max_segments, max_labelled_segments, max_ribbons, barwidth, draw_bars, full_combination, fig_kws, airr_mod, airr_key, chain_idx_key)
     95 chains, airr_variables = zip(
     96     *[
     97         (f"{arm}_{chain}", airr_variable)
   (...)
    100     strict=False,
    101 )
    103 tmp_obs = (
    104     params.get_obs([normalize_to]).reindex(params.adata.obs_names)
    105     if isinstance(normalize_to, str)
    106     else params.adata.obs
    107 )
--> 108 df = get_airr(params, airr_variables, chains).assign(
    109     # make sure this also works with mudata columns:
    110     cell_weights=_normalize_counts(tmp_obs, normalize_to) if isinstance(normalize_to, bool | str) else normalize_to
    111 )
    112 for col in df.columns:
    113     if col.startswith("VJ") or col.startswith("VDJ"):

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/scirpy/get/__init__.py:60, in airr(adata, airr_variable, chain, airr_mod, airr_key, chain_idx_key)
     57     if not multiple_chains:
     58         chain = [chain]  # type: ignore
     59     return pd.DataFrame(
---> 60         {
     61             f"{tmp_chain}_{tmp_var}": _airr_col(
     62                 params.airr,
     63                 params.chain_indices,
     64                 tmp_var,
     65                 cast(ChainType, tmp_chain),
     66             )
     67             for tmp_chain, tmp_var in itertools.product(chain, airr_variable)
     68         },
     69         index=params.adata.obs_names,
     70     )
     71 else:
     72     return pd.Series(
     73         _airr_col(params.airr, params.chain_indices, airr_variable, cast(ChainType, chain)),
     74         index=params.adata.obs_names,
     75     )

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/scirpy/get/__init__.py:62, in <dictcomp>(.0)
     57     if not multiple_chains:
     58         chain = [chain]  # type: ignore
     59     return pd.DataFrame(
     60         {
     61             f"{tmp_chain}_{tmp_var}": _airr_col(
---> 62                 params.airr,
     63                 params.chain_indices,
     64                 tmp_var,
     65                 cast(ChainType, tmp_chain),
     66             )
     67             for tmp_chain, tmp_var in itertools.product(chain, airr_variable)
     68         },
     69         index=params.adata.obs_names,
     70     )
     71 else:
     72     return pd.Series(
     73         _airr_col(params.airr, params.chain_indices, airr_variable, cast(ChainType, chain)),
     74         index=params.adata.obs_names,
     75     )

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/scirpy/util/__init__.py:213, in DataHandler.airr(self)
    211 """Reference to the awkward array with AIRR information."""
    212 if self._airr_key is not None:
--> 213     return cast(ak.Array, self.adata.obsm[self._airr_key])
    214 else:
    215     raise AttributeError("DataHandler was initialized wihtout airr information")

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/aligned_mapping.py:149, in AlignedViewMixin.__getitem__(self, key)
    148 def __getitem__(self, key: str) -> V:
--> 149     return as_view(
    150         _subset(self.parent_mapping[key], self.subset_idx),
    151         ElementRef(self.parent, self.attrname, (key,)),
    152     )

File ~/miniconda3/envs/scirpy/lib/python3.11/functools.py:909, in singledispatch.<locals>.wrapper(*args, **kw)
    905 if not args:
    906     raise TypeError(f'{funcname} requires at least '
    907                     '1 positional argument')
--> 909 return dispatch(args[0].__class__)(*args, **kw)

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/views.py:370, in as_view_awkarray(array, view_args)
    365 # TODO: See https://github.com/scverse/anndata/pull/647#discussion_r963494798_ for more details and
    366 # possible strategies to stack behaviors.
    367 # A better solution might be based on xarray-style "attrs", once this is implemented
    368 # https://github.com/scikit-hep/awkward/issues/1391#issuecomment-1412297114
    369 if type(array).__name__ != "Array":
--> 370     raise NotImplementedError(
    371         "Cannot create a view of an awkward array with __array__ parameter. "
    372         "Please open an issue in the AnnData repo and describe your use-case."
    373     )
    374 array = ak.with_parameter(array, _PARAM_NAME, (parent_key, attrname, keys))
    375 array = ak.with_parameter(array, "__list__", "AwkwardArrayView")

NotImplementedError: Cannot create a view of an awkward array with __array__ parameter. Please open an issue in the AnnData repo and describe your use-case.

adata

AnnData object with n_obs × n_vars = 3618 × 16185
    obs: 'orig.ident', 'nCount_RNA', 'nFeature_RNA', 'decontX_contamination', 'seqType', ... 'contigCellBarcode', 'barcode', 'CTgene', 'CTnt', 'CTaa', 'CTstrict', 'Frequency', 'cloneType', 'ident', 'receptor_type', 'receptor_subtype', 'chain_pairing', 'cc_aa_identity', 'cc_aa_identity_size', 'clone_id', 'clone_id_size', 'VJ_1_junction_aa', 'VDJ_1_junction_aa', 'VJ_2_junction_aa', 'VDJ_2_junction_aa', 'clonal_expansion', 'VJ_1_v_call', 'VDJ_1_v_call', 'VJ_2_v_call', 'VDJ_2_v_call', 'VJ_1_j_call', 'VDJ_1_j_call', 'VJ_2_j_call', 'VDJ_2_j_call'
    var: 'highly_variable', 'means', 'dispersions', 'dispersions_norm', 'highly_variable_nbatches', 'highly_variable_intersection'
    uns: 'X_name', 'chain_indices', 'hvg', 'log1p', 'chain_pairing_colors', 'ir_dist_aa_identity', 'ir_dist_nt_identity', 'cc_aa_identity', 'clonotype_network', 'PatientID_colors', 'clone_id', 'seuratClusters_colors', 'sampleSource_colors', 'repertoire_overlap'
    obsm: 'airr', 'chain_indices', 'X_clonotype_network'
    layers: 'logcounts'

session info:

Click to view session information
-----
anndata             0.10.8
awkward             2.6.9
matplotlib          3.9.2
natsort             8.4.0
numpy               2.0.2
pandas              2.2.3
scanpy              1.10.3
scipy               1.14.1
scirpy              0.18.1.dev8+g7817ce8
seaborn             0.13.2
session_info        1.0.0
tcrdist             NA
-----
Click to view modules imported as dependencies
Levenshtein         0.26.0
PIL                 11.0.0
adjustText          1.2.0
asttokens           NA
awkward_cpp         NA
charset_normalizer  3.4.0
comm                0.2.2
cycler              0.12.1
cython_runtime      NA
dateutil            2.9.0.post0
debugpy             1.8.7
decorator           5.1.1
executing           2.1.0
feather             0.4.1
fishersapi          1.0
fsspec              2024.9.0
h5py                3.12.1
hierdiff            NA
igraph              0.11.6
importlib_metadata  NA
ipykernel           6.29.5
jedi                0.19.1
jinja2              3.1.4
joblib              1.4.2
kiwisolver          1.4.7
legacy_api_wrap     NA
llvmlite            0.43.0
markupsafe          3.0.1
matplotlib_inline   0.1.7
mpl_toolkits        NA
mudata              0.3.1
networkx            3.4.1
numba               0.60.0
olga                NA
packaging           24.1
parasail            1.3.4
parmap              NA
parso               0.8.4
patsy               0.5.6
pexpect             4.9.0
platformdirs        4.3.6
pooch               v1.8.2
progress            1.6
prompt_toolkit      3.0.48
psutil              6.0.0
ptyprocess          0.7.0
pure_eval           0.2.3
pwseqdist           NA
pyarrow             17.0.0
pydev_ipython       NA
pydevconsole        NA
pydevd              3.1.0
pydevd_file_utils   NA
pydevd_plugins      NA
pydevd_tracing      NA
pygments            2.18.0
pyparsing           3.2.0
pytz                2024.2
rapidfuzz           3.10.0
six                 1.16.0
sklearn             1.5.2
squarify            NA
stack_data          0.6.3
statsmodels         0.14.4
tcrsampler          NA
texttable           1.7.0
threadpoolctl       3.5.0
tornado             6.4.1
tqdm                4.66.5
tracerlib           NA
traitlets           5.14.3
typing_extensions   NA
wcwidth             0.2.13
yaml                6.0.2
zipdist             NA
zipp                NA
zmq                 26.2.0
zoneinfo            NA
-----
IPython             8.28.0
jupyter_client      8.6.3
jupyter_core        5.7.2
-----
Python 3.11.10 | packaged by conda-forge | (main, Oct 16 2024, 01:27:36) [GCC 13.3.0]
Linux-3.10.0-1160.118.1.el7.x86_64-x86_64-with-glibc2.17
-----
Session information updated at 2024-10-18 12:13

Part 2/2. Arguably more important to me right now. I’ve been trying to add a TCRdist metric to my data:

Expected output:
To continue the tutorial with TCRdist:

Code used:

from scipy.sparse import csr_matrix

def add_dists(adata, df_dist_alpha, df_dist_beta, name, cutoff):

    adata.uns[f"ir_dist_aa_{name}"] = {
        "params": {"metric": f"{name}", "sequence": "aa", "cutoff": cutoff}
    }

    for chain, dists in [("VJ", df_dist_alpha), ("VDJ", df_dist_beta)]:
        if dists is None:
            continue
        dist_values = dists.values + 1
        dist_values[dist_values > (cutoff + 1)] = 0
        dist_values = csr_matrix(dist_values)
        adata.uns[f"ir_dist_aa_{name}"][chain] = {
            "seqs": dists.index.tolist(),
            "distances": dist_values,
        }


add_dists(adata_tcrdist, df_tcrdist_alpha, df_tcrdist_beta, "tcrdist", 60)

Error:

/tmp/ipykernel_2012933/207276980.py:10: ImplicitModificationWarning: Trying to modify attribute `._uns` of view, initializing view as actual.
  adata.uns[f"ir_dist_aa_{name}"] = {
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
Cell In[284], line 26
     19         dist_values = csr_matrix(dist_values)
     20         adata.uns[f"ir_dist_aa_{name}"][chain] = {
     21             "seqs": dists.index.tolist(),
     22             "distances": dist_values,
     23         }
---> 26 add_dists(adata_tcrdist, df_tcrdist_alpha, df_tcrdist_beta, "tcrdist", 60)

Cell In[284], line 10, in add_dists(adata, df_dist_alpha, df_dist_beta, name, cutoff)
      8 def add_dists(adata, df_dist_alpha, df_dist_beta, name, cutoff):
---> 10     adata.uns[f"ir_dist_aa_{name}"] = {
     11         "params": {"metric": f"{name}", "sequence": "aa", "cutoff": cutoff}
     12     }
     14     for chain, dists in [("VJ", df_dist_alpha), ("VDJ", df_dist_beta)]:
     15         if dists is None:

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/views.py:78, in _SetItemMixin.__setitem__(self, idx, value)
     71 else:
     72     warnings.warn(
     73         f"Trying to modify attribute `.{self._view_args.attrname}` of view, "
     74         "initializing view as actual.",
     75         ImplicitModificationWarning,
     76         stacklevel=2,
     77     )
---> 78     with view_update(*self._view_args) as container:
     79         container[idx] = value

File ~/miniconda3/envs/scirpy/lib/python3.11/contextlib.py:137, in _GeneratorContextManager.__enter__(self)
    135 del self.args, self.kwds, self.func
    136 try:
--> 137     return next(self.gen)
    138 except StopIteration:
    139     raise RuntimeError("generator didn't yield") from None

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/views.py:52, in view_update(adata_view, attr_name, keys)
     32 @contextmanager
     33 def view_update(adata_view: AnnData, attr_name: str, keys: tuple[str, ...]):
     34     """Context manager for updating a view of an AnnData object.
     35 
     36     Contains logic for "actualizing" a view. Yields the object to be modified in-place.
   (...)
     50     `adata.attr[key1][key2][keyn]...`
     51     """
---> 52     new = adata_view.copy()
     53     attr = getattr(new, attr_name)
     54     container = reduce(lambda d, k: d[k], keys, attr)

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/anndata.py:1503, in AnnData.copy(self, filename)
   1497 if not self.isbacked:
   1498     if self.is_view and self._has_X():
   1499         # TODO: How do I unambiguously check if this is a copy?
   1500         # Subsetting this way means we don’t have to have a view type
   1501         # defined for the matrix, which is needed for some of the
   1502         # current distributed backend. Specifically Dask.
-> 1503         return self._mutated_copy(
   1504             X=_subset(self._adata_ref.X, (self._oidx, self._vidx)).copy()
   1505         )
   1506     else:
   1507         return self._mutated_copy()

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/anndata.py:1435, in AnnData._mutated_copy(self, **kwargs)
   1433         new[key] = kwargs[key]
   1434     else:
-> 1435         new[key] = getattr(self, key).copy()
   1436 if "X" in kwargs:
   1437     new["X"] = kwargs["X"]

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/aligned_mapping.py:119, in AlignedMapping.copy(self)
    117 def copy(self):
    118     d = self._actual_class(self.parent, self._axis)
--> 119     for k, v in self.items():
    120         if isinstance(v, AwkArray):
    121             # Shallow copy since awkward array buffers are immutable
    122             d[k] = copy(v)

File ~/miniconda3/envs/scirpy/lib/python3.11/_collections_abc.py:861, in ItemsView.__iter__(self)
    859 def __iter__(self):
    860     for key in self._mapping:
--> 861         yield (key, self._mapping[key])

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/aligned_mapping.py:149, in AlignedViewMixin.__getitem__(self, key)
    148 def __getitem__(self, key: str) -> V:
--> 149     return as_view(
    150         _subset(self.parent_mapping[key], self.subset_idx),
    151         ElementRef(self.parent, self.attrname, (key,)),
    152     )

File ~/miniconda3/envs/scirpy/lib/python3.11/functools.py:909, in singledispatch.<locals>.wrapper(*args, **kw)
    905 if not args:
    906     raise TypeError(f'{funcname} requires at least '
    907                     '1 positional argument')
--> 909 return dispatch(args[0].__class__)(*args, **kw)

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/views.py:370, in as_view_awkarray(array, view_args)
    365 # TODO: See https://github.com/scverse/anndata/pull/647#discussion_r963494798_ for more details and
    366 # possible strategies to stack behaviors.
    367 # A better solution might be based on xarray-style "attrs", once this is implemented
    368 # https://github.com/scikit-hep/awkward/issues/1391#issuecomment-1412297114
    369 if type(array).__name__ != "Array":
--> 370     raise NotImplementedError(
    371         "Cannot create a view of an awkward array with __array__ parameter. "
    372         "Please open an issue in the AnnData repo and describe your use-case."
    373     )
    374 array = ak.with_parameter(array, _PARAM_NAME, (parent_key, attrname, keys))
    375 array = ak.with_parameter(array, "__list__", "AwkwardArrayView")

NotImplementedError: Cannot create a view of an awkward array with __array__ parameter. Please open an issue in the AnnData repo and describe your use-case.

adata & session info same as above.

Sorry to add to this, but adata_tcrdist = adata_tcrdist.copy() also generates the same Part 2/2 error

Error message:

---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
Cell In[387], line 1
----> 1 adata_tcrdist = adata_tcrdist.copy()

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/anndata.py:1503, in AnnData.copy(self, filename)
   1497 if not self.isbacked:
   1498     if self.is_view and self._has_X():
   1499         # TODO: How do I unambiguously check if this is a copy?
   1500         # Subsetting this way means we don’t have to have a view type
   1501         # defined for the matrix, which is needed for some of the
   1502         # current distributed backend. Specifically Dask.
-> 1503         return self._mutated_copy(
   1504             X=_subset(self._adata_ref.X, (self._oidx, self._vidx)).copy()
   1505         )
   1506     else:
   1507         return self._mutated_copy()

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/anndata.py:1435, in AnnData._mutated_copy(self, **kwargs)
   1433         new[key] = kwargs[key]
   1434     else:
-> 1435         new[key] = getattr(self, key).copy()
   1436 if "X" in kwargs:
   1437     new["X"] = kwargs["X"]

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/aligned_mapping.py:119, in AlignedMapping.copy(self)
    117 def copy(self):
    118     d = self._actual_class(self.parent, self._axis)
--> 119     for k, v in self.items():
    120         if isinstance(v, AwkArray):
    121             # Shallow copy since awkward array buffers are immutable
    122             d[k] = copy(v)

File ~/miniconda3/envs/scirpy/lib/python3.11/_collections_abc.py:861, in ItemsView.__iter__(self)
    859 def __iter__(self):
    860     for key in self._mapping:
--> 861         yield (key, self._mapping[key])

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/aligned_mapping.py:149, in AlignedViewMixin.__getitem__(self, key)
    148 def __getitem__(self, key: str) -> V:
--> 149     return as_view(
    150         _subset(self.parent_mapping[key], self.subset_idx),
    151         ElementRef(self.parent, self.attrname, (key,)),
    152     )

File ~/miniconda3/envs/scirpy/lib/python3.11/functools.py:909, in singledispatch.<locals>.wrapper(*args, **kw)
    905 if not args:
    906     raise TypeError(f'{funcname} requires at least '
    907                     '1 positional argument')
--> 909 return dispatch(args[0].__class__)(*args, **kw)

File ~/miniconda3/envs/scirpy/lib/python3.11/site-packages/anndata/_core/views.py:370, in as_view_awkarray(array, view_args)
    365 # TODO: See https://github.com/scverse/anndata/pull/647#discussion_r963494798_ for more details and
    366 # possible strategies to stack behaviors.
    367 # A better solution might be based on xarray-style "attrs", once this is implemented
    368 # https://github.com/scikit-hep/awkward/issues/1391#issuecomment-1412297114
    369 if type(array).__name__ != "Array":
--> 370     raise NotImplementedError(
    371         "Cannot create a view of an awkward array with __array__ parameter. "
    372         "Please open an issue in the AnnData repo and describe your use-case."
    373     )
    374 array = ak.with_parameter(array, _PARAM_NAME, (parent_key, attrname, keys))
    375 array = ak.with_parameter(array, "__list__", "AwkwardArrayView")

NotImplementedError: Cannot create a view of an awkward array with __array__ parameter. Please open an issue in the AnnData repo and describe your use-case.

Hi @yls2g13,

something weird is going on here (also given that I couldn’t reproduce your other error). I have two suspicions:

  1. your conda env is corrupted and it partly contains code from older version of awkward or anndata. In that case re-creating the environment from scratch could help.

  2. The anndata object was created a long time ago when AwkwardArray support in AnnData was not mature yet and you now reloaded it and it is somehow not compatible with the latest versions of AnnData and Awkward. In that case it could help to recreate the AnnData object from the raw data, or if that’s not available, export the AIRR data using ir.io.write_airr and re-importing it to a new object using ir.io.read_airr.

For further inspection, could you please also provide the output of adata.obsm["airr"]?