Archetype Crosstalk Network

Archetype Crosstalk Network#

Adapted from [AMG+23]

Packages#

To run this notebook, install ParTIpy with the tutorial extras: pip install partipy[extra].

import scanpy as sc
import liana as li

import partipy as pt
from partipy.datasets import load_fibroblast_data
from partipy.crosstalk import get_specific_genes_per_archetype, get_archetype_crosstalk, plot_weighted_network

Data#

Here we use the data from [MGenoveL+20].

adata = load_fibroblast_data(verbose=True, )
Loading processed data from data/fibroblast_muhl_processed.h5ad
adata = load_fibroblast_data()
sc.pp.normalize_total(adata)
sc.pp.log1p(adata)
sc.pp.highly_variable_genes(adata)
sc.pp.pca(adata, mask_var="highly_variable", n_comps=50)
adata.layers["z_scaled"]= sc.pp.scale(adata.X, max_value=10, copy=True) # save this for later
sc.pl.pca_scatter(adata, color=["Bmp7", "Wnt2"])
../_images/3c927f5600e1afc24f1b14c1a58b5670bc552514f1d7ca21c9d646582db99c32.png

Based on the PCA scree plot, we will be using 8 PC dimensions to fit the archetypes.

sc.pl.pca_variance_ratio(adata, n_pcs=50)
pt.set_obsm(adata=adata, obsm_key="X_pca", n_dimensions=8)
../_images/f7b6dee481729aa0c5a5383214b5abe430fd6f40757bd4eb639c63260defc723.png
pt.compute_selection_metrics(adata=adata, n_archetypes_list=range(2, 10))
pt.plot_var_explained(adata).show()
pt.plot_IC(adata).show()
pt.compute_bootstrap_variance(adata, n_bootstrap=30, n_archetypes_list=range(2, 10))
pt.plot_bootstrap_variance(adata)
pt.plot_bootstrap_2D(adata, result_filters={"n_archetypes": 6})

Here, we will use 5 archetypes. When fitting 4 archetypes we have observed that running archetypal analysis on bootstrapped data shows that one archetype is quite unstable, whereas using 5 worked much better.

pt.plot_archetypes_2D(adata=adata, show_contours=True, result_filters={"n_archetypes": 6})

Determine which genes are characteristic for each archetype

pt.compute_archetype_weights(adata=adata, mode="automatic", result_filters={"n_archetypes": 6})
pseudobulk = pt.compute_archetype_expression(adata=adata, layer="z_scaled", result_filters={"n_archetypes": 6})

arch_idx = 2
adata.obs[f"weights_archetype_{arch_idx}"] = pt.get_aa_cell_weights(adata, n_archetypes=6)[:, arch_idx]
pt.plot_archetypes_2D(adata=adata, color=f"weights_archetype_{arch_idx}", result_filters={"n_archetypes": 6})
Applied length scale is 12.34.
../_images/2300f06e55e8b1bcd83212a52b463bbdf26aed657351f86efae6ae445bbc8743.png
specific_genes_per_archetype = get_specific_genes_per_archetype(archetype_expression=pseudobulk, min_score=0.05)

[(arch_id, len(arch_genes)) for arch_id, arch_genes in specific_genes_per_archetype.items()]
[(0, 489), (1, 911), (2, 2064), (3, 1793), (4, 6307), (5, 6174)]

Based on the genes we infer the functions of each archetype

for i in range(len(specific_genes_per_archetype)):
    print(i, specific_genes_per_archetype[i].head(25)["gene"].to_list())
0 ['Serping1', 'Bcl6', 'Selenop', 'C1qtnf3', 'Gm16559', 'Ms4a4d', 'Cxcl12', 'Edil3', 'Itm2b', 'Gm15635', 'Mfap4', 'Gm16263', 'Rarres2', 'Gask1b', 'Zfp808', 'Zmynd15', 'Lrrc61', 'Sfrp1', 'Hpse2', 'Fbln1', 'C1rb', 'Sod3', 'Icosl', 'Serpina3n', 'Rax']
1 ['Lmod1', 'Cnn1', 'Actg2', 'Pdgfc', 'Cdh6', 'Itga8', 'Asb2', 'Myocd', 'Kcnmb1', 'Acta1', 'Trpc6', 'Tnfrsf19', 'Slc2a4', 'Hspb7', 'Myh11', 'Tspan2', 'Gm16000', 'Pde6h', 'Tagln', 'Ctxn1', 'Ptk2b', 'Chrdl2', 'Pcp4l1', 'Cacna1h', 'E030013I19Rik']
2 ['Cd38', '1700071M16Rik', 'Nrg1', 'Lef1', 'Lpar3', 'Trpa1', 'Lef1os1', 'Ptprr', 'Pla2g7', 'Rab3b', 'Lama5', 'Cdhr17', 'Ano2', 'Tbx3', 'Glp2r', 'Cd300e', 'C5ar1', 'Ednrb', 'Sema4d', 'Abcc4', 'Col6a4', 'Paqr5', 'Eva1a', 'Insc', 'Plau']
3 ['Efnb2', 'Gm42721', 'Pde1c', 'Gm31258', '1700082M22Rik', 'Gm42785', 'Gm37515', 'Ugcg', 'Gm4117', 'Gm37954', 'Bmp3', 'Gm42513', 'Ryr3', 'Gm43577', 'Gm44153', 'Pkib', 'Kcnq1ot1', 'Gm37086', 'Macrod2os1', 'Ksr2', 'Gm37315', 'Gm42635', 'Mir6240', 'Ddx17', 'Gm17131']
4 ['Sox18', 'Mmrn2', 'Robo4', 'Ushbp1', 'Tie1', 'Plvap', 'Flt1', 'Emcn', 'Kdr', 'Egfl7', 'Podxl', 'Cd300lg', 'Dll4', 'Depp1', 'Fabp4', 'Adgrl4', 'Gata2', 'Apold1', 'Mmp3', 'Gimap6', 'Ramp3', 'Sox17', 'Fam110d', 'Gpnmb', 'Car4']
5 ['Efhd1os', 'Efhd1', 'Gm16083', '1700019D03Rik', 'Sema3c', 'Npy1r', 'Adgrg2', 'Cmah', 'Ebf1', 'Tnfrsf11b', 'Cd55', 'Pcolce2', 'Mfap5', 'Adamts5', 'Lrrn4cl', 'Anxa3', 'Tek', 'Nhsl1', 'Ackr4', 'Pi16', 'Fbn1', 'Flrt2', 'Creb5', 'Cadm3', 'Gm12043']
archetype_functions = {
    0: "ECM-remodeling fibroblasts",
    1: "Vascular-associated fibroblasts",
    2: "Neuronal/epithelial-interacting fibroblasts",
    3: "Myofibroblasts",
    4: "Immune-modulatory fibroblasts"
}

Finally we use LIANA+ to infer ligand and receptor are enriched between archetypes.

mouse_resource = li.rs.select_resource("mouseconsensus")
mouse_resource
ligand receptor
31371 Dll1 Notch1
31372 Dll1 Notch2
31373 Dll1 Notch4
31374 Dll1 Notch3
31375 Nrg2 Erbb2_Erbb3
... ... ...
35355 Serpina1a Lrp1
35356 Serpina1b Lrp1
35357 Serpina1c Lrp1
35358 Serpina1d Lrp1
35359 Serpina1e Lrp1

3989 rows × 2 columns

archetype_crosstalk = get_archetype_crosstalk(archetype_genes=specific_genes_per_archetype, 
                                              lr_resource=mouse_resource)
for sender_arch in archetype_crosstalk.keys():
    for receiever_arch in archetype_crosstalk[sender_arch].keys():
        print(f"{sender_arch} -> {receiever_arch}: {len(archetype_crosstalk[sender_arch][receiever_arch])}")
    break
0 -> 0: 3
0 -> 1: 7
0 -> 2: 14
0 -> 3: 3
0 -> 4: 15
0 -> 5: 23
plot_weighted_network(
    specific_genes_per_archetype=specific_genes_per_archetype,
    archetype_crosstalk_dict=archetype_crosstalk,
    layout="shell", 
    seed=42,
    )
../_images/5a3b3f4d0c123f240ff11798e9c38bc5a6c6c1546ba38576f21aa3110c5f9bf7.png

Session Info#

import session_info2
print(session_info2.session_info(dependencies=True))
pandas	2.2.3
plotnine	0.14.5
scanpy	1.11.2
liana	1.5.1
partipy	0.0.5 (0.0.1)
anndata	0.11.4
----	----
narwhals	1.41.0
pillow	11.2.1
statsmodels	0.14.4
ipython	9.2.0
Deprecated	1.2.18
pydantic	2.12.0
annotated-types	0.7.0
networkx	3.5
numpy	2.2.6
numcodecs	0.15.1
six	1.17.0
pyparsing	3.2.3
cycler	0.12.1
requests-cache	1.2.1
colorama	0.4.6
dask	2024.11.2
executing	2.2.0
llvmlite	0.44.0
traitlets	5.14.3
requests	2.32.3
decorator	5.2.1
mudata	0.3.2
asttokens	3.0.0
xarray	2024.11.0
jupyter_client	8.6.3
typing_extensions	4.15.0
h5py	3.13.0
packaging	25.0
PyYAML	6.0.2
matplotlib-inline	0.1.7
mizani	0.13.5
legacy-api-wrap	1.4.1
crc32c	2.7.1
tqdm	4.67.1
typing-inspection	0.4.2
pydantic_core	2.41.1
tornado	6.5.1
importlib_metadata	8.7.0
idna	3.10
coverage	7.10.7
prompt_toolkit	3.0.51
wcwidth	0.2.13
numba	0.61.2
jedi	0.19.2
ipywidgets	8.1.7
future	1.0.0
cattrs	24.1.3
debugpy	1.8.14
asciitree	0.3.3
kiwisolver	1.4.8
pyzmq	26.4.0
pytz	2025.2
zarr	2.18.7
zipp	3.22.0
stack-data	0.6.3
Pygments	2.19.1
cloudpickle	3.1.1
pybiomart	0.2.0
psutil	7.0.0
docrep	0.3.2
scipy	1.15.2
plotly	6.1.2
platformdirs	4.3.8
jupyter_core	5.8.1
python-dateutil	2.9.0.post0
setuptools	80.8.0
certifi	2025.4.26 (2025.04.26)
comm	0.2.2
ipykernel	6.29.5
url-normalize	2.2.1
joblib	1.5.1
natsort	8.4.0
charset-normalizer	3.4.2
threadpoolctl	3.6.0
patsy	1.0.1
session-info2	0.1.2
pyarrow	20.0.0
parso	0.8.4
pure_eval	0.2.3
matplotlib	3.10.3
urllib3	2.4.0
scikit-learn	1.6.1
appnope	0.1.4
toolz	1.0.0
wrapt	1.17.2
attrs	25.3.0
----	----
Python	3.11.0 | packaged by conda-forge | (main, Jan 14 2023, 12:26:40) [Clang 14.0.6 ]
OS	macOS-15.7.1-arm64-arm-64bit
Updated	2025-11-11 09:12