TITLE: LIANA+_Integrating_MultiOmics_Data
AUTHORS: Daniel Dimitrov, Philipp Sven Lars Schäfer, Elias Farr, Pablo Rodriguez-Mier, Sebastian Lobentanzer, Pau Badia-i-Mompel, Aurelien Dugourd, Jovan Tanevski, Ricardo Omar Ramirez Flores, Julio Saez-Rodriguez

[DESCRIPTION]
A Protocol describing the application of LIANA+ on a spatially-resolved metabolite-transcriptome dataset from a recent murine Parkinson’s disease model Vicari et al., 2023. 

We demonstrate LIANA+’s utility in harmonizing spatially-resolved transcriptomics and MALDI-MSI data to unravel metabolite-mediated interactions and the molecular mechanisms of dopamine regulation in the 
striatum.

Two particular challenges with this data are: - The unaligned spatial locations of the two omics technologies and the untargeted nature of the MALDI-MSI data, which results in a large number of features with unknown
 identities. Only few of which were previously identified as specific metabolites.

Here, we show untargeted modelling of known and unknown metabolite peaks and their spatial relationships with transcriptomics data. Specifically, we use a multi-view modelling strategy MISTy to decipher global spatial relationships of metabolite peaks with cell types and brain-specific receptors. Then, we use LIANA+’s local metrics to pinpoint the subregions of interaction. We also show strategies to enable spatial multi-omics analysis from diverse omics technologies with unaligned locations and observations.

 This protocol is associated with our Nature Cell Biology paper describing LIANA+.

[BEFORE_START]
Install Python. We recommend using conda, miniconda, or mamba for this.

[STEPS]
SECTION: Install LIANA+
1. # In command line / or jupyter notebook
pip install liana[common]
pip install adjustText==0.8
SECTION: Import Required Packages
2. import numpy as np
import liana as li
import mudata as mu
import scanpy as sc
from matplotlib import pyplot as plt
from adjustText import adjust_text
SECTION: Import Required Packages
3. # save figure parameters
kwargs = {'frameon':False, 'size':1.5, 'img_key':'lowres'}
SECTION: Obtain and Examine the Data
4. # let's download the data
rna = sc.read("sma_rna.h5ad", backup_url="https://figshare.com/ndownloader/files/44624974?private_link=4744950f8768d5c8f68c")

msi = sc.read("sma_msi.h5ad", backup_url="https://figshare.com/ndownloader/files/44624971?private_link=4744950f8768d5c8f68c")

ct = sc.read("sma_ct.h5ad", backup_url="https://figshare.com/ndownloader/files/44624968?private_link=4744950f8768d5c8f68c")
5. # and create a MuData object
mdata = mu.MuData({'rna':rna, 'msi':msi, 'ct':ct})
# examine the object
mdata
6. # examine data
fig, axes = plt.subplots(1, 3, figsize=(12, 3))

sc.pl.spatial(rna, color='log1p_total_counts', ax=axes[0], **kwargs, show=False)
sc.pl.spatial(msi, color='Dopamine', cmap='magma', ax=axes[1], **kwargs, show=False)
sc.pl.spatial(ct, color='MSN1', cmap='viridis', ax=axes[2], **kwargs, show=False)

fig.subplots_adjust(wspace=0, hspace=0)
fig.tight_layout()
7. # examine experimental design
sc.pl.spatial(rna, color=['lesion', 'region'], **kwargs, wspace=0.25)
SECTION: Remove Features with little-to-no variation
8. sc.pp.highly_variable_genes(rna, flavor='cell_ranger', n_top_genes=5000)
sc.pp.highly_variable_genes(msi, flavor='cell_ranger', n_top_genes=150)
ct.var['cv'] = ct.X.A.var(axis=0) / ct.X.A.mean(axis=0)
ct.var['highly_variable'] = ct.var['cv'] > np.percentile(ct.var['cv'], 20)

msi = msi[:, msi.var['highly_variable']]
rna = rna[:, rna.var['highly_variable']]
ct = ct[:, ct.var['highly_variable']]

# scale msi intensities
sc.pp.scale(msi, max_value=5)
SECTION: Obtain brain-specific metabolite-receptor interactions
9. metalinks = li.rs.get_metalinks(tissue_location='Brain',
                                biospecimen_location='Cerebrospinal Fluid (CSF)',
                                source=['CellPhoneDB', 'NeuronChat']
                                )
metalinks.head()
SECTION: Obtain brain-specific metabolite-receptor interactions
10. # Obtain Human to Murine gene symbols Map
map_df = li.rs.get_hcop_orthologs(columns=['human_symbol', 'mouse_symbol'],
                                  min_evidence=3
                                  ).rename(columns={'human_symbol':'source',
                                                   'mouse_symbol':'target'})
SECTION: Obtain brain-specific metabolite-receptor interactions
11. metalinks = li.rs.translate_column(resource=metalinks,
                                   map_df=map_df,
                                   column='gene_symbol',
                                   one_to_many=1)
metalinks.head()
SECTION: Compute Spatial Proximies for the Multi-view Model
13. # We use the metabolite modality as a reference to which we align the other modalities.
# We use the `spatial_neighbors` function to compute the spatial proximity from cell types and brain-specific receptors to the metabolite intensities.

plot, _ = li.ut.query_bandwidth(coordinates=rna.obsm['spatial'], start=0, end=1500)
plot
SECTION: Obtain brain-specific metabolite-receptor interactions
12. # Intersect the RNA modality with the receptors
receptors = np.intersect1d(metalinks['gene_symbol'].unique(), rna.var_names)
rec = rna[:, receptors].copy()
SECTION: Compute Spatial Proximies for the Multi-view Model
14. # choose bandwidth
bandwidth = 500
cutoff = 0.1
# distances of metabolties to RNA
reference = mdata.mod["msi"].obsm["spatial"]
SECTION: Compute Spatial Proximies for the Multi-view Model
15. # Compute proximities to the reference for each modality 
li.ut.spatial_neighbors(ct, bandwidth=bandwidth, cutoff=cutoff, spatial_key="spatial", reference=reference, set_diag=False, standardize=False)
li.ut.spatial_neighbors(rec, bandwidth=bandwidth, cutoff=cutoff, spatial_key="spatial", reference=reference, set_diag=False, standardize=False)
SECTION: Construct and Run the Multi-view model
16. # MISTy
mdata.update_obs()
misty = li.mt.MistyData({"intra": msi, "receptor": rec, "ct": ct}, enforce_obs=False, obs=mdata.obs)
misty
SECTION: Construct and Run the Multi-view model
17. # Learn the relationships between intra and the extra views
misty(model=li.mt.sp.LinearModel, verbose=True, bypass_intra=True, maskby='lesion')
SECTION: Examine Results
18. li.pl.target_metrics(misty, stat='multi_R2', return_fig=True, top_n=20, filter_fun=lambda x: x['intra_group']=='intact')
SECTION: Examine Results
19. interactions = misty.uns['interactions']

interactions = interactions[(interactions['intra_group'] == 'intact') & (interactions['target'] == 'Dopamine')]
# Create scatter plot
plt.figure(figsize=(5, 4))
# rank rank by abs importances
interactions['rank'] = interactions['importances'].rank(ascending=False)
plt.scatter(interactions['rank'], interactions['importances'], s=11,
            c=interactions['view'].map({'ct': '#008B8B', 'receptor': '#a11838'}))
            
# add for top 10
top_n = interactions[interactions['rank'] <= 10]
texts = []
for i, row in top_n.iterrows():
    texts.append(plt.text(row['rank'], row['importances'], row['predictor'], fontsize=10))
adjust_text(texts, arrowprops=dict(arrowstyle="->", color='grey', lw=1.5))
plt.tight_layout()
SECTION: Identifying Local Interactions
20. # Focusing on Dopamine, we can next use LIANA+'s local metrics to identify the subregions of interactions with MSN1/2 cells.
# to do so, we need to interpolate one modality to the other, such that they are on the same coordinate system, while this is done internally for the multi-view learning, we need to interpolate it as:
metabs = li.ut.interpolate_adata(target=msi, reference=rna, use_raw=False, spatial_key='spatial')

SECTION: Identifying Local Interactions
21. # let's rebuild this with the update modalities:
mdata = mu.MuData({'msi': metabs, 'rna':rna, 'deconv':ct}, obsm=rna.obsm, obs=rna.obs, uns=rna.uns)
SECTION: Identifying Local Interactions
22. # re-calculate neighbours
li.ut.spatial_neighbors(mdata, bandwidth=bandwidth, cutoff=cutoff, set_diag=True)
SECTION: Identifying Local Interactions
23. # define interactions of interest
interactions = metalinks[['metabolite', 'gene_symbol']].apply(tuple, axis=1).tolist()
SECTION: Identifying Local Interactions
24. # Let's calculate the local metrics for the Dopamine intensities with and Drd1/2 receptors.

li.mt.bivariate(mdata, 
                local_name='cosine',
                x_mod='msi', 
                y_mod='rna',
                key_added='lr',
                x_use_raw=False, 
                y_use_raw=False,
                verbose=True, 
                mask_negatives=True, 
                n_perms=1000,
                interactions=interactions,
                x_transform=sc.pp.scale,
                y_transform=sc.pp.scale,
            )
SECTION: Plot Bivariate Similarity between Dopamine and Drd1/2 receptors
25. sc.pl.spatial(mdata.mod['lr'], 
              color=['Dopamine^Drd1', 'Dopamine^Drd2'],
              cmap='cividis_r', vmax=1, layer='pvals',
              **kwargs)