diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a9c4eb38..8a2805a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,18 @@ and this project adheres to [Semantic Versioning][]. ## [Unreleased] -### Addition +### Additions - Isotypically included B cells are now labelled as `receptor_subtype="IGH+IGK/L"` instead of `ambiguous` in `tl.chain_qc`. ([#537](https://github.com/scverse/scirpy/pull/537)) -- Added the `normalized_hamming` metric to `pp.ir_dist` that accounts for differences in CDR3 sequence length. Additionally, - the hamming distance was reimplemented with numba, achieving a significant speedup ([#512](https://github.com/scverse/scirpy/pull/512)). +- Added the `normalized_hamming` metric to `pp.ir_dist` that accounts for differences in CDR3 sequence length ([#512](https://github.com/scverse/scirpy/pull/512)). + +### Performance improvements + +- The hamming distance was reimplemented with numba, achieving a significant speedup ([#512](https://github.com/scverse/scirpy/pull/512)). + +### Fixes + +- Fix that pl.clonotype_network couldn't use non-standard obsm key ([#545](https://github.com/scverse/scirpy/pull/545)). ### Other changes diff --git a/src/scirpy/pl/_clonotypes.py b/src/scirpy/pl/_clonotypes.py index 732ecec9f..da493921c 100644 --- a/src/scirpy/pl/_clonotypes.py +++ b/src/scirpy/pl/_clonotypes.py @@ -197,7 +197,7 @@ def clonotype_network( pass clonotype_res = params.adata.uns[clonotype_key] - coords, adj_mat = _graph_from_coordinates(params.adata, clonotype_key) + coords, adj_mat = _graph_from_coordinates(params.adata, clonotype_key, basis) nx_graph = nx.Graph(_distance_to_connectivity(adj_mat)) # in 2.6 networkx added functionality to draw self-loops. We don't want # them plotted, so we remove them here diff --git a/src/scirpy/tests/conftest.py b/src/scirpy/tests/conftest.py index 127b7ffe5..df31e0533 100644 --- a/src/scirpy/tests/conftest.py +++ b/src/scirpy/tests/conftest.py @@ -196,12 +196,16 @@ def adata_define_clonotype_clusters_singletons(): @pytest.fixture -def adata_clonotype_network(adata_conn): +def adata_clonotype_network(adata_conn, request): """Adata with clonotype network computed. adata derived from adata_conn that also contains some gene expression data for plotting. """ + try: + kwargs = request.param + except AttributeError: + kwargs = {} if isinstance(adata_conn, AnnData): adata = AnnData( var=pd.DataFrame().assign(gene_symbol=["CD8A", "CD4"]).set_index("gene_symbol"), @@ -216,7 +220,7 @@ def adata_clonotype_network(adata_conn): obsm=adata_conn.obsm, ) adata.obs["continuous"] = [3, 4, 0, 0, 7, 14, 1, 0, 2, 2, 0] - ir.tl.clonotype_network(adata, sequence="aa", metric="alignment") + ir.tl.clonotype_network(adata, sequence="aa", metric="alignment", **kwargs) return adata else: adata_gex = AnnData( @@ -231,7 +235,7 @@ def adata_clonotype_network(adata_conn): ) mdata = MuData({"gex": adata_gex, "airr": adata_conn.mod["airr"]}) mdata.obs["continuous"] = [3, 4, 0, 0, 7, 14, 1, 0, 2, 2, 0] - ir.tl.clonotype_network(mdata, sequence="aa", metric="alignment") + ir.tl.clonotype_network(mdata, sequence="aa", metric="alignment", **kwargs) return mdata diff --git a/src/scirpy/tests/test_plotting.py b/src/scirpy/tests/test_plotting.py index 9a0630bbb..8b98e87f7 100644 --- a/src/scirpy/tests/test_plotting.py +++ b/src/scirpy/tests/test_plotting.py @@ -115,18 +115,18 @@ def test_clonotype_modularity(adata_clonotype_modularity, jitter, show_size_lege ) +@pytest.mark.parametrize( + "adata_clonotype_network,kwargs", + [[{}, {}], [{"key_added": "foo"}, {"basis": "foo"}]], + indirect=["adata_clonotype_network"], +) @pytest.mark.parametrize("color_by_n_cells", [True, False]) @pytest.mark.parametrize("scale_by_n_cells", [True, False]) @pytest.mark.parametrize("show_size_legend", [True, False]) @pytest.mark.parametrize("show_legend", [True, False]) @pytest.mark.parametrize("show_labels", [True, False]) def test_clonotype_network( - adata_clonotype_network, - color_by_n_cells, - scale_by_n_cells, - show_size_legend, - show_legend, - show_labels, + adata_clonotype_network, color_by_n_cells, scale_by_n_cells, show_size_legend, show_legend, show_labels, kwargs ): adata = adata_clonotype_network p = pl.clonotype_network( @@ -136,6 +136,7 @@ def test_clonotype_network( show_size_legend=show_size_legend, show_legend=show_legend, show_labels=show_labels, + **kwargs, ) assert isinstance(p, plt.Axes) diff --git a/src/scirpy/tl/_clonotypes.py b/src/scirpy/tl/_clonotypes.py index 6bcb13a6d..ef3c97ff5 100644 --- a/src/scirpy/tl/_clonotypes.py +++ b/src/scirpy/tl/_clonotypes.py @@ -579,7 +579,7 @@ def clonotype_network( return coord_df -def _graph_from_coordinates(adata: AnnData, clonotype_key: str) -> tuple[pd.DataFrame, sp.csr_matrix]: +def _graph_from_coordinates(adata: AnnData, clonotype_key: str, basis: str) -> tuple[pd.DataFrame, sp.csr_matrix]: """ Given an AnnData object on which `tl.clonotype_network` was ran, and the corresponding `clonotype_key`, extract a data-frame @@ -600,7 +600,7 @@ def _graph_from_coordinates(adata: AnnData, clonotype_key: str) -> tuple[pd.Data # Retrieve coordinates and reduce them to one coordinate per node coords = ( - cast(pd.DataFrame, adata.obsm["X_clonotype_network"]) + cast(pd.DataFrame, adata.obsm[f"X_{basis}"]) .dropna(axis=0, how="any") .join(dist_idx_lookup) .join(clonotype_label_lookup) @@ -653,7 +653,7 @@ def clonotype_network_igraph( raise KeyError(f"{basis} not found in `adata.uns`. Did you run `tl.clonotype_network`?") from None if f"X_{basis}" not in params.adata.obsm_keys(): raise KeyError(f"X_{basis} not found in `adata.obsm`. Did you run `tl.clonotype_network`?") - coords, adj_mat = _graph_from_coordinates(params.adata, clonotype_key) + coords, adj_mat = _graph_from_coordinates(params.adata, clonotype_key, basis) graph = igraph_from_sparse_matrix(adj_mat, matrix_type="distance") # flip y axis to be consistent with networkx