On note, I am creating the anndata object from an .h5ad file writen from a Seurat object. Nevertheless, I checked var
and X
dimensions and they were the same (17353). I think the error may be related to lr_bivar
creating another anndata object within the function.
I have included all the files needed to create the spatial anndata object in this folder.
import pandas as pd
import anndata as ad
import liana as li
# Read H5AD
adata = ad.read_h5ad("path_to/CID44971.h5ad")
adata.layers['counts'] = adata.X.copy()
# Fill image info
from pathlib import Path
from matplotlib.image import imread
import json
path = Path("path_to/CID44971_spatial/slide1/")
library_id = "library_id"
adata.uns["spatial"] = {"library_id": {}}
files = dict(
tissue_positions_file = path / "tissue_positions_list.csv",
scalefactors_json_file = path / "scalefactors_json.json",
hires_image = path / "tissue_hires_image.png",
lowres_image = path / "tissue_lowres_image.png",
)
adata.uns["spatial"][library_id]["images"] = dict()
for res in ["hires", "lowres"]:
adata.uns["spatial"][library_id]["images"][res] = imread(
str(files[f"{res}_image"])
)
adata.uns["spatial"][library_id]["scalefactors"] = json.loads(
files["scalefactors_json_file"].read_bytes()
)
positions = pd.read_csv(
files["tissue_positions_file"],
header = None,
index_col = 0,
)
positions.columns = [
"in_tissue",
"array_row",
"array_col",
"pxl_col_in_fullres",
"pxl_row_in_fullres",
]
adata.obs = adata.obs.join(positions, how = "left")
adata.obsm["spatial"] = adata.obs[["x", "y"]].to_numpy()
adata.obs.drop(columns=["x", "y"], inplace = True)
# Liana
li.ut.spatial_neighbors(adata, bandwidth = 200, cutoff = 0.1, kernel = "gaussian", set_diag = True)
li.mt.lr_bivar(adata,
function_name = "cosine", # Name of the function
n_perms = 100, # Number of permutations to calculate a p-value
mask_negatives = False, # Whether to mask LowLow/NegativeNegative interactions
add_categories = True, # Whether to add local categories to the results
expr_prop = 0.2, # Minimum expr. proportion for ligands/receptors and their subunits
use_raw = False,
verbose = True)
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/liana/method/sp/_lr_bivar.py:86, in SpatialLR.__call__(self, adata, function_name, resource_name, resource, interactions, expr_prop, n_perms, mask_negatives, seed, add_categories, use_raw, layer, connectivity_key, inplace, key_added, obsm_added, lr_sep, verbose)
15 def __call__(self,
16 adata: AnnData,
17 function_name: str,
(...)
33 verbose: Optional[bool] = False,
34 ):
35 \"\"\"
36 Local ligand-receptor interaction metrics and global scores.
37
(...)
83 if `inplace=False`, returns a the above.
84 \"\"\"
---> 86 lr_res, local_scores = super().__call__(
87 mdata=adata,
88 function_name=function_name,
89 connectivity_key=connectivity_key,
90 resource_name=resource_name,
91 resource=resource,
92 interactions=interactions,
93 nz_threshold=expr_prop,
94 n_perms=n_perms,
95 mask_negatives=mask_negatives,
96 add_categories=add_categories,
97 x_mod=True,
98 y_mod=True,
99 x_use_raw=use_raw,
100 x_layer=layer,
101 seed=seed,
102 verbose=verbose,
103 xy_sep=lr_sep,
104 x_name='ligand',
105 y_name='receptor',
106 inplace=False,
107 complex_sep='_'
108 )
110 return self._handle_return(adata, lr_res, local_scores, key_added, obsm_added, inplace)
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/liana/method/sp/_SpatialBivariate.py:221, in SpatialBivariate.__call__(self, mdata, x_mod, y_mod, function_name, interactions, resource, resource_name, connectivity_key, mod_added, key_added, mask_negatives, add_categories, n_perms, seed, nz_threshold, x_use_raw, x_layer, x_transform, y_use_raw, y_layer, y_transform, x_name, y_name, complex_sep, xy_sep, remove_self_interactions, inplace, verbose)
218 assert_covered(entities, adata.var_names, verbose=verbose)
220 # Filter to only include the relevant features
--> 221 adata = adata[:, np.intersect1d(entities, adata.var.index)]
223 xy_stats = pd.DataFrame({'means': adata.X.mean(axis=0).A.flatten(),
224 'props': _get_props(adata.X)},
225 index=adata.var_names
226 ).reset_index().rename(columns={'index': 'gene'})
227 # join global stats to LRs from resource
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:1171, in AnnData.__getitem__(self, index)
1169 \"\"\"Returns a sliced view of the object.\"\"\"
1170 oidx, vidx = self._normalize_indices(index)
-> 1171 return AnnData(self, oidx=oidx, vidx=vidx, asview=True)
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:360, in AnnData.__init__(self, X, obs, var, uns, obsm, varm, layers, raw, dtype, shape, filename, filemode, asview, obsp, varp, oidx, vidx)
358 if not isinstance(X, AnnData):
359 raise ValueError(\"`X` has to be an AnnData object.\")
--> 360 self._init_as_view(X, oidx, vidx)
361 else:
362 self._init_as_actual(
363 X=X,
364 obs=obs,
(...)
376 filemode=filemode,
377 )
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:416, in AnnData._init_as_view(self, adata_ref, oidx, vidx)
414 # fix categories
415 uns = copy(adata_ref._uns)
--> 416 self._remove_unused_categories(adata_ref.obs, obs_sub, uns)
417 self._remove_unused_categories(adata_ref.var, var_sub, uns)
418 # set attributes
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:1181, in AnnData._remove_unused_categories(self, df_full, df_sub, uns)
1179 all_categories = df_full[k].cat.categories
1180 with pd.option_context(\"mode.chained_assignment\", None):
-> 1181 df_sub[k] = df_sub[k].cat.remove_unused_categories()
1182 # also correct the colors...
1183 color_key = f\"{k}_colors\"
File ~/miniconda3/envs/liana/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/liana/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/liana/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/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:1582, in AnnData.copy(self, filename)
1576 if not self.isbacked:
1577 if self.is_view and self._has_X():
1578 # TODO: How do I unambiguously check if this is a copy?
1579 # Subsetting this way means we don’t have to have a view type
1580 # defined for the matrix, which is needed for some of the
1581 # current distributed backend. Specifically Dask.
-> 1582 return self._mutated_copy(
1583 X=_subset(self._adata_ref.X, (self._oidx, self._vidx)).copy()
1584 )
1585 else:
1586 return self._mutated_copy()
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:1527, in AnnData._mutated_copy(self, **kwargs)
1525 elif self.raw is not None:
1526 new[\"raw\"] = self.raw.copy()
-> 1527 return AnnData(**new)
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:362, in AnnData.__init__(self, X, obs, var, uns, obsm, varm, layers, raw, dtype, shape, filename, filemode, asview, obsp, varp, oidx, vidx)
360 self._init_as_view(X, oidx, vidx)
361 else:
--> 362 self._init_as_actual(
363 X=X,
364 obs=obs,
365 var=var,
366 uns=uns,
367 obsm=obsm,
368 varm=varm,
369 raw=raw,
370 layers=layers,
371 dtype=dtype,
372 shape=shape,
373 obsp=obsp,
374 varp=varp,
375 filename=filename,
376 filemode=filemode,
377 )
File ~/miniconda3/envs/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:548, in AnnData._init_as_actual(self, X, obs, var, uns, obsm, varm, varp, obsp, raw, layers, dtype, shape, filename, filemode)
544 # annotations
545 self._obs = _gen_dataframe(
546 obs, [\"obs_names\", \"row_names\"], source=source, attr=\"obs\", length=n_obs
547 )
--> 548 self._var = _gen_dataframe(
549 var, [\"var_names\", \"col_names\"], source=source, attr=\"var\", length=n_vars
550 )
552 # now we can verify if indices match!
553 for attr_name, x_name, idx in x_indices:
File ~/miniconda3/envs/liana/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/liana/lib/python3.11/site-packages/anndata/_core/anndata.py:180, in _gen_dataframe_df(anno, index_names, source, attr, length)
170 @_gen_dataframe.register(pd.DataFrame)
171 def _gen_dataframe_df(
172 anno: pd.DataFrame,
(...)
177 length: int | None = None,
178 ):
179 if length is not None and length != len(anno):
--> 180 raise _mk_df_error(source, attr, length, len(anno))
181 anno = anno.copy(deep=False)
182 if not is_string_dtype(anno.index):
ValueError: Observations annot. `var` must have as many rows as `X` has columns (17353), but has 17506 rows."
}