Tutorial for Mechanical Cell-Cell Communication#

import mechanochat as mc
import scanpy as sc
from matplotlib import pyplot as plt
import numpy as np
/home/kxy/miniforge3/envs/mc_test/lib/python3.9/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Load Data#

imputed seqFISH data from mouse embryo

Steps for preparing the dataset: https://github.com/XiangyuKuang/mechanochat-tutorials/blob/main/seqfish/prepare_dataset.md

adata = sc.read_h5ad('../Mouse_Embryo_seqFISH_imputed.h5ad')
adata = adata[adata.obs['z']=='2'].copy()
cluster_key = 'celltype_mapped_refined'
# remove low quality cells
I = adata.obs[cluster_key] == 'Low quality'
adata = adata[~I].copy() 
sc.pp.filter_cells(adata,min_genes=100)
sc.pp.filter_genes(adata,min_cells=3)
sc.pp.pca(adata)

crop data

x = adata.obsm['spatial']
cid = (x[:,0]>=1540) & (x[:,0]<(1540+57)) & (x[:,1]>=1418) & (x[:,1]<(1418+57))
adata_crop = adata[cid].copy()

# remove cell types that only have one cells
group_counts = adata_crop.obs[cluster_key].value_counts()
small_groups = group_counts[group_counts == 1].index.tolist()# 2. 找出只含 1 个细胞的分组
I = ~adata_crop.obs[cluster_key].isin(small_groups)# 3. 过滤掉这些分组对应的细胞
adata_crop = adata_crop[I].copy()
del adata

MechanoChat Analysis#

load mechanochat db

df_stiffness, df_adh, df_IonTF, df_lr = mc.pp.load_mechanochat_db('mouse')
df_stiffness = mc.pp.filter_db(df_stiffness,adata_crop,filter_criteria='min_cell',min_cell=1)
df_IonTF = mc.pp.filter_db(df_IonTF,adata_crop,filter_criteria='min_cell',min_cell=1, alias_col='Alias')
df_adh = mc.pp.filter_lr_database(df_adh, adata_crop,filter_criteria='min_cell',min_cell=1)
df_lr = mc.pp.filter_lr_database(df_lr, adata_crop,filter_criteria='min_cell',min_cell=1)
print('stiffness gene db',df_stiffness.shape[0],)
print('adhesion gene db',df_adh.shape[0])
print('IonTF db',df_IonTF.shape[0])
filter_db: symbol not in adata.var_names, using alias instead: Mrtfa->Mkl1, Mrtfb->Mkl2
stiffness gene db 45
adhesion gene db 55
IonTF db 13

SEM simulation#

sem = mc.SEM2(
    adata=adata_crop,
    cluster_key = cluster_key,
    sim_name='seqfish',
)
sem.get_param(
    df_adh=df_adh, 
    df_stiffness=df_stiffness
)
sem.sim_gpu()
# sem.save_sim()
add .beta .alpha_csr .param_info
Simulation:   0%|          | 0/1000 [00:00<?, ?it/s]/home/kxy/miniforge3/envs/mc_test/lib/python3.9/site-packages/numba/cuda/dispatcher.py:536: NumbaPerformanceWarning: Grid size 56 will likely result in GPU under-utilization due to low occupancy.
  warn(NumbaPerformanceWarning(msg))
Simulation: 100%|██████████| 1000/1000 [00:02<00:00, 354.17it/s]

cell-cell contact and force

sem.compute_contact()
sem.compute_force()
add .obsp['contacts'], .uns['contacts'] to adata
add .pairs
add .f_pairs
add .f_cc
add .obsp['f_cc'] to adata
Computing force by chunk: 100%|██████████| 1/1 [00:00<00:00,  6.11it/s]

cell shape and mechanical interaction (fig 3b, c)#

fig, ax = plt.subplots(figsize=(12,12))
mc.pl.alphashape_plot(sem,ax=ax,show_axis=False,)
Computing alpha-shape with parameters: alpha=None, ns=10, r=1.5
Processing Cell Shapes:   0%|          | 0/1030 [00:00<?, ?it/s]
Processing Cell Shapes: 100%|██████████| 1030/1030 [00:04<00:00, 244.79it/s]
<Axes: >
../../_images/32173ae22af74ddcd38b321439af0aa04da3bb2332a61b5c7980d8623197b014.png

fig3b

fx_csr, fy_csr = sem.f_pairs_tocsr()
ctri = mc.tl.contact_analysis(sem)
ctri.compute_contact_force()
create ctri for sem seqfish
add ctri.cell_boundary
add .contact_midedge_dict
add .contact_f_dict
add .junction_midedge_dict
add .junction_f_dict
fig,ax = plt.subplots(figsize=(12,12))
mc.pl.alphashape_plot(sem,ax=ax,boundary_width=0.5,boundary_color='gray',)

norm = plt.Normalize(vmin=0.05 ,vmax = 0.8)
mc.pl.contact_plot(
    ctri,
    vis_key = 'mechanical interaction',
    line_width = 2.5,
    norm = norm,
    mean_edge_value = False,
    ax=ax
)

mc.pl.draw_colorbar(norm=norm,cmap_name = 'inferno',orientation='vertical',label = 'Mechanical Interaction')
<Axes: ylabel='Mechanical Interaction'>
../../_images/07d20059438c655242f9a6c46f4fb8d415ad4dbd2f5c9f50bc07c1c8da27ceac.png ../../_images/98ab0e7afb7331f2b1d62c1ff3987a8e9053950fc83859c97702394ce9e36ffb.png

fig3c

# cid_list = [733,746,747,457]
cid_list  = [ 830, 829, 841, 821, 817, 839, 840]

fig,ax = plt.subplots(figsize=(12,12))
mc.pl.alphashape_plot(
    sem,ax=ax,boundary_width=0.5,
    face_alpha=0.5,
    enable_legend=False,
    cid_list = cid_list,
)
mc.pl.element_plot(sem,ax=ax,cid_list=cid_list,enable_legend=False,spot_size=100)
mc.pl.contact_plot(
    ctri,
    vis_key = 'mechanical interaction',
    mean_edge_value=False,
    line_width=8,
    cid_list=cid_list,
    norm = norm,
    ax=ax
)
mc.pl.forceij_plot(sem,cid_i=cid_list,fx_csr=fx_csr,fy_csr=fy_csr,ax=ax,
    fmin = 0.2,
    scale=14,
    linewidths=1,
    edgecolor = 'w'
)
<Axes: >
../../_images/c3420c28664e08ce9ee1bf895ad82dff52d889d7f0b60c62d1a6bb2a7274d852.png

Mechanical cell-cell communication analysis (fig 3d, e)#

mc.tl.mechanical_communication(sem, df_IonTF, df_lr)
print(adata_crop)
apply log1p to cell-cell mechanical interaction
add .obsm['sender_mechano_signal'], .obsm['receiver_mechano_signal'], .uns['mechano_signal_info']
AnnData object with n_obs × n_vars = 1030 × 20914
    obs: 'celltype_mapped_refined', 'z', 'n_genes'
    var: 'n_cells'
    uns: 'celltype_mapped_refined_colors', 'pca', 'contacts', 'mechano_signal_info'
    obsm: 'spatial', 'X_pca', 'sender_mechano_signal', 'receiver_mechano_signal'
    varm: 'PCs'
    obsp: 'contacts', 'f_cc', 'mechano_Piezo1', 'mechano_Piezo2', 'mechano_Kcnk2', 'mechano_Kcnk10', 'mechano_Tmem63a', 'mechano_Tmem63b', 'mechano_Tmem87a', 'mechano_Trpv4', 'mechano_Yap1', 'mechano_Mkl1', 'mechano_Mkl2', 'mechano_Ctnnb1', 'mechano_Srf', 'mechano_Cdh1-Itga2_Itgb1', 'mechano_Cdh1-Itgae_Itgb7', 'mechano_Cspg4-Itga3_Itgb1', 'mechano_Gp1ba-Itgam_Itgb2', 'mechano_Icam1-Itgax_Itgb2', 'mechano_Icam1-Itgal_Itgb2', 'mechano_Icam2-Itgal_Itgb2', 'mechano_Icam1-Itgam_Itgb2', 'mechano_Icam2-Itgam_Itgb2', 'mechano_Icam1-Itgam_Itgb2l', 'mechano_Icam2-Itgam_Itgb2l', 'mechano_Icam1-Itgal', 'mechano_Jam2-Itga3_Itgb1', 'mechano_Jam2-Itgav_Itgb1', 'mechano_Jam3-Itgam_Itgb2', 'mechano_F11r-Itgal_Itgb2', 'mechano_Jam3-Itgam_Itgb2l', 'mechano_L1cam-Itgav_Itgb3', 'mechano_L1cam-Itga4_Itgb7', 'mechano_Madcam1-Itga4_Itgb7', 'mechano_Dll1-Notch1', 'mechano_Dll1-Notch3', 'mechano_Dll1-Notch4', 'mechano_Dll3-Notch1', 'mechano_Dll3-Notch2', 'mechano_Dll3-Notch3', 'mechano_Dll3-Notch4', 'mechano_Dll4-Notch1', 'mechano_Dll4-Notch2', 'mechano_Dll4-Notch3', 'mechano_Dll4-Notch4', 'mechano_Jag1-Notch1', 'mechano_Jag1-Notch2', 'mechano_Jag1-Notch3', 'mechano_Jag1-Notch4', 'mechano_Jag2-Notch1', 'mechano_Jag2-Notch2', 'mechano_Jag2-Notch3', 'mechano_Jag2-Notch4', 'mechano_Dll1-Notch2', 'mechano_Sema7a-Itgb1_Itga1', 'mechano_Thy1-Itgam_Itgb2', 'mechano_Thy1-Itgax_Itgb2', 'mechano_Thy1-Itgav_Itgb3'

fig 3d receiver signal and signal tensor#

fig, ax = plt.subplots(figsize=(12,12))
sig_key = 'Srf'
ax = mc.pl.alphashape_plot(
    sem,
    ax=ax,
    vis_key = f'r-{sig_key}',
    summary = 'receiver_mechano_signal',
    boundary_color='gray',
    boundary_width=0.5,
    cmap_name= 'YlGn',
    show_axis=False,
)

ax=mc.pl.signal_direction_plot(
    sem,signal=f'mechano_{sig_key}',
    ax=ax,
    quiver_length=0.7,
    width_bin_n = 6,
    width_scale = [0.6,2],
    th = lambda x: np.percentile(x,60),
)
sig_mat data th 0.39048896431922914
../../_images/a6909692b0c2db6771766ba090ed9a54e4412adf5dc99c901b2aae2c58f3382a.png
sig_key = 'Srf'
Nx = 12
a = 4.5
xc = sem.xc*sem.scale+sem.deltax
xc2grid_map, grid_center, x_grid, y_grid = mc.tl.cell2grid(xc,Nx)
sig_mat = adata_crop.obsp[f'mechano_{sig_key}']
Xgrid, Vegrid, Sgrid = mc.tl.signal_tensor(xc, xc2grid_map, grid_center, sig_mat)
Vegrid = Vegrid/Vegrid.max()*a
arr = adata_crop.obsm['receiver_mechano_signal'][f'r-{sig_key}']
vmax = np.percentile(arr,99)
vmin = np.percentile(arr,1)

fig, ax = plt.subplots(figsize=(12,12))
ax = mc.pl.alphashape_plot(
    sem,
    ax=ax,
    vis_key = f'r-{sig_key}',
    summary = 'receiver_mechano_signal',
    boundary_color='gray',
    boundary_width=0.5,
    cmap_name= 'YlGn',
    vmax = vmax,
    vmin = vmin,
)
mc.pl.plot_grid_signal_tensor(Xgrid, Vegrid,
    scale = 150,
    pivot = 'tail',
    headwidth = 2.5,
    headlength = 3.2,
    headaxislength = 3,
    width = 0.004,
    color = 'k',
    scale_units = 'width',
    ax=ax
)
<Axes: >
../../_images/d53a5ab65607ed7cf58418c5bdeb957c348eee6398278bcd203e0d2de12eab9b.png

fig 3e spatial-domain-level communicaion#

spatial domain

import random
random.seed(0)
np.random.seed(0)

f_cc_th = sem.adata.obsp['f_cc'].copy()
th = np.percentile(f_cc_th.data,10)
I = f_cc_th.data < th
f_cc_th.data[I] = 0
f_cc_th.eliminate_zeros()
sem.adata.obsp['f_cc_th'] = f_cc_th
sc.tl.leiden(sem.adata,resolution=0.3,obsp = 'f_cc_th',flavor='igraph',n_iterations=2)#
if 'leiden_colors' in sem.adata.uns_keys():
    del sem.adata.uns['leiden_colors']
fig,ax = plt.subplots()
sc.pl.spatial(sem.adata,color = 'leiden',spot_size=1,ax=ax,show=False)
mc.pl.vis_contact_signal(sem,sig_mat=f_cc_th,ax=ax,line_width=0.1)
<Axes: title={'center': 'leiden'}, xlabel='spatial1', ylabel='spatial2'>
../../_images/6f88eb437fd2a6e4d6c830594edef54dea62a384c28bfa43ef75c091f8a6cbe1.png
sem.adata.obs[f'{cluster_key}_mc'] = (sem.adata.obs[cluster_key].astype(str) + '_' + sem.adata.obs['leiden'].astype(str)).astype('category')
subgroup_counts = sem.adata.obs[f'{cluster_key}_mc'].value_counts()
small_groups = subgroup_counts[subgroup_counts < 10].index
sem.adata.obs.loc[sem.adata.obs[f'{cluster_key}_mc'].isin(small_groups), f'{cluster_key}_mc'] = np.nan
sem.adata.obs[f'{cluster_key}_mc'] = sem.adata.obs[f'{cluster_key}_mc'].cat.remove_unused_categories()

spatial-domain-level communicaion

mc.tl.cluster_communication(
    adata = sem.adata,
    cluster_key = f'{cluster_key}_mc',
    signal = 'Srf',
    n_permutations=1000,
)
df_qvalue = sem.adata.uns[f'{cluster_key}_mc_mechano_Srf']['adjusted_communication_pvalue']

visualization

# outline clusters
cmap = dict(zip(sem.adata.obs[cluster_key].cat.categories,sem.adata.uns[f'{cluster_key}_colors']))
fig, ax = plt.subplots(figsize=(12,12))
for ct1 in list(sem.adata.obs[f'{cluster_key}_mc'].cat.categories):
    I = sem.adata.obs[f'{cluster_key}_mc']  == ct1
    ct = sem.adata.obs[cluster_key][I].unique()[0]
    cid_list = np.where(I)[0]
    ar = np.zeros_like(cid_list,dtype=float)
    for n,i in enumerate(cid_list):
        ar[n] = sem.alphashape[i].alpha

    mc.pl.highlight_cell(sem,
        cid_list,
        cmap[ct],
        ar = ar.max()*1.5,
        r = 0.5*sem.scale,
        lw=3,
        ax=ax,
        face_alpha=0.5,
        edge_alpha=1,
        zorder = 1,
    )

mc.pl.alphashape_plot(sem,ax=ax,face_alpha=0.05,boundary_width=0.1,enable_legend=False)
mc.pl.spatial_domain_comm_plot(sem,df_qvalue,cluster_key,ax=ax)
<Axes: >
../../_images/717831ef5531fc39bc4a066340a5262d90eb89dd7d026acf0c5b77c11fc11290.png

save adata

adata_crop.write_h5ad(f'{sem.sim_name}.h5ad')