Skip to contents

Before you start

This vignette is an example of how to combine LIANA’s hypotheses with those produced by NicheNet. The biological results that are obtained here are highly dependent on the hypothesis in mind, which is described in NicheNet’s original article. Before you start, we strongly recommend to have a look to the NicheNet’s repository, as the data and the analyses that are run here were extracted from its vignettes. We acknowledge NicheNet’s authors for the extensive and reproducible documentation that they provide in this repository.

Background

LIANA (LIgand-receptor ANalysis frAmework) is a framework able to prioritize ligand-receptor interactions from single-cell transcriptomics using different resources and methods. It allows users to systematically generate hypotheses about which ligands from a given cell type is binding receptors on another. In contrast to LIANA, NicheNet aims to deepen in the intra-cellular mechanisms that connect a ligand with a set of transcriptional targets, making an extensive usage of prior knowledge from multiple sources. LIANA and NicheNet are not mutually exclusive, but in certain scenarios could be rather complementary, given that they aim to explore different aspects of inter- and intra-cellular communication.

Because of this, in this vignette, we show how to use LIANA in combination with NicheNet using the data and the biological scenario described in NicheNet’s vignette. Briefly, the biological question here is: Which ligands expressed by cancer-associated fibroblasts (CAFs) can induce a specific gene program in neighboring malignant cells? (Using data from Puram et al. 2017).

Load packages and data

We first install NicheNet and load required libraries

if(!require('ggpubr')) install.packages('ggpubr', quiet = TRUE, repos = "http://cran.us.r-project.org")
if(!require('ggrepel')) install.packages('ggrepel', quiet = TRUE, repos = "http://cran.us.r-project.org")
if(!require('cowplot')) install.packages('cowplot', quiet = TRUE, repos = "http://cran.us.r-project.org")
if(!require('remotes')) install.packages('remotes', quiet = TRUE, repos = "http://cran.us.r-project.org")
if(!require('nichenetr')) remotes::install_github("saeyslab/nichenetr", quiet = TRUE)
library(tidyverse)
library(liana)
library(nichenetr)
library(Seurat)
library(ggrepel)
library(cowplot)
options(timeout=600) # required to download expression data /w slow connection

Then, we load and prepare the single-cell data, NicheNet’s model weights, and the gene set of interest. The latest is composed by genes that are known to participate in the partial epithelial-mesenchymal transition (p-EMT) program, as defined in NicheNet’s vignette.

# single-cell expression matrix described in Puram et al. 2017
hnscc_expression <- readRDS(url("https://zenodo.org/record/3260758/files/hnscc_expression.rds"))
# model weights
ligand_target_matrix <- readRDS(url("https://zenodo.org/record/3260758/files/ligand_target_matrix.rds"))

Furthermore, we restrict the single-cell data to the two cell types of interest for this example, which are the cancer associated fibroblasts (CAFs) and the tumor cells.

expression <- hnscc_expression$expression
sample_info <- hnscc_expression$sample_info
colnames(sample_info) <- make.names(colnames(sample_info))

# filter samples based on vignette's information and add cell type
tumors_remove <-  c("HN10", "HN", "HN12", "HN13", "HN24", "HN7", "HN8", "HN23")
sample_info <- sample_info %>%
  subset( !(tumor %in% tumors_remove) & Lymph.node == 0) %>%
  # fix some cell type identity names
  mutate(cell_type = ifelse(classified..as.cancer.cell == 1, "Tumor", non.cancer.cell.type)) %>%
  subset(cell_type %in% c("Tumor", "CAF"))

# cell ID as rownames
rownames(sample_info) <- sample_info$cell

# subset expression to selected cells
expression <- expression[sample_info$cell, ]

# gene set of interest
geneset_oi <- read_tsv(url("https://zenodo.org/record/3260758/files/pemt_signature.txt"), col_types = cols(), col_names = "gene") %>%
  pull(gene) %>%
  .[. %in% rownames(ligand_target_matrix)]

Run LIANA

In the first step, we run LIANA to systematically score all the ligand-receptor interactions between all the cell types included in the dataset. To do so, we first need to create a Seurat object from data:

# create seurat object
seurat_object <- Seurat::CreateAssayObject(counts = expm1(t(expression))) %>%
  Seurat::CreateSeuratObject(., meta.data = sample_info) %>%
  Seurat::NormalizeData()

# set cell identity to cell type
Idents(seurat_object) <- seurat_object@meta.data$cell_type

And then we can execute LIANA using default parameters. After LIANA execution, we employ the function liana_aggregate() to summarize the output of different methods and to obtain a single score for each interaction.

liana_results <- liana_wrap(seurat_object) %>%
  liana_aggregate()

By default, LIANA will score the ligand-receptor interactions in all the possible directions within the two cell types of interest. This includes: Autocrine signaling (e.g. CAFs -> CAFs), CAFs -> Tumor cells and Tumor cells -> CAFs. As we are only interested in the CAFs -> Tumor cell direction, we filter the results and visualize the top 50 interactions according to the consensus/aggregate rank across methods. The aggregate rank itself can be interpreted as the significance of preferential enrichment for the interactions.

# filter results to cell types of interest
caf_tumor_results <- liana_results %>%
  subset(source == "CAF" & target == "Tumor") %>%
  dplyr::rename(ligand=ligand.complex, receptor=receptor.complex)

# filter results to top N interactions
n <- 50
top_n_caf_tumor <- caf_tumor_results %>%
  arrange(aggregate_rank) %>%
  slice_head(n = n) %>%
  mutate(id = fct_inorder(paste0(ligand, " -> ", receptor)))

# visualize median rank
top_n_caf_tumor %>%
  ggplot(aes(y = aggregate_rank, x = id)) +
  geom_bar(stat = "identity") +
  xlab("Interaction") + ylab("LIANA's aggregate rank") +
  theme_cowplot() +
  theme(axis.text.x = element_text(size = 8, angle = 60, hjust = 1, vjust = 1))

Run NicheNet using LIANA’s ligands

The key aspect of combining LIANA with NicheNet is that we can use the ligands prioritized by LIANA as the set of potential ligands for NicheNet. Instead of evaluating all the expressed ligands for which the receptor is also expressed in the receiver cell type, we will only explore those that were prioritized by the methods included in LIANA. Hence, we select the ligands that form the interactions previously shown.

# get ligands and filter to those included in NicheNet's ligand-target matrix
ligands <- unique(top_n_caf_tumor$ligand)
ligands <- ligands[ligands %in% colnames(ligand_target_matrix)]
ligands
##  [1] "COL1A1" "FN1"    "THBS2"  "CXCL12" "COL4A1" "THBS1"  "THBS4"  "LAMB1" 
##  [9] "IGF1"   "LAMA2"  "COMP"   "LAMB2"

Before running NicheNet, we also need to define a list of background genes. To do so, we employ the threshold defined in NicheNet’s vignette.

background_genes <- expression[sample_info$cell[sample_info$cell_type == "Tumor"], ] %>%
  apply(2,function(x){10*(2**x - 1)}) %>%
  apply(2,function(x){log2(mean(x) + 1)}) %>%
  .[. >= 4] %>%
  names()

And execute NicheNet to predict the ligand activities using the pEMT gene set previously mentioned

nichenet_activities <- predict_ligand_activities(
  geneset = geneset_oi,
  background_expressed_genes = background_genes,
  ligand_target_matrix = ligand_target_matrix, potential_ligands = ligands
)

As a result, we obtain the NicheNet’s activity predictions for the ligands previously prioritized using LIANA. In a final step, we will visualize the ligand-receptor scores of LIANA and the ligand activity score of NicheNet in a single figure.

# prepare data for visualization
vis_liana_nichenet <- top_n_caf_tumor %>%
  inner_join(nichenet_activities, by = c("ligand" = "test_ligand")) %>%
  arrange(pearson) %>%
  mutate(ligand = fct_inorder(ligand))

# prepare NicheNet figure
nichenet_scores_plot <- vis_liana_nichenet %>%
  group_by(ligand) %>%
  summarize(pearson = mean(pearson)) %>%
  ggplot(aes(y = ligand, x = pearson)) +
  geom_bar(stat = "identity") +
  ggtitle("NicheNet") +
  xlab("Pearson's score") +
  theme_cowplot() +
  theme(axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        axis.title.y = element_blank(),
        axis.line.y = element_line(color = "white"),
        plot.title = element_text(hjust = 0.5),
        axis.text.x = element_text(angle = 60, hjust = 1, vjust = 1))

# prepare LIANA figure
liana_receptor_heatmap <- vis_liana_nichenet %>%
  ggplot(aes(y = ligand, x = receptor, fill = aggregate_rank)) +
  geom_tile() +
  theme_cowplot() +
  ggtitle("LIANA") +
  ylab("Ligand") + xlab("Receptor") +
  theme(axis.text.x = element_text(angle = 60, hjust = 1, vjust = 1),
        plot.title = element_text(hjust = 0.5),
        panel.grid.major = element_line(colour = "gray", linetype = 2),
        legend.position = "left")

# combine plots
plot_grid(liana_receptor_heatmap, nichenet_scores_plot,
          align = "h", nrow = 1, rel_widths = c(0.8,0.3))

Conclusion

In this vignette, we exemplify how to use LIANA’s predictions as NicheNet’s input. Although both methods are complementary, there is one point that should not be forgotten: LIANA predicts ligand-receptor interaction pairs. However, NicheNet score for a given ligand comes from how likely is to reach a set of given targets from it. A ligand can have a great Pearson correlation score to regulate a given set of targets, but we do not actually know if it is mediated by the receptor that we predicted using LIANA. Given this, the combination of methods to predict cell-cell communication (like LIANA) with tools that are able to model intracellular signaling using prior knowledge (e.g. NicheNet) constitute a promising approach to deepen in the signaling mechanisms implicated in the biological process under study.

R Session information

## R version 4.1.2 (2021-11-01)
## Platform: x86_64-conda-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.5 LTS
## 
## Matrix products: default
## BLAS/LAPACK: /home/dbdimitrov/anaconda3/envs/liana4.1/lib/libopenblasp-r0.3.18.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=de_DE.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=de_DE.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=de_DE.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] sp_1.5-0           SeuratObject_4.1.0 Seurat_4.1.1       liana_0.1.12      
##  [5] forcats_0.5.1      stringr_1.4.0      dplyr_1.0.9        purrr_0.3.4       
##  [9] readr_2.1.2        tidyr_1.2.0        tibble_3.1.8       tidyverse_1.3.2   
## [13] nichenetr_1.1.1    remotes_2.4.2      cowplot_1.1.1      ggrepel_0.9.1     
## [17] ggpubr_0.4.0       ggplot2_3.3.6      BiocStyle_2.22.0  
## 
## loaded via a namespace (and not attached):
##   [1] rsvd_1.0.5                  Hmisc_4.7-0                
##   [3] ica_1.0-3                   class_7.3-20               
##   [5] foreach_1.5.2               lmtest_0.9-40              
##   [7] rprojroot_2.0.3             crayon_1.5.1               
##   [9] spatstat.core_2.4-4         MASS_7.3-58                
##  [11] nlme_3.1-158                backports_1.4.1            
##  [13] reprex_2.0.1                basilisk_1.9.12            
##  [15] rlang_1.0.6                 XVector_0.34.0             
##  [17] caret_6.0-92                ROCR_1.0-11                
##  [19] readxl_1.4.0                irlba_2.3.5                
##  [21] limma_3.50.3                scater_1.22.0              
##  [23] filelock_1.0.2              BiocParallel_1.28.3        
##  [25] rjson_0.2.21                bit64_4.0.5                
##  [27] glue_1.6.2                  sctransform_0.3.3          
##  [29] vipor_0.4.5                 parallel_4.1.2             
##  [31] spatstat.sparse_3.0-0       BiocGenerics_0.40.0        
##  [33] spatstat.geom_3.0-3         haven_2.5.0                
##  [35] tidyselect_1.2.0            SummarizedExperiment_1.24.0
##  [37] fitdistrplus_1.1-8          zoo_1.8-10                 
##  [39] xtable_1.8-4                magrittr_2.0.3             
##  [41] evaluate_0.15               scuttle_1.4.0              
##  [43] cli_3.6.0                   zlibbioc_1.40.0            
##  [45] rstudioapi_0.13             miniUI_0.1.1.1             
##  [47] bslib_0.4.0                 logger_0.2.2               
##  [49] rpart_4.1.16                shiny_1.7.2                
##  [51] BiocSingular_1.10.0         xfun_0.31                  
##  [53] clue_0.3-61                 cluster_2.1.3              
##  [55] caTools_1.18.2              listenv_0.8.0              
##  [57] png_0.1-7                   future_1.27.0              
##  [59] ipred_0.9-13                withr_2.5.0                
##  [61] bitops_1.0-7                plyr_1.8.7                 
##  [63] cellranger_1.1.0            hardhat_1.2.0              
##  [65] e1071_1.7-11                dqrng_0.3.0                
##  [67] pROC_1.18.0                 pillar_1.8.0               
##  [69] GlobalOptions_0.1.2         cachem_1.0.6               
##  [71] fs_1.5.2                    GetoptLong_1.0.5           
##  [73] DelayedMatrixStats_1.16.0   vctrs_0.4.1                
##  [75] ellipsis_0.3.2              generics_0.1.3             
##  [77] lava_1.6.10                 tools_4.1.2                
##  [79] foreign_0.8-82              beeswarm_0.4.0             
##  [81] munsell_0.5.0               proxy_0.4-27               
##  [83] DelayedArray_0.20.0         fastmap_1.1.0              
##  [85] compiler_4.1.2              abind_1.4-5                
##  [87] httpuv_1.6.5                plotly_4.10.0              
##  [89] rgeos_0.5-9                 GenomeInfoDbData_1.2.7     
##  [91] prodlim_2019.11.13          gridExtra_2.3              
##  [93] edgeR_3.36.0                lattice_0.20-45            
##  [95] dir.expiry_1.2.0            deldir_1.0-6               
##  [97] visNetwork_2.1.0            utf8_1.2.2                 
##  [99] later_1.3.0                 recipes_1.0.1              
## [101] jsonlite_1.8.0              scales_1.2.0               
## [103] ScaledMatrix_1.2.0          pbapply_1.5-0              
## [105] carData_3.0-5               sparseMatrixStats_1.6.0    
## [107] lazyeval_0.2.2              promises_1.2.0.1           
## [109] car_3.1-0                   doParallel_1.0.17          
## [111] latticeExtra_0.6-30         goftest_1.2-3              
## [113] spatstat.utils_3.0-1        reticulate_1.25            
## [115] checkmate_2.1.0             rmarkdown_2.14             
## [117] pkgdown_2.0.6               textshaping_0.3.6          
## [119] statmod_1.4.36              Rtsne_0.16                 
## [121] Biobase_2.54.0              uwot_0.1.11                
## [123] igraph_1.3.0                survival_3.3-1             
## [125] yaml_2.3.5                  systemfonts_1.0.4          
## [127] htmltools_0.5.3             memoise_2.0.1              
## [129] locfit_1.5-9.6              IRanges_2.28.0             
## [131] viridisLite_0.4.0           digest_0.6.29              
## [133] assertthat_0.2.1            mime_0.12                  
## [135] rappdirs_0.3.3              future.apply_1.9.0         
## [137] data.table_1.14.2           S4Vectors_0.32.4           
## [139] ragg_1.2.2                  DiagrammeR_1.0.9           
## [141] labeling_0.4.2              splines_4.1.2              
## [143] Formula_1.2-4               googledrive_2.0.0          
## [145] RCurl_1.98-1.7              broom_1.0.0                
## [147] hms_1.1.1                   modelr_0.1.8               
## [149] colorspace_2.0-3            base64enc_0.1-3            
## [151] BiocManager_1.30.18         ggbeeswarm_0.6.0           
## [153] GenomicRanges_1.46.1        shape_1.4.6                
## [155] nnet_7.3-17                 sass_0.4.2                 
## [157] Rcpp_1.0.8.3                bookdown_0.27              
## [159] RANN_2.6.1                  circlize_0.4.15            
## [161] fansi_1.0.3                 tzdb_0.3.0                 
## [163] parallelly_1.32.1           ModelMetrics_1.2.2.2       
## [165] R6_2.5.1                    grid_4.1.2                 
## [167] ggridges_0.5.3              lifecycle_1.0.3            
## [169] bluster_1.4.0               curl_4.3.2                 
## [171] ggsignif_0.6.3              googlesheets4_1.0.0        
## [173] leiden_0.4.2                jquerylib_0.1.4            
## [175] Matrix_1.5-3                desc_1.4.1                 
## [177] RcppAnnoy_0.0.19            RColorBrewer_1.1-3         
## [179] iterators_1.0.14            gower_1.0.0                
## [181] htmlwidgets_1.5.4           beachmat_2.10.0            
## [183] polyclip_1.10-0             rvest_1.0.2                
## [185] ComplexHeatmap_2.10.0       mgcv_1.8-40                
## [187] globals_0.15.1              htmlTable_2.4.1            
## [189] patchwork_1.1.1             spatstat.random_3.0-1      
## [191] progressr_0.10.1            codetools_0.2-18           
## [193] matrixStats_0.62.0          lubridate_1.8.0            
## [195] metapod_1.2.0               randomForest_4.7-1.1       
## [197] prettyunits_1.1.1           SingleCellExperiment_1.16.0
## [199] dbplyr_2.2.1                basilisk.utils_1.9.4       
## [201] GenomeInfoDb_1.30.1         gtable_0.3.0               
## [203] DBI_1.1.3                   stats4_4.1.2               
## [205] highr_0.9                   tensor_1.5                 
## [207] httr_1.4.3                  KernSmooth_2.23-20         
## [209] stringi_1.7.6               vroom_1.5.7                
## [211] progress_1.2.2              farver_2.1.1               
## [213] reshape2_1.4.4              viridis_0.6.2              
## [215] fdrtool_1.2.17              timeDate_4021.104          
## [217] xml2_1.3.3                  BiocNeighbors_1.12.0       
## [219] OmnipathR_3.7.2             interp_1.1-3               
## [221] scattermore_0.8             bit_4.0.4                  
## [223] scran_1.22.1                jpeg_0.1-9                 
## [225] MatrixGenerics_1.6.0        spatstat.data_3.0-0        
## [227] pkgconfig_2.0.3             gargle_1.2.0               
## [229] rstatix_0.7.0               knitr_1.39