#' @title Infer Cell-Cell Communication
#' @description Use this function to identify ligand/receptor pairs expressed by cell clusters in human, mouse or zebrafish single cell data. A CellChat object is generated which can be used to visualize these connections using bb_cellchat_heatmap or other tools from package CellChat.
#' @param cds The cell data set object. It should usually be pre-filtered to conatin a single biological sample.
#' @param group_var The cell metadata column identifying cell groups for cell-cell communication inference.
#' @param n_cores Number of cores for the analysis, Default: 12
#' @param species Species for the assay, Default: c("human", "mouse", "zebrafish")
#' @param min_cells Cell clusters smaller than this value will be ignored., Default: 10
#' @param prob_type Methods for computing the average gene expression per cell group. By default = "triMean", producing fewer but stronger interactions; When setting ‘type = "truncatedMean"', a value should be assigned to ’trim', producing more interactions, Default: c("triMean", "truncatedMean", "median")
#' @param prob_trim the fraction (0 to 0.25) of observations to be trimmed from each end of x before the mean is computed if using truncatedMean, Default: NULL
#' @param project Whether or not to smooth gene expression, Default: TRUE
#' @param pop_size_arg Whether consider the proportion of cells in each group across all sequenced cells. Set population.size = FALSE if analyzing sorting-enriched single cells, to remove the potential artifact of population size. Set population.size = TRUE if analyzing unsorted single-cell transcriptomes, with the reason that abundant cell populations tend to send collectively stronger signals than the rare cell populations., Default: TRUE
#' @return A CellChat object
#' @details see github::sqjin/CellChat
#' @seealso
#' \code{\link[monocle3]{normalized_counts}}
#' \code{\link[dplyr]{mutate-joins}},\code{\link[dplyr]{pull}}
#' \code{\link[tibble]{tibble}}
#' \code{\link[CellChat]{createCellChat}},\code{\link[CellChat]{CellChatDB.human}},\code{\link[CellChat]{CellChatDB.mouse}},\code{\link[CellChat]{character(0)}},\code{\link[CellChat]{subsetData}},\code{\link[CellChat]{identifyOverExpressedGenes}},\code{\link[CellChat]{identifyOverExpressedInteractions}},\code{\link[CellChat]{projectData}},\code{\link[CellChat]{computeCommunProb}},\code{\link[CellChat]{filterCommunication}},\code{\link[CellChat]{computeCommunProbPathway}},\code{\link[CellChat]{aggregateNet}}
#' \code{\link[SingleCellExperiment]{character(0)}}
#' \code{\link[future]{plan}}
#' @rdname bb_cellchat
#' @export
#' @importFrom monocle3 normalized_counts
#' @importFrom dplyr left_join pull
#' @importFrom tibble tibble
#' @import CellChat
#' @importFrom SingleCellExperiment colData
#' @importFrom future plan
bb_cellchat <-
function(cds,
group_var,
n_cores = 12,
species = c("human", "mouse", "zebrafish"),
min_cells = 10,
prob_type = c("triMean", "truncatedMean", "median"),
prob_trim = NULL,
project = TRUE,
pop_size_arg = TRUE,
ask = TRUE) {
# print a warning and ask whether to continue
continue <- TRUE
if (ask) {
message("This function takes a long time to run and may exceed the memory available on your computer.\nYou should save your work and your workspace.")
continue <- askYesNo(msg = "Do you wish to continue?")
}
if (continue) {
species <- match.arg(species)
prob_type <- match.arg(prob_type)
# get the expression data
cellchat_data <- monocle3::normalized_counts(cds)
if (class(cellchat_data) != "dgCMatrix") {
cellchat_data <- as(cellchat_data, "dgCMatrix")
}
row.names(cellchat_data) <-
dplyr::left_join(tibble::tibble(feature_id = row.names(cellchat_data)),
bb_rowmeta(cds)) |>
dplyr::pull(gene_short_name)
# set up the cellchat object
cellchat <- CellChat::createCellChat(
object = cellchat_data,
meta = droplevels(as.data.frame(SingleCellExperiment::colData(cds))),
group.by = group_var
)
# Set the ligand-receptor interaction database
if (species == "human") {
CellChatDB <-
CellChat::CellChatDB.human
} else if (species == "mouse") {
CellChatDB <-
CellChat::CellChatDB.mouse
} else if (species == "zebrafish") {
CellChatDB <-
CellChat::CellChatDB.zebrafish
} else {
stop("The species must be one of human, mouse or zebrafish.")
}
# use all CellChatDB for cell-cell communication analysis
CellChatDB.use <- CellChatDB
# set the used database in the object
cellchat@DB <- CellChatDB.use
# subset the expression data of signaling genes for saving computation cost
cellchat <-
CellChat::subsetData(cellchat) # This step is necessary even if using the whole database
# future::plan("multiprocess", workers = n_cores) # do parallel
future::plan("multisession", workers = n_cores) # do parallel
cellchat <- CellChat::identifyOverExpressedGenes(cellchat)
cellchat <-
CellChat::identifyOverExpressedInteractions(cellchat)
# project gene expression data onto PPI network (optional)
if (project) {
if (species == "human") {
cellchat <- CellChat::projectData(cellchat, PPI.human)
} else if (species == "mouse") {
cellchat <- CellChat::projectData(cellchat, PPI.human)
} else {
stop("Can only project if the species is human or mouse.")
}
}
# Compute the communication probability and infer cellular communication network
cellchat <-
CellChat::computeCommunProb(cellchat, population.size = pop_size_arg, type = prob_type)
# Filter out the cell-cell communication if there are only few number of cells in certain cell groups
cellchat <-
CellChat::filterCommunication(cellchat, min.cells = min_cells)
# Infer the cell-cell communication at a signaling pathway level
cellchat <- CellChat::computeCommunProbPathway(cellchat)
# Calculate the aggregated cell-cell communication network
cellchat <- CellChat::aggregateNet(cellchat)
future::plan("sequential")
return(cellchat)
} else {
message("Stopping bb_cellchat.")
}
}
#' @title Make a Complex Heatmap From a CellChat Object With Sensible Defaults
#' @description This will generate heatmap from a CellChat object using ComplexHeatmap::Heatmap. Options are provided to filter for sender and receiver cells, to generate simple marginal annotations and for aesthetic control.
#' @param object The CellChat object to plot
#' @param source_filter Optional filter for source cell clusters from the object metadata. Accepts a single string or vector of cell groups., Default: NULL
#' @param target_filter Optional filter for target cell clusters from the object metadata. Accepts a single string or vector of cell groups., Default: NULL
#' @param interaction_filter Optional filter to include only certain interactions in the figure.
#' @param interaction_threshold Optional filter to only include interactions above a certain threshold.
#' @param colors Color scale endpoints, Default: c("transparent", "red3")
#' @param rowanno Options for simple row annotation; must be one of c(NULL, "Annotation", "Pathway")
#' @param rowanno_colors Optional colors to replace the poor color selections from Complex heatmap. Must be supplied as a named list with one element each for "Annotation" and "Pathway". Not required if not showing these annotations. The list should be of the form: list(Annotation = c("name1" = "color value1", "name2" = "color_value2")), Default: NULL
#' @param colanno Options for simple column annotation; must be one of c(NULL, "Source", "Target")
#' @param colanno_colors See rowanno_colors, Default: NULL
#' @param pval_filter Filter for significance of associations. CellChat returns pvalues of 0, 0.01, and 0.05; this function will filter and retain values less than or equal to the provided value. Default: 0.05
#' @param heatmap_name Name for the main color scale of the heatmap, Default: 'InteractionScore'
#' @param heatmap_show_row_dend Show row dendrograms? Default: TRUE
#' @param heatmap_row_dend_width Width of row dendrograms Default: unit(5, "mm")
#' @param heatmap_show_column_dend Show column dendrograms?' Default: TRUE
#' @param heatmap_column_dend_height Height of column dendrograms. Default: unit(5, "mm")
#' @param heatmap_row_names_gp Row name graphical params, Default: gpar(fontsize = 10)
#' @param heatmap_column_names_gp Column name graphical params, Default: gpar(fontsize = 10)
#' @param heatmap_column_names_rot Column name rotation, Default: 90
#' @param heatmap_column_title Column title, Default: NULL
#' @param heatmap_column_title_gp Column title graphical params, Default: gpar(fontsize = 12, fontface = "bold")
#' @param col_anno_name_gp Column annotation name graphical params, Default: gpar(fonmtsize = 10, fontface = "bold")
#' @param row_anno_name_gp Row annotation name graphical params, Default: gpar(fontsize = 10, fontface = "bold")
#' @param return_value Return a heatmap plot or a matrix.
#' @return a heatmap as a grid object; plot using cowplot::plot_grid
#' @details see github::sqjin/CellChat
#' @seealso
#' \code{\link[CellChat]{subsetCommunication}}
#' \code{\link[tibble]{as_tibble}},\code{\link[tibble]{c("tibble", "tibble")}},\code{\link[tibble]{rownames}}
#' \code{\link[dplyr]{filter}},\code{\link[dplyr]{mutate}},\code{\link[dplyr]{select}},\code{\link[dplyr]{mutate-joins}},\code{\link[dplyr]{group_by}},\code{\link[dplyr]{summarise}}
#' \code{\link[tidyr]{pivot_wider}}
#' \code{\link[ComplexHeatmap]{rowAnnotation}},\code{\link[ComplexHeatmap]{columnAnnotation}},\code{\link[ComplexHeatmap]{draw-dispatch}},\code{\link[ComplexHeatmap]{Heatmap}}
#' \code{\link[circlize]{colorRamp2}}
#' \code{\link[grid]{grid.grab}}
#' @rdname bb_cellchat_heatmap
#' @export
#' @importFrom CellChat subsetCommunication
#' @importFrom tibble as_tibble tibble column_to_rownames
#' @importFrom dplyr filter mutate select left_join group_by summarise any_of
#' @importFrom tidyr pivot_wider
#' @importFrom MatrixGenerics rowMaxs
#' @importFrom ComplexHeatmap rowAnnotation columnAnnotation draw Heatmap
#' @importFrom circlize colorRamp2
#' @importFrom grid grid.grabExpr
bb_cellchat_heatmap <- function(object,
source_filter = NULL,
target_filter = NULL,
interaction_filter = NULL,
interaction_threshold = 0,
colors = c("transparent", "red3"),
rowanno = c(NULL, "Annotation", "Pathway"),
rowanno_colors = NULL,
colanno = c(NULL, "Source", "Target"),
colanno_colors = NULL,
pval_filter = 0.05,
heatmap_name = "Interaction\nScore",
heatmap_show_row_dend = TRUE,
heatmap_row_dend_width = unit(5, "mm"),
heatmap_show_column_dend = TRUE,
heatmap_column_dend_height = unit(5, "mm"),
heatmap_row_names_gp = gpar(fontsize = 10),
heatmap_column_names_gp = gpar(fontsize = 10),
heatmap_column_names_rot = 90,
heatmap_column_title = NULL,
heatmap_column_title_gp = gpar(fontsize = 12, fontface = "bold"),
col_anno_name_gp = gpar(fontsize = 10, fontface = "bold"),
row_anno_name_gp = gpar(fontsize = 10, fontface = "bold"),
return_value = c("heatmap", "plot","matrix")) {
stopifnot("The object must be of class CellChat." = "CellChat" %in% class(object))
stopifnot("You must select valid colors." = all(areColors(colors)))
stopifnot("You can only select 2 colors." = length(colors) == 2)
stopifnot(
"You can only annotate rows by c(NULL, 'Annotation', 'Pathway')" = rowanno %in% c(NULL, "Annotation", "Pathway")
)
stopifnot(
"You can only annotate columns by c(NULL, 'Source', 'Target')" = colanno %in% c(NULL, "Source", "Target")
)
return_value <- match.arg(return_value)
mtx <- CellChat::subsetCommunication(object = object) |>
tibble::as_tibble() |>
dplyr::filter(pval <= pval_filter)
if (!is.null(source_filter))
mtx <- mtx |> dplyr::filter(source %in% source_filter)
if (!is.null(target_filter))
mtx <- mtx |> dplyr::filter(target %in% target_filter)
mtx <- mtx |>
dplyr::mutate(source_target = paste0(source, " -> ", target)) |>
dplyr::select(source_target, interaction_name, prob) |>
tidyr::pivot_wider(
names_from = interaction_name,
values_from = prob,
values_fill = 0,
names_repair = "universal"
) |>
bb_tbl_to_matrix() |>
t()
if (!is.null(interaction_filter)) {
mtx <- mtx[,interaction_filter]
}
mtx <- mtx[MatrixGenerics::rowMaxs(mtx) > interaction_threshold,]
if (return_value == "matrix") {
return(mtx)
} else {
if (!is.null(rowanno)) {
row_anno_df <- tibble::tibble(rownames = rownames(mtx)) |>
dplyr::left_join(
CellChat::subsetCommunication(object) |>
dplyr::group_by(
rownames = interaction_name_2,
Annotation = annotation,
Pathway = pathway_name
) |>
dplyr::summarise() |>
dplyr::ungroup() |>
dplyr::select(dplyr::any_of(c(
"rownames", rowanno
)))
)
row_anno_df <- row_anno_df |>
tibble::column_to_rownames("rownames") |>
as.data.frame()
rowanno <-
ComplexHeatmap::rowAnnotation(df = row_anno_df,
col = rowanno_colors,
annotation_name_gp = row_anno_name_gp)
}
if (!is.null(colanno)) {
col_anno_df <- tibble::tibble(colnames = colnames(mtx)) |>
dplyr::left_join(
CellChat::subsetCommunication(object) |>
dplyr::mutate(source_target = paste0(source, " -> ", target)) |>
dplyr::group_by(
colnames = source_target,
Source = source,
Target = target
) |>
dplyr::summarise() |>
dplyr::ungroup() |>
dplyr::select(dplyr::any_of(c(
"colnames", colanno
)))
)
col_anno_df <- col_anno_df |>
tibble::column_to_rownames("colnames") |>
as.data.frame()
colanno <-
ComplexHeatmap::columnAnnotation(df = col_anno_df,
col = colanno_colors,
annotation_name_gp = col_anno_name_gp)
}
col_fun <-
circlize::colorRamp2(breaks = c(min(mtx), max(mtx)),
colors = colors)
grid::grid.grabExpr(
ComplexHeatmap::draw(
ComplexHeatmap::Heatmap(
mtx,
name = heatmap_name,
col = col_fun,
left_annotation = rowanno,
top_annotation = colanno,
show_row_dend = heatmap_show_row_dend,
row_dend_width = heatmap_row_dend_width,
show_column_dend = heatmap_show_column_dend,
column_dend_height = heatmap_column_dend_height,
row_names_gp = heatmap_row_names_gp,
column_names_gp = heatmap_column_names_gp,
column_names_rot = heatmap_column_names_rot,
column_title = heatmap_column_title,
column_title_gp = heatmap_column_title_gp
),
heatmap_legend_side = "right",
annotation_legend_side = "bottom",
legend_grouping = "original"
),
wrap = FALSE
)
}
}
areColors <- function(x) {
sapply(x, function(X) {
tryCatch(is.matrix(col2rgb(X)),
error = function(e) FALSE)
})
}
areColors <- function(x) {
sapply(x, function(X) {
tryCatch(is.matrix(col2rgb(X)),
error = function(e) FALSE)
})
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.