diff --git a/.Rbuildignore b/.Rbuildignore index 9c32498..97dbc85 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -13,3 +13,4 @@ ^.+\.tar\.gz$ ^\.github\/.+$ ^conda\/.+ +^Makefile$ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1aa9101..1d9a243 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -52,9 +52,12 @@ jobs: cache-version: 2 extra-packages: | any::devtools, any::pkgdown, any::plotthis, any::ComplexHeatmap, + any::Seurat, any::concaveman, any::gridGraphics, any::ggVennDiagram, any::clustree, any::ggwordcloud, any::ggalluvial, any::ggpubr, any::ggforce, any::ggraph, any::ggridges, any::hexbin, any::igraph, - any::scattermore + any::scattermore, any::ggupset, any::iNEXT, any::metap + + - uses: r-lib/actions/check-r-package@v2 - name: Install scplotter run: | diff --git a/.gitignore b/.gitignore index aa3872e..56f2b38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /test.ipynb docs/ +*.Rcheck/ +/*.tar.gz diff --git a/DESCRIPTION b/DESCRIPTION index 2845be1..7eabb0f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,10 +1,10 @@ Package: scplotter Title: Publication Quality Plots for Single Cell Data Analysis -Version: 0.0.2 +Version: 0.1.0 Authors@R: person("Panwen", "Wang", , "pwwang@pwwang.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-4614-8970")) -Description: Publication quality plots for single cell data analysis +Description: Publication quality plots for single cell data analysis. License: GPL (>= 3) Encoding: UTF-8 Roxygen: list(markdown = TRUE) @@ -13,22 +13,41 @@ Config/Needs/website: rmarkdown Depends: R (>= 4.0.0) Imports: - Seurat (>= 5.0.0), SeuratObject, - plotthis, + scRepertoire (>= 2.0.8), + circlize, + dplyr, + scales, + ggnewscale, + ggplot2, + rlang, + stringr, + tidyr, + plotthis +Suggests: ComplexHeatmap, - clustree, - ggwordcloud, + Seurat (>= 5.0.0), ggalluvial, - ggpubr, ggforce, + ggpubr, ggraph, ggridges, + ggupset, + ggwordcloud, + ggVennDiagram (>= 1.5.0), hexbin, igraph, - scattermore + clustree, + concaveman, + scattermore, + gridGraphics, + iNEXT, + metap LazyData: true -Remotes: pwwang/plotthis +LazyDataCompression: xz +Remotes: + pwwang/plotthis, + ncborcherding/scRepertoire URL: https://github.com/pwwang/scplotter https://pwwang.github.io/scplotter/ BugReports: diff --git a/NAMESPACE b/NAMESPACE index a7c86d5..4791d35 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,7 +1,19 @@ # Generated by roxygen2: do not edit by hand +export(CCCPlot) export(CellDimPlot) export(CellStatPlot) +export(ClonalAbundancePlot) +export(ClonalCompositionPlot) +export(ClonalDiversityPlot) +export(ClonalGeneUsagePlot) +export(ClonalKmerPlot) +export(ClonalLengthPlot) +export(ClonalOverlapPlot) +export(ClonalPositionalPlot) +export(ClonalRarefactionPlot) +export(ClonalResidencyPlot) +export(ClonalVolumePlot) export(ClustreePlot) export(EnrichmentPlot) export(FeatureStatPlot) @@ -14,19 +26,52 @@ importFrom(SeuratObject,Embeddings) importFrom(SeuratObject,GetAssayData) importFrom(SeuratObject,Graphs) importFrom(SeuratObject,Reductions) +importFrom(circlize,colorRamp2) importFrom(dplyr,"%>%") +importFrom(dplyr,across) +importFrom(dplyr,all_of) +importFrom(dplyr,case_when) +importFrom(dplyr,distinct) +importFrom(dplyr,ends_with) +importFrom(dplyr,filter) +importFrom(dplyr,first) importFrom(dplyr,group_by) +importFrom(dplyr,group_modify) importFrom(dplyr,mutate) importFrom(dplyr,n) +importFrom(dplyr,pull) +importFrom(dplyr,rename) +importFrom(dplyr,rename_with) +importFrom(dplyr,select) +importFrom(dplyr,slice_max) importFrom(dplyr,slice_min) +importFrom(dplyr,slice_sample) +importFrom(dplyr,starts_with) importFrom(dplyr,summarise) importFrom(dplyr,ungroup) +importFrom(ggnewscale,new_scale_fill) +importFrom(ggplot2,aes) +importFrom(ggplot2,element_blank) +importFrom(ggplot2,element_line) +importFrom(ggplot2,geom_point) +importFrom(ggplot2,geom_segment) +importFrom(ggplot2,guide_legend) +importFrom(ggplot2,guides) +importFrom(ggplot2,scale_fill_manual) +importFrom(ggplot2,scale_x_continuous) +importFrom(ggplot2,scale_x_discrete) +importFrom(ggplot2,scale_y_continuous) +importFrom(ggplot2,theme) +importFrom(ggplot2,unit) +importFrom(ggplot2,waiver) importFrom(plotthis,AreaPlot) importFrom(plotthis,BarPlot) importFrom(plotthis,BoxPlot) +importFrom(plotthis,ChordPlot) importFrom(plotthis,CircosPlot) importFrom(plotthis,CorPairsPlot) importFrom(plotthis,CorPlot) +importFrom(plotthis,DensityPlot) importFrom(plotthis,DimPlot) importFrom(plotthis,DotPlot) importFrom(plotthis,EnrichMap) @@ -35,20 +80,55 @@ importFrom(plotthis,FeatureDimPlot) importFrom(plotthis,GSEAPlot) importFrom(plotthis,GSEASummaryPlot) importFrom(plotthis,Heatmap) +importFrom(plotthis,Histogram) +importFrom(plotthis,LinePlot) importFrom(plotthis,LollipopPlot) +importFrom(plotthis,Network) importFrom(plotthis,PieChart) importFrom(plotthis,PrepareEnrichrResult) +importFrom(plotthis,RadarPlot) +importFrom(plotthis,RarefactionPlot) importFrom(plotthis,RidgePlot) importFrom(plotthis,RingPlot) importFrom(plotthis,SankeyPlot) +importFrom(plotthis,ScatterPlot) +importFrom(plotthis,SpiderPlot) importFrom(plotthis,TrendPlot) +importFrom(plotthis,UpsetPlot) +importFrom(plotthis,VennDiagram) importFrom(plotthis,ViolinPlot) importFrom(plotthis,VolcanoPlot) importFrom(plotthis,WordCloudPlot) +importFrom(plotthis,palette_this) importFrom(rlang,"%||%") +importFrom(rlang,":=") importFrom(rlang,sym) importFrom(rlang,syms) +importFrom(scRepertoire,addVariable) +importFrom(scRepertoire,clonalAbundance) +importFrom(scRepertoire,clonalDiversity) +importFrom(scRepertoire,clonalHomeostasis) +importFrom(scRepertoire,clonalLength) +importFrom(scRepertoire,clonalOverlap) +importFrom(scRepertoire,clonalProportion) +importFrom(scRepertoire,clonalQuant) +importFrom(scRepertoire,clonalScatter) +importFrom(scRepertoire,percentAA) +importFrom(scRepertoire,percentKmer) +importFrom(scRepertoire,positionalEntropy) +importFrom(scRepertoire,positionalProperty) +importFrom(scRepertoire,vizGenes) +importFrom(stats,as.dist) +importFrom(stats,cor.test) +importFrom(stats,quantile) importFrom(stringr,str_wrap) +importFrom(tidyr,complete) importFrom(tidyr,drop_na) +importFrom(tidyr,full_seq) importFrom(tidyr,pivot_longer) importFrom(tidyr,pivot_wider) +importFrom(tidyr,replace_na) +importFrom(tidyr,separate) +importFrom(tidyr,unite) +importFrom(utils,combn) +importFrom(utils,getFromNamespace) diff --git a/R/cccplot.R b/R/cccplot.R new file mode 100644 index 0000000..046c519 --- /dev/null +++ b/R/cccplot.R @@ -0,0 +1,225 @@ +#' Cell-Cell Communication Plot +#' +#' @description Plot the cell-cell communication. +#' See also: +#' * The review: \url{https://www.sciencedirect.com/science/article/pii/S2452310021000081} +#' * The LIANA package: \url{https://liana-py.readthedocs.io/en/latest/notebooks/basic_usage.html#Tileplot} +#' * The CCPlotR package: \url{https://github.com/Sarah145/CCPlotR} +#' +#' @param data A data frame with the cell-cell communication data. +#' A typical data frame should have the following columns: +#' * `source` The source cell type. +#' * `target` The target cell type. +#' * `ligand` The ligand gene. +#' * `receptor` The receptor gene. +#' * `ligand_means` The mean expression of the ligand gene per cell type. +#' * `receptor_means` The mean expression of the receptor gene per cell type. +#' * `ligand_props` The proportion of cells that express the entity. +#' * `receptor_props` The proportion of cells that express the entity. +#' * `` The magnitude of the communication. +#' * `` The specificity of the communication. +#' Depends on the `plot_type`, some columns are optional. But the `source`, `target`, +#' `ligand`, `receptor` and `` are required. +#' @param plot_type The type of plot to use. Default is "dot". +#' Possible values are "network", "chord", "circos", "heatmap", "sankey", "alluvial", and "dot". +#' * network: A network plot with the source and target cells as the nodes and the communication as the edges. +#' * chord: A chord plot with the source and target cells as the nodes and the communication as the chords. +#' * circos: Alias of "chord". +#' * heatmap: A heatmap plot with the source and target cells as the rows and columns. +#' * sankey: A sankey plot with the source and target cells as the nodes and the communication as the flows. +#' * alluvial: Alias of "sankey". +#' * dot: A dot plot with the source and target cells as the nodes and the communication as the dots. +#' @param method The method to determine the plot entities. +#' * aggregation: Aggregate the ligand-receptor pairs interactions for each source-target pair. +#' Only the source / target pairs will be plotted. +#' * interaction: Plot the ligand-receptor pairs interactions directly. +#' The ligand-receptor pairs will also be plotted. +#' @param magnitude The column name in the data to use as the magnitude of the communication. +#' By default, the second last column will be used. +#' See `li.mt.show_methods()` for the available methods in `LIANA`. +#' or \url{https://liana-py.readthedocs.io/en/latest/notebooks/basic_usage.html#Tileplot} +#' @param specificity The column name in the data to use as the specificity of the communication. +#' By default, the last column will be used. +#' If the method doesn't have a specificity, set it to NULL. +#' @param weighted Whether to use the magnitude as the weight of the aggregation interaction strength. +#' for source-target pairs. Default is TRUE. Otherwise, the number of interactions will be used. +#' Only used when `method` is "aggregation". +#' @param meta_specificity The method to calculate the specificity when there are multiple +#' ligand-receptor pairs interactions. Default is "sumlog". +#' It should be one of the methods in the `metap` package. +#' @param split_by A character vector of column names to split the plots. Default is NULL. +#' @param x_text_angle The angle of the x-axis text. Default is 90. +#' Only used when `plot_type` is "dot". +#' @param link_curvature The curvature of the links. Default is 0.2. +#' Only used when `plot_type` is "network". +#' @param link_alpha The transparency of the links. Default is 0.6. +#' Only used when `plot_type` is "network". +#' @param facet_by A character vector of column names to facet the plots. Default is NULL. +#' It should always be NULL. +#' @param show_row_names Whether to show the row names in the heatmap. Default is TRUE. +#' Only used when `plot_type` is "heatmap". +#' @param show_column_names Whether to show the column names in the heatmap. Default is TRUE. +#' Only used when `plot_type` is "heatmap". +#' @param ... Other arguments passed to the specific plot function. +#' * For `Network`, see [plotthis::Network]. +#' * For `ChordPlot`, see [plotthis::ChordPlot]. +#' * For `Heatmap`, see [plotthis::Heatmap]. +#' * For `SankeyPlot`, see [plotthis::SankeyPlot]. +#' * For `DotPlot`, see [plotthis::DotPlot]. +#' @return A ggplot object or a list if `combine` is FALSE +#' @importFrom utils getFromNamespace +#' @importFrom rlang syms sym +#' @importFrom dplyr group_by summarise distinct filter n +#' @importFrom tidyr replace_na pivot_wider +#' @importFrom ggplot2 waiver +#' @importFrom plotthis Network ChordPlot Heatmap SankeyPlot DotPlot +#' @export +#' @examples +#' set.seed(8525) +#' data(cellphonedb_res) +#' CCCPlot(data = cellphonedb_res, plot_type = "network", legend.position = "none", +#' theme = "theme_blank", theme_args = list(add_coord = FALSE)) +#' CCCPlot(cellphonedb_res, plot_type = "chord") +#' CCCPlot(cellphonedb_res, plot_type = "heatmap") +#' CCCPlot(cellphonedb_res, plot_type = "dot", weighted = FALSE) +#' +#' cellphonedb_res_sub <- cellphonedb_res[ +#' cellphonedb_res$source %in% c("Dendritic", "CD14+ Monocyte"),] +#' CCCPlot(cellphonedb_res_sub, plot_type = "dot", method = "interaction") +#' CCCPlot(cellphonedb_res_sub, plot_type = "network", method = "interaction", +#' node_size_by = 1) +#' CCCPlot(cellphonedb_res_sub, plot_type = "heatmap", method = "interaction") +CCCPlot <- function( + data, + plot_type = c("dot", "network", "chord", "circos", "heatmap", "sankey", "alluvial"), + method = c("aggregation", "interaction"), + magnitude = waiver(), + specificity = waiver(), + weighted = TRUE, + meta_specificity = "sumlog", + split_by = NULL, + x_text_angle = 90, + link_curvature = 0.2, + link_alpha = 0.6, + facet_by = NULL, + show_row_names = TRUE, + show_column_names = TRUE, + ... +) { + stopifnot("'facet_by' is not supported in CCCPlot." = is.null(facet_by)) + + plot_type <- match.arg(plot_type) + method <- match.arg(method) + source_col <- check_columns(data, "source", force_factor = TRUE) + target_col <- check_columns(data, "target", force_factor = TRUE) + ligand_col <- check_columns(data, "ligand", force_factor = TRUE) + receptor_col <- check_columns(data, "receptor", force_factor = TRUE) + + stopifnot("Columns 'source', 'target', 'ligand', and 'receptor' are required." = + !is.null(source_col) && !is.null(target_col) && !is.null(ligand_col) && !is.null(receptor_col)) + + if (inherits(magnitude, "waiver")) { + magnitude <- names(data)[ncol(data) - 1] + } + if (inherits(specificity, "waiver")) { + specificity <- names(data)[ncol(data)] + } + stopifnot("At least one of 'magnitude' and 'specificity' is required." = + !inherits(magnitude, "waiver") || !inherits(specificity, "waiver")) + + if (method == "aggregation") { + links <- data %>% group_by(!!!syms(c(source_col, target_col, split_by))) + if (!weighted || is.null(magnitude)) { + link_weight_name = "No. of interactions" + if (!is.null(specificity)) { + metap_fn <- getFromNamespace(meta_specificity, "metap") + links <- suppressWarnings({ links %>% + filter(!is.na(!!sym(specificity))) %>% + summarise( + interactionStrength = n(), + .specificity = if (n() == 1) !!sym(specificity) else metap_fn(!!sym(specificity))$p, + .groups = "drop")%>% + replace_na(list(.specificity = 0)) + }) + } else { + links <- links %>% + summarise(interactionStrength = n(), .groups = "drop") + } + } else { + link_weight_name = "Interaction strength" + if (!is.null(specificity)) { + metap_fn <- getFromNamespace(meta_specificity, "metap") + links <- suppressWarnings({ links %>% + filter(!is.na(!!sym(specificity))) %>% + summarise( + interactionStrength = sum(!!sym(magnitude)), + .specificity = if (n() == 1) !!sym(specificity) else metap_fn(!!sym(specificity))$p, + .groups = "drop") %>% + replace_na(list(.specificity = 0)) + }) + } else { + links <- links %>% + summarise(interactionStrength = sum(!!sym(magnitude)), .groups = "drop") + } + } + + if (plot_type == "network") { + Network(links, from = source_col, to = target_col, node_fill_by = "name", + link_curvature = link_curvature, link_weight_name = link_weight_name, link_alpha = link_alpha, + node_fill_name = "Source/Target", link_weight_by = "interactionStrength", ...) + } else if (plot_type %in% c("chord", "circos")) { + ChordPlot(links, y = "interactionStrength", from = source_col, to = target_col, + ...) + } else if (plot_type == "heatmap") { + sources <- if (is.factor(links[[source_col]])) { + levels(links[[source_col]]) + } else { + unique(links[[source_col]]) + } + links <- pivot_wider(links, names_from = source_col, values_from = "interactionStrength", + values_fill = 0) + Heatmap(links, rows = sources, columns_by = target_col, rows_name = "source", + name = link_weight_name, show_row_names = show_row_names, show_column_names = show_column_names, + ...) + } else if (plot_type %in% c("sankey", "alluvial")) { + SankeyPlot(links, y = "interactionStrength", nodes_by = c(source_col, target_col), + ...) + } else if (plot_type == "dot") { + if (!is.null(specificity)) { + DotPlot(links, x = source_col, y = target_col, size_by = "interactionStrength", + fill_by = ".specificity", fill_name = paste0(meta_specificity, "(", specificity, ")"), + size_name = link_weight_name, x_text_angle = x_text_angle, ...) + } else { + DotPlot(links, x = source_col, y = target_col, size_by = "interactionStrength", + size_name = link_weight_name, x_text_angle = x_text_angle, ...) + } + } + } else if (method == "interaction") { + if (plot_type == "dot") { + if (!is.null(specificity)) { + data[[specificity]] <- -log10(data[[specificity]]) + } + data[[source_col]] <- paste0("source: ", data[[source_col]]) + DotPlot(data, x = target_col, y = c(ligand_col, receptor_col), y_sep = " -> ", + fill_by = specificity, fill_name = paste0("-log10(", specificity, ")"), + size_by = magnitude, x_text_angle = x_text_angle, + facet_by = source_col, ...) + } else if (plot_type == "network") { + data$source_target <- paste0(data[[source_col]], " -> ", data[[target_col]]) + Network(data, from = "ligand", to = "receptor", + link_weight_by = magnitude, link_alpha = link_alpha, link_color_by = "source_target", + link_color_name = "source -> target", ...) + } else if (plot_type == "heatmap") { + data$ligand_receptor <- paste0(data[[ligand_col]], " -> ", data[[receptor_col]]) + all_lrs <- unique(data$ligand_receptor) + data <- pivot_wider(data, names_from = "ligand_receptor", values_from = magnitude, + values_fill = 0) + Heatmap(data, rows = all_lrs, rows_name = "Ligand -> Receptor", + name = magnitude, columns_by = target_col, columns_split_by = source_col, + show_row_names = show_row_names, show_column_names = show_column_names, + ...) + } else { + stop("Plot type '", plot_type, "' is not supported for method 'interaction' in CCCPlot yet.") + } + } +} diff --git a/R/celldimplot.R b/R/celldimplot.R index 705e953..56d9938 100644 --- a/R/celldimplot.R +++ b/R/celldimplot.R @@ -4,7 +4,7 @@ #' @param object A seurat object #' @param reduction Name of the reduction to plot (for example, "umap"). #' @param graph Specify the graph name to add edges between cell neighbors to the plot. -#' @param ... Other arguments passed to \code{\link{plotthis::DimPlot}} +#' @param ... Other arguments passed to [plotthis::DimPlot]. #' @return A ggplot object or a list if `combine` is FALSE #' @export #' @importFrom SeuratObject DefaultDimReduc Embeddings Graphs Reductions @@ -56,7 +56,7 @@ #' CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", #' add_mark = TRUE) #' CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", -#' add_mark = TRUE, mark_expand = unit(1, "mm")) +#' add_mark = TRUE, mark_expand = grid::unit(1, "mm")) #' CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", #' add_mark = TRUE, mark_alpha = 0.3) #' CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", diff --git a/R/cellstatplot.R b/R/cellstatplot.R index f3a0c6e..20e6742 100644 --- a/R/cellstatplot.R +++ b/R/cellstatplot.R @@ -20,7 +20,7 @@ #' @param facet_by The column name in the meta data to facet the plots. Default: NULL #' Not available for 'circos', 'sankey', and 'heatmap' plots. #' @param plot_type The type of plot to use. Default is "bar". -#' Possible values are "bar", "circos", "pie", "pies", "ring"/"donut", "trend", "area", "heatmap", and "sankey"/"alluvial". +#' Possible values are "bar", "circos", "pie", "pies", "ring"/"donut", "trend", "area", "heatmap", "sankey"/"alluvial", "radar" and "spider". #' 'pie' vs 'pies': 'pie' plot will plot a single pie chart for each group, while 'pies' plot will plot multiple pie charts for each group and split. #' 'pies' basically is a heatmap with 'cell_type = "pie"'. #' @param frac The way of calculating the fraction. Default is "none". @@ -47,24 +47,24 @@ #' @param name The name of the 'pies'/'heatmap' plot, shown as the name of the main legend. Default is NULL. #' @param ylab The y-axis label. Default is NULL. #' @param ... Other arguments passed to the specific plot function. -#' * For `bar` plot, see \code{\link{plotthis::BarPlot}}. -#' * For `circos` plot, see \code{\link{plotthis::CircosPlot}}. -#' * For `pie` chart, see \code{\link{plotthis::PieChart}}. -#' * For `pies` plot, see \code{\link{plotthis::Heatmap}}. -#' * For `heatmap` plot, see \code{\link{plotthis::Heatmap}}. -#' * For `ring`/`donut` plot, see \code{\link{plotthis::RingPlot}}. -#' * For `trend` plot, see \code{\link{plotthis::TrendPlot}}. -#' * For `area` plot, see \code{\link{plotthis::AreaPlot}}. -#' * For `sankey`/`alluvial` plot, see \code{\link{plotthis::SankeyPlot}}. +#' * For `bar` plot, see [plotthis::BarPlot]. +#' * For `circos` plot, see [plotthis::CircosPlot]. +#' * For `pie` chart, see [plotthis::PieChart]. +#' * For `pies` plot, see [plotthis::Heatmap]. +#' * For `heatmap` plot, see [plotthis::Heatmap]. +#' * For `ring`/`donut` plot, see [plotthis::RingPlot]. +#' * For `trend` plot, see [plotthis::TrendPlot]. +#' * For `area` plot, see [plotthis::AreaPlot]. +#' * For `sankey`/`alluvial` plot, see [plotthis::SankeyPlot]. #' #' @return A ggplot object or a list if `combine` is FALSE #' @importFrom rlang sym syms #' @importFrom dplyr %>% summarise mutate ungroup n #' @importFrom tidyr drop_na pivot_wider -#' @importFrom plotthis BarPlot CircosPlot PieChart RingPlot TrendPlot AreaPlot SankeyPlot Heatmap +#' @importFrom plotthis BarPlot CircosPlot PieChart RingPlot TrendPlot AreaPlot SankeyPlot Heatmap RadarPlot SpiderPlot #' @export #' @examples -#' library(patchwork) +#' # library(patchwork) #' data(ifnb_sub) #' #' # Bar plot @@ -128,11 +128,16 @@ #' CellStatPlot(ifnb_sub, plot_type = "heatmap", group_by = "stim", palette = "Blues") #' CellStatPlot(ifnb_sub, plot_type = "heatmap", group_by = "stim", #' frac = "group", columns_split_by = "seurat_annotations", swap = TRUE) +#' +#' # Radar plot/Spider plot +#' pr <- CellStatPlot(ifnb_sub, plot_type = "radar", group_by = "stim") +#' ps <- CellStatPlot(ifnb_sub, plot_type = "spider", group_by = "stim") +#' pr | ps CellStatPlot <- function( object, ident = "seurat_clusters", group_by = NULL, group_by_sep = "_", split_by = NULL, split_by_sep = "_", facet_by = NULL, rows = NULL, columns_split_by = NULL, frac = c("none", "group", "ident", "cluster", "all"), rows_name = NULL, name = NULL, - plot_type = c("bar", "circos", "pie", "pies", "ring", "donut", "trend", "area", "sankey", "alluvial", "heatmap"), + plot_type = c("bar", "circos", "pie", "pies", "ring", "donut", "trend", "area", "sankey", "alluvial", "heatmap", "radar", "spider"), swap = FALSE, ylab = NULL, ... ) { data <- object@meta.data @@ -141,17 +146,17 @@ CellStatPlot <- function( if (plot_type == "donut") plot_type <- "ring" if (plot_type == "alluvial") plot_type <- "sankey" if (isFALSE(swap) && plot_type %in% c("sankey", "heatmap")) { - group_by <- plotthis:::check_columns(data, group_by, force_factor = TRUE, + group_by <- check_columns(data, group_by, force_factor = TRUE, allow_multi = TRUE) } else { - group_by <- plotthis:::check_columns(data, group_by, force_factor = TRUE, + group_by <- check_columns(data, group_by, force_factor = TRUE, allow_multi = TRUE, concat_multi = TRUE, concat_sep = group_by_sep) } - split_by <- plotthis:::check_columns(data, split_by, force_factor = TRUE, + split_by <- check_columns(data, split_by, force_factor = TRUE, allow_multi = TRUE, concat_multi = TRUE, concat_sep = split_by_sep) - facet_by <- plotthis:::check_columns(data, facet_by, force_factor = TRUE, + facet_by <- check_columns(data, facet_by, force_factor = TRUE, allow_multi = TRUE) - rows <- plotthis:::check_columns(data, rows, force_factor = TRUE, + rows <- check_columns(data, rows, force_factor = TRUE, allow_multi = TRUE) frac <- match.arg(frac) @@ -183,7 +188,7 @@ CellStatPlot <- function( if (frac != "none") { data <- data %>% dplyr::group_by(!!!syms(unique(c(split_by, facet_by, columns_split_by)))) %>% - mutate(.frac = .n / sum(.n)) + mutate(.frac = !!sym(".n") / sum(!!sym(".n"))) } else { data <- data %>% mutate(.frac = 1) # not used } @@ -198,17 +203,17 @@ CellStatPlot <- function( if (frac == "group") { dat <- dat %>% dplyr::group_by(!!!syms(unique(c(split_by, facet_by, g, columns_split_by)))) %>% - mutate(.frac = .n / sum(.n)) %>% + mutate(.frac = !!sym(".n") / sum(!!sym(".n"))) %>% ungroup() } else if (frac == "ident") { dat <- dat %>% dplyr::group_by(!!!syms(unique(c(split_by, facet_by, columns_split_by, ident)))) %>% - mutate(.frac = .n / sum(.n)) %>% + mutate(.frac = !!sym(".n") / sum(!!sym(".n"))) %>% ungroup() } else if (frac == "all") { dat <- dat %>% dplyr::group_by(!!!syms(unique(c(split_by, facet_by, columns_split_by)))) %>% - mutate(.frac = .n / sum(.n)) %>% + mutate(.frac = !!sym(".n") / sum(!!sym(".n"))) %>% ungroup() } else { dat <- dat %>% mutate(.frac = 1) # not used @@ -225,17 +230,17 @@ CellStatPlot <- function( if (frac == "group") { data <- data %>% dplyr::group_by(!!!syms(unique(c(split_by, facet_by, group_by, columns_split_by)))) %>% - mutate(.frac = .n / sum(.n)) %>% + mutate(.frac = !!sym(".n") / sum(!!sym(".n"))) %>% ungroup() } else if (frac == "ident") { data <- data %>% dplyr::group_by(!!!syms(unique(c(split_by, facet_by, columns_split_by, ident)))) %>% - mutate(.frac = .n / sum(.n)) %>% + mutate(.frac = !!sym(".n") / sum(!!sym(".n"))) %>% ungroup() } else if (frac == "all") { data <- data %>% dplyr::group_by(!!!syms(unique(c(split_by, facet_by, columns_split_by)))) %>% - mutate(.frac = .n / sum(.n)) %>% + mutate(.frac = !!sym(".n") / sum(!!sym(".n"))) %>% ungroup() } else { data <- data %>% mutate(.frac = 1) # not used @@ -313,7 +318,6 @@ CellStatPlot <- function( if (isTRUE(swap)) { stop("'swap = TRUE' is not supported for 'sankey' plot.") } - d <<- data SankeyPlot( data, nodes_by = c(ident, group_by), @@ -362,5 +366,24 @@ CellStatPlot <- function( columns_by = if (swap) columns_split_by else group_by, columns_split_by = if (swap) group_by else columns_split_by, split_by = split_by, ...) + } else { + if (is.null(group_by)) { + stop("Cannot create a '", plot_type, "' plot without specifying 'group_by'.") + } + if (plot_type == "radar") { + RadarPlot( + data, + x = ifelse(swap, group_by, ident), + y = ifelse(identical(frac, "none"), ".n", ".frac"), + group_by = ifelse(swap, ident, group_by), + split_by = split_by, facet_by = facet_by, ...) + } else if (plot_type == "spider") { + SpiderPlot( + data, + x = ifelse(swap, group_by, ident), + y = ifelse(identical(frac, "none"), ".n", ".frac"), + group_by = ifelse(swap, ident, group_by), + split_by = split_by, facet_by = facet_by, ...) + } } } diff --git a/R/clonaldivplot.R b/R/clonaldivplot.R new file mode 100644 index 0000000..336d976 --- /dev/null +++ b/R/clonaldivplot.R @@ -0,0 +1,272 @@ +#' Calculate the clonal diversities. +#' +#' @keywords internal +#' @importFrom utils getFromNamespace +#' @importFrom dplyr slice_sample +ClonalDiversity <- function( + input.data, cloneCall = "gene", chain = "both", + method = c("shannon", "gini.coeff", "inv.simpson", "norm.entropy", "gini.simpson", "chao1", "ACE"), + group_by = NULL, n_boots = 100) { + method <- match.arg(method) + if (method == "gini.coeff") { + div_fn <- function(d) { + n <- length(d) + 1 / n * (n + 1 - 2 * sum((n + 1 - 1:n) * d) / sum(d)) + } + } else { + div_fn <- getFromNamespace(paste0(".", method), "scRepertoire") + } + + is_seurat_object <- getFromNamespace("is_seurat_object", "scRepertoire") + is_se_object <- getFromNamespace("is_se_object", "scRepertoire") + .data.wrangle <- getFromNamespace(".data.wrangle", "scRepertoire") + .theCall <- getFromNamespace(".theCall", "scRepertoire") + .groupList <- getFromNamespace(".groupList", "scRepertoire") + .short.check <- getFromNamespace(".short.check", "scRepertoire") + + sco <- is_seurat_object(input.data) | is_se_object(input.data) + input.data <- .data.wrangle(input.data, group_by, .theCall(input.data, + cloneCall, + check.df = FALSE + ), chain) + cloneCall <- .theCall(input.data, cloneCall) + mat <- NULL + sample <- c() + if (!is.null(group_by) & !sco) { + input.data <- .groupList(input.data, group_by) + } + min <- .short.check(input.data, cloneCall) + for (i in seq_along(input.data)) { + data <- as.data.frame(table(input.data[[i]][, cloneCall])) + mat_a <- NULL + sample <- c() + if (n_boots > 0) { + sample <- div_fn(data$Freq) + mat_a <- rbind(mat_a, sample) + mat_a[is.na(mat_a)] <- 0 + mat <- rbind(mat, mat_a) + mat <- as.data.frame(mat) + } else { + for (j in seq(seq_len(n_boots))) { + x <- slice_sample(data, n = min) + sample <- div_fn(x$Freq) + mat_a <- rbind(mat_a, sample) + } + mat_a[is.na(mat_a)] <- 0 + mat_b <- colMeans(mat_a) + mat_b <- as.data.frame(t(mat_b)) + mat <- rbind(mat, mat_b) + } + } + if (is.null(group_by)) { + group_by <- "Group" + } + colnames(mat) <- method + mat[, group_by] <- names(input.data) + mat +} + +#' ClonalDiversityPlot +#' +#' Plot the clonal diversities of the samples/groups. +#' +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' in the data +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param method The method to calculate the diversity. Options are "shannon" (default), +#' "inv.simpson", "norm.entropy", "gini.simpson", "chao1", "ACE" and "gini.coeff". +#' See [scRepertoire::clonalDiversity] for details. +#' @param plot_type The type of plot. Options are "bar", "box" and "violin". +#' @param position The position adjustment for the bars. Default is "dodge". +#' @param group_by A character vector of column names to group the samples. Default is NULL. +#' @param facet_by A character vector of column names to facet the plots. Default is NULL. +#' @param split_by A character vector of column names to split the plots. Default is NULL. +#' @param xlab The x-axis label. Default is NULL. +#' @param ylab The y-axis label. Default is NULL. +#' @param ... Other arguments passed to the specific plot function. +#' * For "bar", [plotthis::BarPlot] +#' * For "box", [plotthis::BoxPlot] +#' * For "violin", [plotthis::ViolinPlot] +#' @return A ggplot object or a list if `combine` is FALSE +#' @export +#' @importFrom tidyr separate +#' @importFrom scRepertoire clonalDiversity +#' @importFrom plotthis BarPlot BoxPlot ViolinPlot +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Subject", +#' variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +#' ) +#' +#' ClonalDiversityPlot(data) +#' ClonalDiversityPlot(data, group_by = "Type") +#' ClonalDiversityPlot(data, group_by = "Type", plot_type = "box") +#' ClonalDiversityPlot(data, group_by = "Type", plot_type = "violin") +#' ClonalDiversityPlot(data, group_by = "Type", plot_type = "violin", +#' method = "gini.coeff", add_box = TRUE) +ClonalDiversityPlot <- function( + data, clone_call = "gene", chain = "both", + method = c("shannon", "gini.coeff", "inv.simpson", "norm.entropy", "gini.simpson", "chao1", "ACE"), + plot_type = c("bar", "box", "violin"), position = "dodge", + group_by = NULL, facet_by = NULL, split_by = NULL, + xlab = NULL, ylab = NULL, + ...) { + method <- match.arg(method) + plot_type <- match.arg(plot_type) + + if (plot_type %in% c("box", "violin") && is.null(group_by)) { + stop("'group_by' must be provided for box/violin ClonalDiversityPlot") + } + all_groupings <- unique(c("Sample", group_by, facet_by, split_by)) + method_name <- switch(method, + shannon = "Shannon Index", + gini.coeff = "Gini Coefficient", + inv.simpson = "Inverse Simpson Index", + norm.entropy = "Normalized Entropy", + gini.simpson = "Gini-Simpson Index", + chao1 = "Chao1 Index", + ACE = "ACE Index" + ) + + data <- MergeClonalGroupings(data, all_groupings) + data <- ClonalDiversity(data, + cloneCall = clone_call, chain = chain, method = method, + group_by = ".group" + ) + data <- separate(data, ".group", into = all_groupings, sep = " // ") + + if (plot_type == "bar") { + x <- group_by %||% "Sample" + group_by <- if(is.null(group_by)) NULL else "Sample" + BarPlot(data, + x = x, y = method, group_by = group_by, position = position, + xlab = xlab %||% group_by, ylab = ylab %||% method_name, + split_by = split_by, facet_by = facet_by, ... + ) + } else if (plot_type == "box") { + BoxPlot(data, + x = group_by, y = method, + xlab = xlab %||% group_by, ylab = ylab %||% method_name, + split_by = split_by, facet_by = facet_by, ... + ) + } else if (plot_type == "violin") { + ViolinPlot(data, + x = group_by, y = method, + xlab = xlab %||% group_by, ylab = ylab %||% method_name, + split_by = split_by, facet_by = facet_by, ... + ) + } +} + +#' ClonalRarefactionPlot +#' +#' Plot the rarefaction curves +#' +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param group_by A character vector of column names to group the samples. Default is "Sample". +#' @param group_by_sep The separator for the group_by column. Default is "_". +#' @param n_boots The number of bootstrap samples. Default is 20. +#' @param q The hill number. Default is 0. +#' * 0 - Species richness +#' * 1 - Shannon entropy +#' * 2 - Simpson index#' +#' @param facet_by A character vector of column names to facet the plots. Default is NULL. +#' @param split_by A character vector of column names to split the plots. Default is NULL. +#' @param split_by_sep The separator for the split_by column. Default is "_". +#' @param palette The color palette to use. Default is "Paired". +#' @param combine Whether to combine the plots into a single plot. Default is TRUE. +#' @param nrow The number of rows in the combined plot. Default is NULL. +#' @param ncol The number of columns in the combined plot. Default is NULL. +#' @param byrow Whether to fill the combined plot by row. Default is TRUE. +#' @param ... Other arguments passed to [plotthis::RarefactionPlot]. +#' @return A ggplot object or a list if `combine` is FALSE +#' @importFrom plotthis RarefactionPlot +#' @export +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Subject", +#' variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +#' ) +#' +#' ClonalRarefactionPlot(data, type = 1, q = 0, n_boots = 2) +#' ClonalRarefactionPlot(data, type = 2, q = 0, n_boots = 2) +#' ClonalRarefactionPlot(data, type = 3, q = 0, n_boots = 2) +#' ClonalRarefactionPlot(data, q = 1, n_boots = 2) +#' ClonalRarefactionPlot(data, q = 1, n_boots = 2, group_by = "Type") +#' ClonalRarefactionPlot(data, n_boots = 2, split_by = "Type") +ClonalRarefactionPlot <- function( + data, clone_call = "aa", chain = "both", + group_by = "Sample", group_by_sep = "_", + n_boots = 20, q = 0, facet_by = NULL, split_by = NULL, split_by_sep = "_", + palette = "Paired", combine = TRUE, nrow = NULL, ncol = NULL, byrow = TRUE, ... +) { + if (!is.null(facet_by)) { + stop("'facet_by' is not supported in ClonalRarefactionPlot.") + } + + all_groupings <- unique(c(group_by, split_by)) + data <- MergeClonalGroupings(data, all_groupings) + + .data.wrangle <- getFromNamespace(".data.wrangle", "scRepertoire") + .theCall <- getFromNamespace(".theCall", "scRepertoire") + .groupList <- getFromNamespace(".groupList", "scRepertoire") + is_se_object <- getFromNamespace("is_se_object", "scRepertoire") + is_seurat_object <- getFromNamespace("is_seurat_object", "scRepertoire") + + data <- .data.wrangle(data, ".group", .theCall(data, clone_call, check.df = FALSE), + chain) + cloneCall <- .theCall(data, clone_call) + + if (!is_seurat_object(data) && !is_se_object(data)) { + data <- .groupList(data, group.by = ".group") + } + + if (is.null(split_by)) { + matlist <- lapply(data, function(x) { table(x[, cloneCall]) }) + RarefactionPlot(matlist, q = q, nboot = n_boots, palette = palette, + group_name = paste(group_by, sep = group_by_sep), ...) + } else { + datas <- list() + for (nm in names(data)) { + nms <- strsplit(nm, " // ", fixed = TRUE)[[1]] + names(nms) <- all_groupings + gname <- paste(nms[group_by], collapse = group_by_sep) + sname <- paste(nms[split_by], collapse = split_by_sep) + d <- list(table(data[[nm]][, cloneCall])) + names(d) <- gname + datas[[sname]] <- c(datas[[sname]], d) + } + + plots <- lapply(names(datas), function(nm) { + RarefactionPlot(datas[[nm]], q = q, nboot = n_boots, palette = palette, + group_name = paste(group_by, sep = group_by_sep), title = nm, ...) + }) + + combine_plots(plots, combine = combine, nrow = nrow, ncol = ncol, byrow = byrow) + } +} diff --git a/R/clonalgeneusageplot.R b/R/clonalgeneusageplot.R new file mode 100644 index 0000000..4f19cc6 --- /dev/null +++ b/R/clonalgeneusageplot.R @@ -0,0 +1,155 @@ +#' ClonalGeneUsagePlot +#' +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param genes The prefix of genes to be plotted. Default is "TRBV". +#' If two sets of genes are provided (e.g. c("TRBV", "TRBJ")), the second dimension will be the +#' second set of genes instead of the group_by variable. +#' @param scale Whether to use the proportion that is scaled to the group or the count. +#' @param top The number of top genes/genepairs to be plotted. +#' @param plot_type The type of plot to be generated. Default is "bar". +#' Options are "bar", "heatmap", "circos" (aka "chord"). +#' @param group_by The variable to group the data by. Default is "Sample". +#' @param facet_by A character vector of column names to facet the plots. Default is NULL. +#' Should not be specified manually. +#' @param facet_ncol The number of columns in the facet grid. Default is 1. +#' @param split_by A character vector of column names to split the plots. Default is NULL. +#' @param aspect.ratio The aspect ratio of the plot. Only available for "bar" plot. Default is 2/top. +#' @param theme_args A list of arguments to be passed to the [ggplot2::theme] function. +#' @param ylab The y-axis label. Default is NULL. +#' @param row_annotation A list of row annotations to be added to the heatmap. Default is NULL. +#' @param row_annotation_type A list of row annotation types. +#' @param row_annotation_side The side of the row annotation. Default is "right". +#' @param row_annotation_agg A list of row annotation aggregation functions. +#' @param ... Other arguments passed to the specific plot function. +#' * For "bar", [plotthis::BarPlot] +#' * For "heatmap", [plotthis::Heatmap] +#' * For "circos", [plotthis::ChordPlot] +#' * For "chord", [plotthis::ChordPlot] +#' @return A ggplot object or a list if `combine` is FALSE +#' @importFrom rlang := syms +#' @importFrom dplyr mutate rename slice_max summarise pull filter all_of +#' @importFrom tidyr separate +#' @importFrom ggplot2 unit element_blank +#' @importFrom scRepertoire vizGenes +#' @importFrom plotthis BarPlot Heatmap ChordPlot SankeyPlot +#' @export +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Subject", +#' variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +#' ) +#' +#' ClonalGeneUsagePlot(data) +#' ClonalGeneUsagePlot(data, genes = "TRBJ", genes2 = "TRBV") +#' ClonalGeneUsagePlot(data, top = 40, plot_type = "heatmap") +#' ClonalGeneUsagePlot(data, genes = c("TRBV", "TRBJ"), plot_type = "heatmap") +#' ClonalGeneUsagePlot(data, genes = "TRBV", group_by = "Type", plot_type = "chord") +#' ClonalGeneUsagePlot(data, genes = c("TRBV", "TRBJ"), group_by = "Type", plot_type = "chord") +#' ClonalGeneUsagePlot(data, genes = c("TRBV", "TRBJ"), plot_type = "alluvial") +ClonalGeneUsagePlot <- function( + data, genes = "TRBV", scale = TRUE, top = 20, + plot_type = c("bar", "heatmap", "circos", "chord", "alluvial", "sankey"), + group_by = "Sample", facet_by = NULL, facet_ncol = 1, split_by = NULL, + aspect.ratio = 2 / top, theme_args = list(), ylab = NULL, + row_annotation = NULL, row_annotation_type = list(), row_annotation_side = "right", + row_annotation_agg = list(), ... +) { + plot_type <- match.arg(plot_type) + if (!is.null(facet_by)) { + stop("'facet_by' should not be specified in ClonalGeneUsagePlot.") + } + + if (length(genes) > 1) { + genes2 <- genes[2] + genes <- genes[1] + } else { + genes2 <- NULL + } + + all_groupings <- unique(c(group_by, split_by)) + data <- MergeClonalGroupings(data, all_groupings) + data <- vizGenes(data, x.axis = genes, y.axis = genes2, group.by = ".group", scale = scale, exportTable = TRUE) + if (is.null(genes2)) { + axis1 <- genes + axis2 <- group_by + + data <- separate(data, "y.axis", into = all_groupings, sep = " // ") %>% + rename(!!sym(axis1) := "x.axis") + + selected_genes <- data %>% dplyr::group_by(!!sym(genes)) %>% + summarise(total = sum(!!sym("count")), .groups = "drop") %>% + slice_max(order_by = !!sym("total"), n = top) %>% + pull(axis1)%>% + as.character() + + data <- data %>% filter(!!sym(axis1) %in% selected_genes) + } else { + axis1 <- genes + axis2 <- genes2 + + data <- separate(data, "element.names", into = all_groupings, sep = " // ") %>% + rename(!!sym(genes) := "x.axis", !!sym(genes2) := "y.axis") %>% + unite("GenePairs", c(genes, genes2), sep = " // ", remove = FALSE) + + genepairs <- data %>% dplyr::group_by(!!!syms(c(genes, genes2))) %>% + summarise(total = sum(!!sym("count")), .groups = "drop") %>% + slice_max(order_by = !!sym("total"), n = top) %>% + unite("GenePairs", c(genes, genes2), sep = " // ") %>% + pull("GenePairs") + + data <- data %>% filter(!!sym("GenePairs") %in% genepairs) + data$GenePairs <- NULL + } + + if (plot_type == "bar") { + # theme_args$panel.spacing <- theme_args$panel.spacing %||% unit(-0.1, "lines") + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% element_blank() + ylab <- ylab %||% ifelse(scale, "Gene Usage Fraction", "Gene Usage Count") + + BarPlot(data, x = axis1, y = ifelse(scale, "proportion", "count"), facet_by = axis2, facet_ncol = facet_ncol, + split_by = split_by, legend.position = "none", x_text_angle = 90, aspect.ratio = aspect.ratio, + facet_args = list(strip.position = "right"), theme_args = theme_args, ylab = ylab, ...) + } else if (plot_type == "heatmap") { + rmcols <- c("total", "n", "varcount", "sd", "mean") + if (!is.null(genes2)) { + rmcols <- c(rmcols, group_by) + } + if (scale) { + rmcols <- c(rmcols, "count") + } else { + rmcols <- c(rmcols, "proportion") + } + data <- data[, setdiff(colnames(data), rmcols), drop = FALSE] + row_genes <- unique(as.character(data[[axis1]])) + data <- data %>% pivot_wider(names_from = axis1, values_from = ifelse(scale, "proportion", "count"), + values_fn = sum, values_fill = 0) + rows_data <- data[, c(split_by, row_genes), drop = FALSE] %>% + dplyr::group_by(!!!syms(split_by)) %>% + summarise(across(all_of(row_genes), sum), .groups = "drop") %>% + pivot_longer(cols = -all_of(split_by), names_to = axis1, values_to = ".total") + + row_annotation <- row_annotation %||% list(`Total Usage` = ".total") + row_annotation_type$`Total Usage` <- row_annotation_type$`Total Usage` %||% "lines" + row_annotation_agg$`Total Usage` <- row_annotation_agg$`Total Usage` %||% sum + + Heatmap(data, rows = row_genes, columns_by = axis2, split_by = split_by, rows_data = rows_data, + rows_name = axis1, name = ifelse(scale, "Gene Usage Fraction", "Gene Usage Count"), + row_annotation = row_annotation, row_annotation_type = row_annotation_type, row_annotation_agg = row_annotation_agg, + split_rows_data = TRUE, row_annotation_side = row_annotation_side, ...) + } else if (plot_type %in% c("circos", "chord")) { + ChordPlot(data, from = axis1, to = axis2, y = ifelse(scale, "proportion", "count"), + split_by = split_by, theme_args = theme_args, ...) + } else { # alluvial / sankey + SankeyPlot(data, links_by = axis1, nodes_by = c(axis1, axis2), y = ifelse(scale, "proportion", "count"), + split_by = split_by, theme_args = theme_args, ...) + } +} diff --git a/R/clonalpositionalplot.R b/R/clonalpositionalplot.R new file mode 100644 index 0000000..0dadf09 --- /dev/null +++ b/R/clonalpositionalplot.R @@ -0,0 +1,299 @@ +#' ClonalPositionalPlot +#' +#' Visualize the positional entropy, property or amino acid frequency of CDR3 sequences. +#' +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param chain The chain to be analyzed. Default is "TRB". +#' @param aa_length The length of the amino acid sequence. Default is 20. +#' @param group_by The variable to group the data by. Default is "Sample". +#' @param group_by_sep The separator to use when combining groupings. Default is "_". +#' @param split_by The variable to split the data by. Default is NULL. +#' @param method The method to calculate the positional entropy. Default is "AA". +#' * "AA": Amino acid frequency. +#' * "shannon": Shannon entropy. +#' * "inv.simpson": Inverse Simpson index. +#' * "norm.entropy": Normalized entropy. +#' * "Atchley": Atchley factors. +#' * "Kidera": Kidera factors. +#' * "stScales": stScales factors. +#' * "tScales": tScales factors. +#' * "VHSE": Vectors of Hydrophobic, Steric, and Electronic properties. +#' See also [scRepertoire::percentAA], [scRepertoire::positionalEntropy] and +#' [scRepertoire::positionalProperty]. +#' @param plot_type The type of plot to generate. Default is "bar". +#' * "bar": Bar plot. +#' * "line": Line plot. +#' * "heatmap": Heatmap. +#' * "box": Box plot. +#' * "violin": Violin plot. +#' @param theme_args A list of arguments to be passed to the [ggplot2::theme] function. +#' @param xlab The x-axis label. Default is NULL. +#' @param ylab The y-axis label. Default is NULL. +#' @param facet_by A character vector of column names to facet the plots. Default is NULL. +#' @param facet_ncol The number of columns in the facet grid. Default is NULL. +#' @param facet_nrow The number of rows in the facet grid. Default is NULL. +#' @param aspect.ratio The aspect ratio of the plot. Default is NULL. +#' @param ... Other arguments passed to the specific plot function. +#' * For "bar", [plotthis::BarPlot] +#' * For "line", [plotthis::LinePlot] +#' * For "heatmap", [plotthis::Heatmap] +#' * For "box", [plotthis::BoxPlot] +#' * For "violin", [plotthis::ViolinPlot] +#' @return A ggplot object or a list if `combine` is FALSE +#' @export +#' @importFrom ggplot2 element_blank +#' @importFrom scRepertoire percentAA positionalEntropy positionalProperty +#' @importFrom plotthis BarPlot LinePlot Heatmap BoxPlot ViolinPlot +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' +#' ClonalPositionalPlot(data) +#' ClonalPositionalPlot(data, method = "shannon") +#' ClonalPositionalPlot(data, method = "norm.entropy", plot_type = "heatmap") +#' ClonalPositionalPlot(data, method = "Atchley", group_by = "Type", plot_type = "bar") +#' ClonalPositionalPlot(data, method = "Atchley", plot_type = "line") +ClonalPositionalPlot <- function ( + data, chain = "TRB", aa_length = 20, group_by = "Sample", group_by_sep = "_", split_by = NULL, + method = c("AA", "shannon", "inv.simpson", "norm.entropy", "Atchley", + "Kidera", "stScales", "tScales", "VHSE"), + plot_type = c("bar", "line", "heatmap", "box", "violin"), theme_args = list(), + xlab = NULL, ylab = NULL, facet_by = NULL, facet_ncol = NULL, facet_nrow = NULL, + aspect.ratio = NULL, + ... +) { + method <- match.arg(method) + plot_type <- match.arg(plot_type) + if (plot_type %in% c("box", "violin")) { + if (is.null(group_by) || identical(group_by, "Sample")) { + stop("'group_by' must be provided for box/violin ClonalPositionalPlot") + } + all_groupings <- unique(c("Sample", group_by, facet_by, split_by)) + } else { + all_groupings <- unique(c(group_by, facet_by, split_by)) + } + data <- MergeClonalGroupings(data, all_groupings) + + if (method == "AA") { + if (!is.null(facet_by)) { + stop("'facet_by' should not be specified for AA bar plot in ClonalPositionalPlot.") + } + data <- percentAA(data, chain = chain, aa.length = aa_length, group.by = ".group", + exportTable = TRUE) + data <- separate(data, "group", into = all_groupings, sep = " // ") + + if (plot_type == "bar") { + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% element_blank() + + BarPlot(data, x = "variable", y = "value", group_by = "AA", position = "stack", + xlab = xlab %||% "Position", ylab = ylab %||% "Amino Acid Frequency", + split_by = split_by, facet_by = group_by, facet_ncol = facet_ncol %||% 1, facet_nrow = facet_nrow, + x_text_angle = 90, facet_args = list(strip.position = "right"), + aspect.ratio = aspect.ratio %||% (2 / aa_length), theme_args = theme_args, ... + ) + } else if (plot_type == "heatmap") { + data <- data %>% unite(".group", !!!syms(group_by), sep = group_by_sep) + allgroups <- unique(data$.group) + data <- data %>% + pivot_wider(names_from = ".group", values_from = "value") %>% + rename(Position = "variable") + + Heatmap(data, columns_by = "Position", rows = allgroups, rows_name = paste(group_by, collapse = group_by_sep), + cell_type = "pie", pie_group_by = "AA", cluster_rows = FALSE, cluster_columns = FALSE, + pie_values = "sum", ...) + } else { + stop("Only 'bar' and 'heatmap' plot types are supported for AA in ClonalPositionalPlot.") + } + } else if (method %in% c("shannon", "inv.simpson", "norm.entropy")) { + data <- positionalEntropy(data, chain = chain, aa.length = aa_length, group.by = ".group", + method = method, exportTable = TRUE) %>% + separate("Var1", into = all_groupings, sep = " // ") %>% + rename(Position = "Var2") %>% + unite(".group", !!!syms(group_by), sep = group_by_sep) + + group_by <- paste(group_by, sep = group_by_sep) + data <- rename(data, !!sym(group_by) := ".group") + + if (plot_type == "bar") { + if (!is.null(facet_by)) { + stop("'facet_by' should not be specified for entropy bar plot in ClonalPositionalPlot.") + } + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% element_blank() + + BarPlot(data, x = "Position", y = "value", + xlab = xlab %||% "Position", ylab = ylab %||% method, split_by = split_by, + facet_by = group_by, facet_ncol = facet_ncol %||% 1, facet_nrow = facet_nrow, + x_text_angle = 90, legend.position = "none", facet_args = list(strip.position = "right"), + aspect.ratio = aspect.ratio %||% (2 / aa_length), theme_args = theme_args, ... + ) + } else if (plot_type == "line") { + LinePlot(data, x = "Position", y = "value", group_by = group_by, pt_size = 2, + xlab = xlab %||% "Position", ylab = ylab %||% method, split_by = split_by, + facet_by = facet_by, facet_ncol = facet_ncol, facet_nrow = facet_nrow, x_text_angle = 90, + facet_args = list(strip.position = "right"), aspect.ratio = aspect.ratio %||% (6 / aa_length), + theme_args = theme_args, ... + ) + } else if (plot_type == "heatmap") { + allgroups <- unique(data[[group_by]]) + data <- data %>% pivot_wider(names_from = group_by, values_from = "value") + + Heatmap(data, columns_by = "Position", rows = allgroups, rows_name = group_by, + name = method, cluster_columns = FALSE, show_column_names = TRUE, show_row_names = TRUE, + ...) + } else if (plot_type == "box") { + BoxPlot(data, x = "Position", y = "value", xlab = xlab %||% "Position", + ylab = ylab %||% method, split_by = split_by, group_by = group_by, facet_ncol = facet_ncol, + facet_nrow = facet_nrow, x_text_angle = 90, theme_args = theme_args, + aspect.ratio = aspect.ratio %||% (10 / aa_length), ... + ) + } else if (plot_type == "violin") { + ViolinPlot(data, x = "Position", y = "value", xlab = xlab %||% "Position", + ylab = ylab %||% method, split_by = split_by, group_by = group_by, facet_ncol = facet_ncol, + facet_nrow = facet_nrow, x_text_angle = 90, theme_args = theme_args, + aspect.ratio = aspect.ratio %||% (10 / aa_length), ... + ) + } + } else { + # https://github.com/ncborcherding/scRepertoire/issues/420 + data <- positionalProperty(data, chain = chain, aa.length = aa_length, group.by = ".group", + method = method)$data %>% + separate("group", into = all_groupings, sep = " // ") %>% + rename(Position = "position") %>% + unite(".group", !!!syms(group_by), sep = group_by_sep) + + group_by <- paste(group_by, sep = group_by_sep) + data <- rename(data, !!sym(group_by) := ".group") + + n_properties <- length(unique(data$property)) + + if (plot_type == "bar") { + if (!is.null(facet_by)) { + stop("'facet_by' should not be specified for property bar plot in ClonalPositionalPlot.") + } + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% element_blank() + + BarPlot(data, x = "Position", y = "mean", + xlab = xlab %||% "Position", ylab = ylab %||% "Mean Values", split_by = split_by, + facet_by = c("property", group_by), facet_ncol = facet_ncol, facet_nrow = facet_nrow %||% n_properties, + x_text_angle = 90, legend.position = "none", + aspect.ratio = aspect.ratio %||% (4 / aa_length), theme_args = theme_args, ... + ) + } else if (plot_type == "line") { + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% element_blank() + LinePlot(data, x = "Position", y = "mean", group_by = group_by, pt_size = 2, + xlab = xlab %||% "Position", ylab = ylab %||% "Mean Values", split_by = split_by, + facet_by = "property", facet_ncol = facet_ncol %||% 1, facet_nrow = facet_nrow, x_text_angle = 90, + facet_args = list(strip.position = "right"), aspect.ratio = aspect.ratio %||% (6 / aa_length), + theme_args = theme_args, ... + ) + } else { + stop("Only 'bar' and 'line' plot types are supported for property in ClonalPositionalPlot.") + } + } +} + +#' ClonalKmerPlot +#' +#' Explore the k-mer frequency of CDR3 sequences. +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param chain The chain to be analyzed. Default is "TRB". +#' @param clone_call The column name of the clone call. Default is "aa". +#' @param k The length of the k-mer. Default is 3. +#' @param top The number of top k-mers to display. Default is 25. +#' @param group_by The variable to group the data by. Default is "Sample". +#' @param group_by_sep The separator to use when combining groupings. Default is "_". +#' @param facet_by A character vector of column names to facet the plots. Default is NULL. +#' @param split_by A character vector of column names to split the plots. Default is NULL. +#' @param plot_type The type of plot to generate. Default is "bar". +#' * "bar": Bar plot. +#' * "line": Line plot. +#' * "heatmap": Heatmap. +#' @param theme_args A list of arguments to be passed to the [ggplot2::theme] function. +#' @param aspect.ratio The aspect ratio of the plot. Default is NULL. +#' @param facet_ncol The number of columns in the facet grid. Default is NULL. +#' @param ... Other arguments passed to the specific plot function. +#' * For "bar", [plotthis::BarPlot] +#' * For "line", [plotthis::LinePlot] +#' * For "heatmap", [plotthis::Heatmap] +#' @return A ggplot object or a list if `combine` is FALSE +#' @export +#' @importFrom tidyr pivot_longer separate unite +#' @importFrom dplyr %>% rename +#' @importFrom scRepertoire percentKmer +#' @importFrom plotthis BarPlot Heatmap +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Subject", +#' variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +#' ) +#' +#' ClonalKmerPlot(data) +#' ClonalKmerPlot(data, group_by = "Type") +#' ClonalKmerPlot(data, group_by = "Type", plot_type = "line") +#' ClonalKmerPlot(data, group_by = "Type", plot_type = "heatmap") +ClonalKmerPlot <- function ( + data, chain = "TRB", clone_call = "aa", k = 3, top = 25, group_by = "Sample", + group_by_sep = "_", facet_by = NULL, split_by = NULL, + plot_type = c("bar", "line", "heatmap"), theme_args = list(), aspect.ratio = NULL, + facet_ncol = NULL, ... +) { + plot_type <- match.arg(plot_type) + all_groupings <- unique(c(group_by, split_by)) + data <- MergeClonalGroupings(data, all_groupings) + + data <- percentKmer(data, chain = chain, cloneCall = clone_call, motif.length = k, + top.motifs = top, group.by = ".group", exportTable = TRUE) + data <- as.data.frame(data) + motifs <- colnames(data) + data$.group <- rownames(data) + data <- data %>% + separate(".group", into = all_groupings, sep = " // ") %>% + unite(".group", !!!syms(group_by), sep = group_by_sep) %>% + rename(!!sym(paste(group_by, sep = group_by_sep)) := ".group") + + group_by <- paste(group_by, sep = group_by_sep) + + if (plot_type == "bar") { + if (!is.null(facet_by)) { + stop("'facet_by' should not be specified in bar ClonalKmerPlot.") + } + + data <- data %>% pivot_longer(cols = motifs, names_to = "Motifs", values_to = "Frequency") + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% element_blank() + + BarPlot(data, x = "Motifs", y = "Frequency", facet_by = group_by, + xlab = "Motifs", ylab = "Frequency", split_by = split_by, + facet_ncol = facet_ncol %||% 1, x_text_angle = 90, facet_args = list(strip.position = "right"), + aspect.ratio = aspect.ratio %||% (4 / length(motifs)), legend.position = "none", + theme_args = theme_args, ... + ) + } else if (plot_type == "line") { + data <- data %>% pivot_longer(cols = motifs, names_to = "Motifs", values_to = "Frequency") + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% element_blank() + + LinePlot(data, x = "Motifs", y = "Frequency", group_by = group_by, pt_size = 2, + xlab = "Motifs", ylab = "Frequency", split_by = split_by, facet_by = facet_by, + facet_ncol = facet_ncol, x_text_angle = 90, + aspect.ratio = aspect.ratio %||% (8 / length(motifs)), theme_args = theme_args, ... + ) + } else if (plot_type == "heatmap") { + Heatmap(data, columns_by = group_by, rows = motifs, rows_name = "Motifs", name = "Frequency", + ...) + } +} diff --git a/R/clonalstatplot.R b/R/clonalstatplot.R new file mode 100644 index 0000000..1e23032 --- /dev/null +++ b/R/clonalstatplot.R @@ -0,0 +1,1168 @@ +#' MergeClonalGroupings +#' +#' @description Merge the multiple clonal groupings into a single grouping. +#' @details Because [scRepertoire::clonalQuant] and families don't support mutliple groupings, +#' this is trying to merge the multiple groupings into a single grouping. And then +#' later restore the original groupings. +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param groupings A list of the clonal groupings. Each element is a column in the data. +#' @return The data with the combined groupings (`.group`) +#' @importFrom rlang syms +#' @importFrom tidyr unite +#' @importFrom scRepertoire addVariable +#' @keywords internal +MergeClonalGroupings <- function(data, groupings, sep = " // ") { + if (inherits(data, "Seurat")) { + if (!"Sample" %in% colnames(data@meta.data)) { + warning("The 'Sample' column is not found in the meta data, 'orig.indent' will be used instead.") + data$Sample <- data$orig.ident + } + samples <- unique(data$Sample) + # combine Sample and group_by so that the count/frequency is calculated for each + # combined group + data@meta.data <- unite(data@meta.data, ".group", !!!syms(groupings), sep = sep, remove = FALSE) + } else { + samples <- names(data) + data <- addVariable(data, variable.name = "Sample", variables = samples) + # combine Sample and group_by so that the count/frequency is calculated for each + # combined group + vs <- sapply(samples, function(s) { + paste0(sapply(groupings, function(group) { + data[[s]][, group, drop = TRUE][1] + }), collapse = sep) + }) + data <- addVariable(data, variable.name = ".group", variables = vs) + } + + data +} + +#' ClonalVolumePlot +#' +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' in the data +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param scale Whether to use clone proportion or clone size for the plot. +#' @param plot_type The type of plot to use. Default is "bar". +#' Possible values are "bar", "box", and "violin". +#' When "box" or "violin" is used, the data will be broken down by the Sample and plotted +#' for each group. +#' @param x The column name in the meta data to use as the x-axis. Default: "Sample" +#' @param ylab The y-axis label. +#' @param group_by The column name in the meta data to group the cells. Default: NULL +#' @param facet_by The column name in the meta data to facet the plots. Default: NULL +#' @param split_by The column name in the meta data to split the plots. Default: NULL +#' @param order The order of the x-axis items or groups. Default is an empty list. +#' It should be a list of values. The names are the column names, and the values are the order. +#' @param ... Other arguments passed to the specific plot function. +#' * For `bar` plot, see [plotthis::BarPlot]. +#' * For `box` plot, see [plotthis::BoxPlot]. +#' * For `violin` plot, see [plotthis::ViolinPlot]. +#' @return A ggplot object or a list if `combine` is FALSE +#' @importFrom tidyr separate +#' @importFrom plotthis BarPlot BoxPlot ViolinPlot +#' @importFrom scRepertoire clonalQuant +#' @export +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = sample(c("B", "L"), 8, replace = TRUE) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Sex", +#' variables = sample(c("M", "F"), 8, replace = TRUE) +#' ) +#' +#' ClonalVolumePlot(data) +#' ClonalVolumePlot(data, x = "Type") +#' ClonalVolumePlot(data, x = "Type", order = list(Type = c("L", "B"))) +#' ClonalVolumePlot(data, x = c("Type", "Sex"), scale = TRUE) +#' ClonalVolumePlot(data, x = "Type", group_by = "Sex", position = "stack") +#' ClonalVolumePlot(data, +#' plot_type = "box", x = "Type", comparisons = TRUE, +#' group_by = "Sex" +#' ) +#' ClonalVolumePlot(data, plot_type = "violin", x = "Type", add_box = TRUE) +#' +#' # on a Seurat object +#' data(scRep_example, package = "scRepertoire") +#' data(contig_list, package = "scRepertoire") +#' combined <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B", "P19L", "P20B", "P20L") +#' ) +#' sobj <- scRepertoire::combineExpression(combined, scRep_example) +#' ClonalVolumePlot(sobj) +#' ClonalVolumePlot(sobj, x = "seurat_clusters") +#' ClonalVolumePlot(sobj, group_by = "seurat_clusters") +#' ClonalVolumePlot(sobj, x = "seurat_clusters", plot_type = "box") +ClonalVolumePlot <- function( + data, clone_call = "aa", chain = "both", scale = FALSE, + plot_type = c("bar", "box", "violin"), x = "Sample", group_by = NULL, + facet_by = NULL, split_by = NULL, order = list(), ylab = NULL, ...) { + plot_type <- match.arg(plot_type) + if (plot_type %in% c("box", "violin")) { + all_groupings <- unique(c("Sample", x, group_by, facet_by, split_by)) + } else { + all_groupings <- unique(c(x, group_by, facet_by, split_by)) + } + data <- MergeClonalGroupings(data, all_groupings) + data <- clonalQuant(data, + cloneCall = clone_call, chain = chain, scale = scale, + group.by = ".group", exportTable = TRUE + ) + # restore the groups + data <- separate(data, ".group", into = all_groupings, sep = " // ") + for (group in all_groupings) { + if (!is.null(order[[group]])) { + data[[group]] <- factor(data[[group]], levels = order[[group]]) + } + } + if (scale) { + data$scaled <- data$scaled / 100 + } + + if (plot_type == "bar") { + BarPlot(data, + x = x, y = ifelse(scale, "scaled", "contigs"), + group_by = group_by, facet_by = facet_by, split_by = split_by, + ylab = ylab %||% ifelse(scale, "Fraction of Unique Clones", "Number of Unique Clones"), + ... + ) + } else if (plot_type == "box") { + BoxPlot(data, + x = x, y = ifelse(scale, "scaled", "contigs"), group_by = group_by, + facet_by = facet_by, split_by = split_by, + ylab = ylab %||% ifelse(scale, "Fraction of Unique Clones", "Number of Unique Clones"), + ... + ) + } else { + ViolinPlot(data, + x = x, y = ifelse(scale, "scaled", "contigs"), + group_by = group_by, facet_by = facet_by, split_by = split_by, + ylab = ylab %||% ifelse(scale, "Fraction of Unique Clones", "Number of Unique Clones"), + ... + ) + } +} + + +#' ClonalAbundancePlot +#' +#' @description Plot the count or density of the clones at different abundance levels. +#' +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' in the data +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param plot_type The type of plot to use. Default is "trend". +#' Possible values are "trend", "histogram" and "density". +#' @param trend_skip_zero Whether to skip the zero values in the trend line. Default is TRUE. +#' @param binwidth The binwidth for the histogram plot. Default is 0.1. +#' @param group_by The column name in the meta data to group the cells. Default: "Sample" +#' @param group_by_sep The separator to use when combining the group_by columns. Default: "_" +#' @param facet_by The column name in the meta data to facet the plots. Default: NULL +#' @param split_by The column name in the meta data to split the plots. Default: NULL +#' @param order The order of the x-axis items or groups. Default is an empty list. +#' It should be a list of values. The names are the column names, and the values are the order. +#' @param bw The smoothing bandwidth to be used for density plots. Default is 0.5. +#' @param xlab The x-axis label. Default is "Abundance". +#' @param ylab The y-axis label. Default is "Number of Clones" for trend and histogram, and +#' "Density of Clones" for density. +#' @param xtrans The transformation to apply to the x-axis. Default is "log10". +#' @param ytrans The transformation to apply to the y-axis. Default is "identity". +#' @param theme_args The theme arguments to be passed to the plot function. +#' @param ... Other arguments passed to the specific plot function. +#' * For `trend` plot, see [plotthis::Histogram]. +#' * For `histogram` plot, see [plotthis::Histogram]. +#' * For `density` plot, see [plotthis::DensityPlot]. +#' @return A ggplot object or a list if `combine` is FALSE +#' @importFrom tidyr full_seq complete +#' @importFrom dplyr group_modify +#' @importFrom plotthis Histogram DensityPlot +#' @importFrom scRepertoire clonalAbundance +#' @export +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = sample(c("B", "L"), 8, replace = TRUE) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Sex", +#' variables = sample(c("M", "F"), 8, replace = TRUE) +#' ) +#' +#' ClonalAbundancePlot(data) +#' ClonalAbundancePlot(data, ytrans = "log10") +#' ClonalAbundancePlot(data, plot_type = "histogram") +#' ClonalAbundancePlot(data, plot_type = "histogram", add_trend = TRUE, trend_skip_zero = TRUE) +#' ClonalAbundancePlot(data, plot_type = "density") +ClonalAbundancePlot <- function( + data, clone_call = "aa", chain = "both", xtrans = "log10", ytrans = "identity", + plot_type = c("trend", "histogram", "density"), binwidth = 0.1, trend_skip_zero = TRUE, + bw = 0.5, group_by = "Sample", group_by_sep = "_", facet_by = NULL, split_by = NULL, + order = list(), xlab = "Abundance", ylab = NULL, theme_args = list(), ...) { + plot_type <- match.arg(plot_type) + + all_groupings <- unique(c(group_by, facet_by, split_by)) + data <- MergeClonalGroupings(data, all_groupings) + if (length(all_groupings) > 0) { + data <- clonalAbundance(data, + cloneCall = clone_call, chain = chain, + group.by = ".group", exportTable = TRUE + ) + + # restore the groups + data <- separate(data, ".group", into = all_groupings, sep = " // ") + for (group in all_groupings) { + if (!is.null(order[[group]])) { + data[[group]] <- factor(data[[group]], levels = order[[group]]) + } + } + } else { + data <- clonalAbundance(data, + cloneCall = clone_call, chain = chain, + group.by = group_by, exportTable = TRUE + ) + } + + if (plot_type == "trend") { + theme_args$panel.grid.major.y <- theme_args$panel.grid.major.y %||% + element_line(color = "grey", linetype = 2) + Histogram(data, + x = "Abundance", group_by = group_by, group_by_sep = group_by_sep, + facet_by = facet_by, split_by = split_by, xtrans = xtrans, xlab = xlab, binwidth = binwidth, + ytrans = ytrans, ylab = ylab %||% "Number of Clones", use_trend = TRUE, + trend_skip_zero = trend_skip_zero, theme_args = theme_args, ... + ) + } else if (plot_type == "histogram") { + Histogram(data, + x = "Abundance", group_by = group_by, group_by_sep = group_by_sep, + facet_by = facet_by, split_by = split_by, xtrans = xtrans, xlab = xlab, binwidth = binwidth, + trend_skip_zero = trend_skip_zero, ytrans = ytrans, theme_args = theme_args, + ylab = ylab %||% "Number of Clones", ... + ) + } else if (plot_type == "density") { + DensityPlot(data, + x = "Abundance", group_by = group_by, group_by_sep = group_by_sep, + facet_by = facet_by, split_by = split_by, xtrans = xtrans, xlab = xlab, + ytrans = ytrans, ylab = ylab %||% "Density of Clones", theme_args = theme_args, + bw = bw, ... + ) + } +} + +#' ClonalLengthPlot +#' +#' @description Plot the length distribution of the CDR3 sequences +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - only "nt" or "aa" is supported. +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRB", "TRD", "TRG", "IGH", or "IGL" to specify a specific chain. +#' @param plot_type The type of plot to use. Default is "bar". +#' Possible values are "histogram" and "density". +#' @param x_nbreaks The number of breaks for the x-axis. Default is 10. +#' @param group_by The column name in the meta data to group the cells. Default: "Sample" +#' @param facet_by The column name in the meta data to facet the plots. Default: NULL +#' @param split_by The column name in the meta data to split the plots. Default: NULL +#' @param order The order of the groups. Default is an empty list. +#' It should be a list of values. The names are the column names, and the values are the order. +#' @param xlab The x-axis label. +#' @param ylab The y-axis label. +#' @param position The position of the bars for bar plot on the x-axis. Default is "dodge". +#' @param ... Other arguments passed to the specific plot function. +#' * For +#' @return A ggplot object or a list if `combine` is FALSE +#' @importFrom stats quantile +#' @importFrom rlang syms +#' @importFrom dplyr summarise n +#' @importFrom tidyr separate +#' @importFrom ggplot2 element_blank scale_x_discrete element_line +#' @importFrom plotthis BarPlot +#' @importFrom scRepertoire clonalLength +#' @export +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list) +#' data <- scRepertoire::addVariable(data, variable.name = "Type", +#' variables = sample(c("B", "L"), 8, replace = TRUE)) +#' data <- scRepertoire::addVariable(data, variable.name = "Sex", +#' variables = sample(c("M", "F"), 8, replace = TRUE)) +#' +#' ClonalLengthPlot(data) +#' ClonalLengthPlot(data, plot_type = "box") +#' ClonalLengthPlot(data, clone_call = "nt", plot_type = "violin", chain = "TRB", +#' group_by = "Type", comparisons = TRUE) +#' ClonalLengthPlot(data, plot_type = "density", chain = "TRA") +ClonalLengthPlot <- function( + data, clone_call = "aa", chain = "both", plot_type = c("bar", "box", "violin", "density"), + x_nbreaks = 10, group_by = "Sample", order = list(), xlab = "Length", ylab = NULL, + position = "dodge", facet_by = NULL, split_by = NULL, ...) { + plot_type <- match.arg(plot_type) + + if (plot_type %in% c("box", "violin")) { + all_groupings <- unique(c("Sample", group_by, facet_by, split_by)) + } else { + all_groupings <- unique(c(group_by, facet_by, split_by)) + } + data <- MergeClonalGroupings(data, all_groupings) + + if (identical(all_groupings, "Sample")) { + data <- clonalLength(data, cloneCall = clone_call, chain = chain, exportTable = TRUE) + data$Sample <- data$values + } else { + data <- clonalLength(data, + cloneCall = clone_call, chain = chain, group.by = ".group", + exportTable = TRUE + ) + data <- separate(data, ".group", into = all_groupings, sep = " // ") + } + + # restore the groups + for (group in all_groupings) { + if (!is.null(order[[group]])) { + data[[group]] <- factor(data[[group]], levels = order[[group]]) + } + } + + if (plot_type == "density") { + rawdata <- data + } + data <- data %>% + dplyr::group_by(!!!syms(c("length", all_groupings))) %>% + summarise(.n = n(), .groups = "drop") + + default_ylab <- ifelse(clone_call == "aa", "Number of CDR3 (AA)", "Number of CDR3 (NT)") + if (plot_type == "bar") { + args <- list(...) + args$data <- data + args$x <- "length" + args$y <- ".n" + args$group_by <- group_by + args$facet_by <- facet_by + args$split_by <- split_by + args$position <- position + args$xlab <- "Length" + args$ylab <- ylab %||% default_ylab + args$theme_args <- args$theme_args %||% list() + args$theme_args$panel.grid.major.x <- element_blank() + breaks <- quantile(data$length, probs = seq(0, 1, length.out = x_nbreaks), type = 3) + if (is.null(args$split_by)) { + suppressMessages({ + do.call(BarPlot, args) + scale_x_discrete(breaks = breaks) + }) + } else { + suppressMessages({ + do.call(BarPlot, args) & scale_x_discrete(breaks = breaks) + }) + } + } else if (plot_type == "box") { + args <- list(...) + args$data <- data + args$x <- "length" + args$y <- ".n" + args$group_by <- if (identical(group_by, "Sample")) NULL else group_by + args$facet_by <- facet_by + args$split_by <- split_by + args$xlab <- "Length" + args$ylab <- ylab %||% default_ylab + args$theme_args <- args$theme_args %||% list() + if (clone_call == "nt" || chain == "both") { + args$theme_args$panel.grid.major.x <- args$theme_args$panel.grid.major.x %||% element_blank() + } + args$theme_args$panel.grid.major.y <- args$theme_args$panel.grid.major.y %||% + element_line(color = "grey", linetype = 2) + args$legend.position <- args$legend.position %||% ifelse(is.null(args$split_by), "none", "right") + do.call(BoxPlot, args) + } else if (plot_type == "violin") { + args <- list(...) + args$data <- data + args$x <- "length" + args$y <- ".n" + args$group_by <- if (identical(group_by, "Sample")) NULL else group_by + args$facet_by <- facet_by + args$split_by <- split_by + args$xlab <- "Length" + args$ylab <- ylab %||% default_ylab + args$theme_args <- args$theme_args %||% list() + if (clone_call == "nt" || chain == "both") { + args$theme_args$panel.grid.major.x <- args$theme_args$panel.grid.major.x %||% element_blank() + } + args$theme_args$panel.grid.major.y <- args$theme_args$panel.grid.major.y %||% + element_line(color = "grey", linetype = 2) + args$legend.position <- args$legend.position %||% ifelse(is.null(args$split_by), "none", "right") + do.call(ViolinPlot, args) + } else if (plot_type == "density") { + DensityPlot( + rawdata, + x = "length", + group_by = group_by, + facet_by = facet_by, + split_by = split_by, + xlab = xlab %||% "Length", + ylab = ylab %||% ifelse(clone_call == "aa", "Density of CDR3 (AA)", "Density of CDR3 (NT)"), + ... + ) + } +} + +#' ClonalSizeData +#' +#' @description Function to get the clonal size data for all group_by values. +#' +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' in the data +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param groupings The column names in the meta data to group the cells. +#' @keywords internal +#' @importFrom dplyr distinct filter +#' @importFrom tidyr pivot_longer +ClonalSizeData <- function(data, clone_call, chain, groupings) { + data <- MergeClonalGroupings(data, groupings) + + if (inherits(data, "Seurat")) { + all_gvalues <- unique(data@meta.data$.group) + } else { + # clonalScatter only returns data for each sample + # need to re-organize the data to get the data for each group + all_gvalues <- unique(sapply(data, function(x) x$.group[1])) + newdata <- list() + for (d in data) { + nd <- split(d, d$.group) + for (gn in names(nd)) { + if (is.null(newdata[[gn]])) { + newdata[[gn]] <- nd[[gn]] + } else { + newdata[[gn]] <- rbind(newdata[[gn]], nd[[gn]]) + } + } + } + data <- newdata + } + + gv_pairs <- as.list(as.data.frame(combn(all_gvalues, 2, simplify = TRUE))) + do.call(rbind, lapply(gv_pairs, function(gv) { + d <- clonalScatter(data, + cloneCall = clone_call, chain = chain, + x.axis = gv[1], y.axis = gv[2], exportTable = TRUE + ) + d$class <- NULL + d$sum <- NULL + d$size <- NULL + names(d)[2:3] <- paste(names(d)[2:3], "count", sep = ".") + d <- pivot_longer( + d, + cols = -"Var1", + names_to = c(".group", ".value"), + names_sep = "\\." + ) + d + })) %>% + distinct(!!sym("Var1"), !!sym(".group"), .keep_all = TRUE) %>% + separate(".group", into = groupings, sep = " // ") %>% + filter(!!sym("count") > 0) +} + +#' DummyClonalScatterPlot +#' +#' @description Function to plot the scatter plot of the clonal data for a dummy group pair. +#' @param df The data frame with the clonal data. +#' The data frame should have the columns: group_by, 'count', 'fraction'. +#' @return A ggplot object +#' @importFrom rlang sym +#' @importFrom stats cor.test +#' @importFrom circlize colorRamp2 +#' @importFrom dplyr case_when mutate distinct summarise group_by first +#' @importFrom ggplot2 geom_segment aes element_blank theme scale_x_continuous scale_y_continuous +#' @importFrom ggplot2 guides geom_point scale_fill_manual guide_legend +#' @importFrom ggnewscale new_scale_fill +#' @importFrom plotthis ScatterPlot palette_this +#' @keywords internal +DummyClonalScatterPlot <- function(df, title, group_by, scatter_cor, size_by, ...) { + if (nrow(df) == 0) { + stop("No data found for the group_by: ", group_by, + ". Did you specify the correct 'group_by'/'groups' parameters?") + } + pair <- unique(as.character(df[[group_by]])) + if (length(pair) != 2) { + stop("The group_by should have exactly 2 unique values.") + } + + exponent <- function(x) { floor(log10(abs(x))) } + + mantissa <- function(x) { + mant <- log10(abs(x)) + 10 ^ (mant - floor(mant)) + } + + df <- pivot_wider(df, names_from = group_by, values_from = c("count", "fraction"), values_fill = 0) + suf1 <- paste0("count_", pair[1]) + suf2 <- paste0("count_", pair[2]) + df <- df[df[[suf1]] > 0 | df[[suf2]] > 0, , drop = FALSE] + N <- nrow(df) + dual <- which(df[[suf1]] > 0 & df[[suf2]] > 0) + if (length(dual) <= 2) { + test <- list(estimate = NA, p.value = NA) + } else { + test <- cor.test(log(df[[suf1]][dual]), log(df[[suf2]][dual]), method = scatter_cor) + } + sum_counts1 <- sum(df[[suf1]]) + sum_counts2 <- sum(df[[suf2]]) + + counts1_norm <- jitter(1 + df[[suf1]], amount = 0.25) / sum_counts1 + counts2_norm <- jitter(1 + df[[suf2]], amount = 0.25) / sum_counts2 + + # Avoid some points always overlaying each other + oo <- sample(length(counts1_norm)) + plotdata <- data.frame(x = counts1_norm[oo], y = counts2_norm[oo]) + names(plotdata) <- pair + + n_singlet1 <- sum(df[[suf1]] == 1 & df[[suf2]] == 0) + n_singlet2 <- sum(df[[suf1]] == 0 & df[[suf2]] == 1) + n_expanded1 <- sum(df[[suf1]] > 1 & df[[suf2]] == 0) + n_expanded2 <- sum(df[[suf1]] == 0 & df[[suf2]] > 1) + n_dual1 <- sum(df[[suf1]] > 0 & df[[suf2]] > 0 & df[[suf1]] > df[[suf2]]) + n_dual2 <- sum(df[[suf1]] > 0 & df[[suf2]] > 0 & df[[suf1]] < df[[suf2]]) + n_dual <- sum(df[[suf1]] > 0 & df[[suf2]] > 0 & df[[suf1]] == df[[suf2]]) + + labels <- c( + paste0(pair[1], " Singlet (", n_singlet1, ")"), + paste0(pair[1], " Expanded (", n_expanded1, ")"), + paste0(pair[2], " Singlet (", n_singlet2, ")"), + paste0(pair[2], " Expanded (", n_expanded2, ")"), + paste0("Dual (", pair[2], " < ", pair[1], ") (", n_dual1, ")"), + paste0("Dual (", pair[2], " > ", pair[1], ") (", n_dual2, ")"), + paste0("Dual (Equal) (", n_dual, ")") + ) + + plotdata <- mutate( + plotdata, + Type = case_when( + df[[suf1]][oo] == 1 & df[[suf2]][oo] == 0 ~ 1, + df[[suf1]][oo] > 1 & df[[suf2]][oo] == 0 ~ 3, + df[[suf1]][oo] == 0 & df[[suf2]][oo] > 1 ~ 9, + df[[suf1]][oo] == 0 & df[[suf2]][oo] == 1 ~ 11, + df[[suf1]][oo] > df[[suf2]][oo] ~ 5, + df[[suf1]][oo] < df[[suf2]][oo] ~ 7, + TRUE ~ 6 + ), + TypeName = case_when( + Type == 1 ~ labels[1], + Type == 3 ~ labels[2], + Type == 9 ~ labels[4], + Type == 11 ~ labels[3], + Type == 5 ~ labels[5], + Type == 7 ~ labels[6], + TRUE ~ labels[7] + ), + Max_Size = pmax(df[[suf1]][oo], df[[suf2]][oo]), + Total_Size = df[[suf1]][oo] + df[[suf2]][oo], + # Make sure color similar in each category + NumType = !!sym("Type") + scales::rescale(!!sym("Total_Size"), to = c(0, 1)) + ) + + xbreaks <- c( + 1 / sum_counts1, + 0.001 + 1 / sum_counts1, + 0.01 + 1 / sum_counts1, + 0.1 + 1 / sum_counts1 + ) + ybreaks <- c( + 1 / sum_counts2, + 0.001 + 1 / sum_counts2, + 0.01 + 1 / sum_counts2, + 0.1 + 1 / sum_counts2 + ) + + minx <- min(plotdata[[pair[1]]], na.rm = TRUE) + miny <- min(plotdata[[pair[1]]], na.rm = TRUE) + maxx <- max(plotdata[[pair[2]]], na.rm = TRUE) + maxy <- max(plotdata[[pair[2]]], na.rm = TRUE) + + n_formatted <- formatC(length(oo), format = "f", big.mark = ",", digits = 0) + r_formatted <- format(test$estimate, digits = 2, scientific = F) + if (is.na(test$p.value)) { + subtitle <- bquote( + italic(N) == .(N) ~ ~ italic(N)[dual] == .(length(dual)) ~ ~ italic(r) == + .(r_formatted) ~ ~ italic(p) == "NA" + ) + } else if (test$p.value < 1e-4) { + P_mant <- format(mantissa(test$p.value), digits = 2) + P_exp <- exponent(test$p.value) + subtitle <- bquote( + italic(N) == .(N) ~ ~ italic(N)[dual] == .(length(dual)) ~ ~ italic(r) == + .(r_formatted) ~ ~ italic(p) == .(P_mant) %*% 10^.(P_exp) + ) + } else { + P_formatted <- format(test$p.value, digits = 2) + subtitle <- bquote( + italic(N) == .(N) ~ ~ italic(N)[dual] == .(length(dual)) ~ ~ italic(r) == + .(r_formatted) ~ ~ italic(p) == .(P_formatted) + ) + } + + colfun <- colorRamp2( + seq(min(plotdata$NumType, na.rm = TRUE), max(plotdata$NumType, na.rm = TRUE), length.out = 100), + palette_this(palette = "Spectral", palcolor = NULL) + ) + label_df <- plotdata %>% group_by(!!sym("TypeName")) %>% + summarise( + x = first(!!sym(pair[1])), + y = first(!!sym(pair[2])), + Type = mean(!!sym("Type"), na.rm = TRUE), .groups = "drop") + label_df$TypeName <- factor(label_df$TypeName, levels = labels) + label_df <- label_df[order(label_df$TypeName), , drop = FALSE] + + p <- ScatterPlot( + plotdata, x = pair[1], y = pair[2], color_by = "NumType", + title = if (identical(title, "...")) NULL else title, subtitle = subtitle, + border_color = TRUE, + size_by = ifelse(size_by == "max", "Max_Size", "Total_Size"), + size_name = ifelse(size_by == "max", "Max Size", "Total Size"), + aspect.ratio = 1, ...) + + guides(color = "none") + + new_scale_fill() + + geom_point(data = label_df, aes(x = !!sym("x"), y = !!sym("y"), fill = !!sym("TypeName")), shape = 21, alpha = 0) + + scale_fill_manual( + values = colfun(label_df$Type), + breaks = label_df$TypeName, + guide = guide_legend( + title = "Category", + order = 9, + override.aes = list(alpha = 1, color = "grey90", size = 3) + )) + + theme(panel.grid.major = element_blank()) + + geom_segment( + data = data.frame( + # diagnal, horizontal, vertical, horizontal short, vertical short + x = c(1.5 / sum_counts1, minx, 1.5 / sum_counts1, minx, 2.5 / sum_counts1), + xend = c(maxx, maxx, 1.5 / sum_counts1, 1.5 / sum_counts1, 2.5 / sum_counts1), + y = c(1.5 / sum_counts2, 1.5 / sum_counts2, miny, 2.5 / sum_counts2, miny), + yend = c(maxy, 1.5 / sum_counts2, maxy, 2.5 / sum_counts2, 1.5 / sum_counts2) + ), + aes(x = !!sym("x"), y = !!sym("y"), xend = !!sym("xend"), yend = !!sym("yend")), color = "gray90" + ) + + # remove the x, y scales + p$scales$scales[[4]] <- NULL + p$scales$scales[[4]] <- NULL # list shrinks after removing one element + p + scale_x_continuous(trans = "log2", limits = c(minx, maxx), breaks = xbreaks, + labels = c("0", "0.001", "0.01", "0.1")) + + scale_y_continuous(trans = "log2", limits = c(miny, maxy), breaks = ybreaks, + labels = c("0", "0.001", "0.01", "0.1")) +} + +#' ClonalResidencyPlot +#' +#' @description Plot the residency of the clones in different samples. +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' in the data +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param plot_type The type of plot to use. Default is "scatter". +#' Possible values are "scatter", "venn", and "upset". +#' @param group_by The column name in the meta data to group the cells. Default: "Sample" +#' @param groups The groups to compare. Default is NULL. +#' If NULL, all the groups in `group_by` will be compared. +#' Note that for "scatter" plot, only two groups can be compared. +#' @param facet_by The column name in the meta data to facet the plots. Default: NULL +#' @param split_by The column name in the meta data to split the plots. Default: NULL +#' @param split_by_sep The separator used to concatenate the split_by when multiple columns are used. +#' @param scatter_cor The correlation method to use for the scatter plot. Default is "pearson". +#' @param scatter_size_by The size of the points in the scatter plot. Default is "max". +#' Possible values are "max" and "total". +#' * "max" - The max size of the clone in the two groups. +#' * "total" - The total size of the clone in the two groups. +#' @param order The order of the x-axis items or groups. Default is an empty list. +#' It should be a list of values. The names are the column names, and the values are the order. +#' @param combine Whether to combine the plots into a single plot. Default is TRUE. +#' @param nrow The number of rows in the combined plot. Default is NULL. +#' @param ncol The number of columns in the combined plot. Default is NULL. +#' @param byrow Whether to fill the combined plot by row. Default is TRUE. +#' @param ... Other arguments passed to the specific plot function. +#' * For `scatter` plot, see [plotthis::ScatterPlot]. +#' * For `venn` plot, see [plotthis::VennDiagram]. +#' * For `upset` plot, see [plotthis::UpsetPlot]. +#' @return A ggplot object or a list if `combine` is FALSE +#' @export +#' @importFrom utils combn +#' @importFrom dplyr distinct rename_with select across starts_with ends_with +#' @importFrom tidyr pivot_longer separate pivot_wider unite +#' @importFrom scRepertoire clonalScatter +#' @importFrom plotthis ScatterPlot VennDiagram UpsetPlot +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Subject", +#' variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +#' ) +#' +#' ClonalResidencyPlot(data, groups = c("P18B", "P18L")) +#' ClonalResidencyPlot(data, group_by = "Type", split_by = "Subject") +#' ClonalResidencyPlot(data, plot_type = "venn", groups = c("B", "L"), group_by = "Type", +#' split_by = "Subject") +#' ClonalResidencyPlot(data, plot_type = "upset", groups = c("P18B", "P18L")) +ClonalResidencyPlot <- function( + data, clone_call = "aa", chain = "both", plot_type = c("scatter", "venn", "upset"), + group_by = "Sample", groups = NULL, facet_by = NULL, split_by = NULL, split_by_sep = "_", + scatter_cor = "pearson", scatter_size_by = c("max", "total"), + order = list(), combine = TRUE, nrow = NULL, ncol = NULL, byrow = TRUE, ... +) { + + stopifnot("ClonalResidencyPlot supports only a single group_by column" = length(group_by) == 1) + stopifnot("'facet_by' is not supported for 'ClonalResidencyPlot'" = is.null(facet_by)) + + plot_type <- match.arg(plot_type) + scatter_size_by <- match.arg(scatter_size_by) + + all_groupings <- unique(c(group_by, facet_by, split_by)) + data <- ClonalSizeData(data, clone_call, chain, all_groupings) + + # restore the groups + for (group in all_groupings) { + if (!is.null(order[[group]])) { + data[[group]] <- factor(data[[group]], levels = order[[group]]) + } + } + + groups <- groups %||% unique(data[[group_by]]) + if (plot_type == "scatter") { + if (!is.null(split_by)) { + data <- unite(data, ".split", split_by, sep = split_by_sep, remove = FALSE) + } else { + data$.split <- "..." + } + data <- split(data, data$.split) + spdata <- list() + if (!is.list(groups)) { + groups <- as.list(as.data.frame(combn(groups, 2, simplify = TRUE))) + } + for (spname in names(data)) { + sdata <- data[[spname]] + for (gp in groups) { + gname <- paste(gp, collapse = " - ") + gname <- ifelse(identical(spname, "..."), gname, paste(spname, gname, sep = ": ")) + spdata[[gname]] <- sdata[sdata[[group_by]] %in% gp, , drop = FALSE] + } + } + if (is.null(nrow) && is.null(ncol) && isTRUE(byrow) && length(groups) > 1 && length(data) > 1) { + nrow <- length(data) + } + data <- spdata + rm(spdata) + } else { + groups <- unique(unlist(groups)) + } + + if (plot_type == "scatter") { + plots <- lapply(names(data), function(nm) { + DummyClonalScatterPlot(data[[nm]], nm, group_by, scatter_cor, scatter_size_by, ...) + }) + + combine_plots(plots, combine = combine, nrow = nrow, ncol = ncol, byrow = byrow) + } else if (plot_type == "venn") { + if (length(groups) > 4) { + stop("Too many groups for venn plot. Please use 'upset' plot instead.") + } + data <- data[data[[group_by]] %in% groups, , drop = FALSE] + data <- data[data$count > 0, , drop = FALSE] + if (!is.null(split_by)) { + data <- unite(data, ".split", split_by, sep = split_by_sep, remove = FALSE) + } + # Calculate the # singlets for each group + label_fun <- function(df) { + label <- c() + for (i in 1:nrow(df)) { + if (grepl("/", df$id[i], fixed = TRUE)) { + label <- c(label, df$count[i]) + } else { + indicator <- data$Var1 %in% df$item[[i]] & + data[[group_by]] == df$name[i] & + data$count == 1 + if (!is.null(split_by)) { + indicator <- indicator & data$.split == df$.split[i] + } + ns <- data[indicator, , drop = FALSE] + label <- c(label, paste0(df$count[i], "\n(singlets: ", nrow(ns), ")")) + } + } + label + } + + VennDiagram(data, + in_form = "long", id_by = "Var1", group_by = group_by, label = label_fun, + split_by = split_by, split_by_sep = split_by_sep, ... + ) + } else if (plot_type == "upset") { + data$fraction <- NULL + data <- data[data[[group_by]] %in% groups, , drop = FALSE] + data <- data[data$count > 0, , drop = FALSE] + data <- data %>% + pivot_wider(names_from = !!sym("group_by"), names_prefix = "count_", values_from = !!sym("count"), values_fill = 0) %>% + mutate(across( + .cols = starts_with("count_"), + .names = "{.col} Singlet", + .fns = ~ .x == 1 + )) %>% + mutate(across( + .cols = starts_with("count_") & !ends_with(" Singlet"), + .names = "{.col} Expanded", + .fns = ~ .x > 1 + )) %>% + select(!starts_with("count_") | ends_with(" Singlet") | ends_with(" Expanded")) %>% + rename_with( + function(x) substring(x, 7), + .cols = starts_with("count_") + ) + + UpsetPlot(data, in_form = "wide", id_by = "Var1", split_by = split_by, split_by_sep = split_by_sep, ...) + } +} + +#' ClonalCompositionPlot +#' +#' @description Plot the composition of the clones in different samples/groups. +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' in the data +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param method The method of plot to use. Default is "homeostasis". +#' Possible values are "homeostasis", "homeo", "rel", "top", and "rare". +#' * "homeostasis" - Plot the homeostasis/relative abundance of the clones. The `clone_split` will +#' be the fraction of the clones in each sample. +#' * "homeo" - Same as "homeostasis". +#' * "rel" - Same as "homeostasis". +#' * "top" - Plot the top clones. The `clone_split` will be indexes to cut the clones. +#' * "rare" - Plot the rare clones. The `clone_split` will be the clone sizes. +#' @param clone_split The split for the clones. Default is NULL. +#' * For "homeostasis", "homeo", "rel" - Default is `list(Rare = 1e-04, Small = 0.001, Medium = 0.01, Large = 0.1, Hyperexpanded = 1)`. +#' * For "top" - Default is `c(10, 100, 1000, 10000, 30000, 100000)`. +#' * For "rare" - Default is `c(1, 3, 10, 30, 100)`. +#' @param scale Whether to scale the number to 1 for each sample. Default is TRUE. +#' @param facet_by The column name in the meta data to facet the plots. Default: NULL +#' @param group_by The column name in the meta data to group the cells. Default: NULL +#' @param split_by The column name in the meta data to split the plots. Default: NULL +#' @param xlab The x-axis label. Default is NULL. +#' @param ylab The y-axis label. Default is NULL. +#' @param plot_type The type of plot to use. Default is "bar". +#' Possible values are "bar", "ring", "box", and "violin". +#' @param order The order of the x-axis items or groups. Default is an empty list. +#' It should be a list of values. The names are the column names, and the values are the order. +#' @param ... Other arguments passed to the specific plot function. +#' * For `bar` plot, see [plotthis::BarPlot]. +#' * For `ring` plot, see [plotthis::RingPlot]. +#' * For `box` plot, see [plotthis::BoxPlot]. +#' * For `violin` plot, see [plotthis::ViolinPlot]. +#' @return A ggplot object or a list if `combine` is FALSE +#' @importFrom dplyr %>% mutate summarise ungroup n +#' @importFrom tidyr pivot_longer separate +#' @importFrom plotthis BarPlot RingPlot BoxPlot ViolinPlot +#' @importFrom scRepertoire clonalHomeostasis clonalProportion +#' @export +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Subject", +#' variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +#' ) +#' +#' ClonalCompositionPlot(data) +#' ClonalCompositionPlot(data, method = "top") +#' ClonalCompositionPlot(data, plot_type = "ring") +#' ClonalCompositionPlot(data, group_by = "Type", plot_type = "box", comparison = TRUE) +#' ClonalCompositionPlot(data, group_by = "Type", plot_type = "violin", add_box = TRUE, +#' add_bg = TRUE) +#' ClonalCompositionPlot(data, method = "rare") +ClonalCompositionPlot <- function( + data, clone_call = "aa", chain = "both", method = c("homeostasis", "homeo", "rel", "top", "rare"), + clone_split = NULL, scale = TRUE, facet_by = NULL, group_by = NULL, split_by = NULL, + xlab = NULL, ylab = NULL, plot_type = c("bar", "ring", "box", "violin"), order = list(), ... +) { + plot_type <- match.arg(plot_type) + method <- match.arg(method) + if (plot_type %in% c("box", "violin") && is.null(group_by)) { + stop("'group_by' must be provided for box/violin ClonalCompositionPlot") + } + + if (is.null(clone_split)) { + if (method %in% c("homeostasis", "homeo", "rel")) { + clone_split <- list(Rare = 1e-04, Small = 0.001, Medium = 0.01, Large = 0.1, Hyperexpanded = 1) + } else if (method == "top") { + # clone indexes + clone_split <- c(10, 100, 1000, 10000, 30000, 100000) + } else { # rare + # clone sizes + clone_split <- c(1, 3, 10, 30, 100) + } + } + all_groupings <- unique(c("Sample", group_by, facet_by, split_by)) + if (method == "homeostasis" || method == "homeo" || method == "rel" || method == "top") { + data <- MergeClonalGroupings(data, all_groupings) + if (method == "top") { + data <- clonalProportion(data, cloneCall = clone_call, chain = chain, + clonalSplit = clone_split, group.by = ".group", exportTable = TRUE) + } else { + data <- clonalHomeostasis(data, cloneCall = clone_call, chain = chain, + cloneSize = clone_split, group.by = ".group", exportTable = TRUE) + } + data <- as.data.frame(data) + data$.group <- rownames(data) + # restore the groups + data <- separate(data, ".group", into = all_groupings, sep = " // ") + data <- pivot_longer(data, cols = -all_groupings, names_to = ".names", values_to = ".values") + # Sample Type .names .values + name_levels <- unique(data$.names) + } else { # rare + data <- ClonalSizeData(data, clone_call, chain, all_groupings) + # Var1 Sample Type count fraction + clone_split <- sort(clone_split) + clone_split <- c(-Inf, clone_split, Inf) + labels <- sapply(1:(length(clone_split) - 1), function(i) { + if (clone_split[i] == -Inf && clone_split[i + 1] == 1) { + "1" + } else if (clone_split[i] == -Inf) { + paste0("[1:", clone_split[i + 1], "]") + } else if (i == length(clone_split) - 1) { + paste0("[", clone_split[i], ":MAX]") + } else { + paste0("[", clone_split[i] + 1, ":", clone_split[i + 1], "]") + } + }) + data$.names <- cut(data$count, breaks = clone_split, labels = labels) + name_levels <- levels(data$.names) + data <- data %>% + dplyr::group_by(!!!syms(setdiff(colnames(data), c("Var1", "count", "fraction")))) %>% + summarise(.values = n(), .groups = "drop") + } + + for (group in all_groupings) { + if (!is.null(order[[group]])) { + data[[group]] <- factor(data[[group]], levels = order[[group]]) + } + } + + if (method == "homeostasis" || method == "homeo" || method == "rel") { + if (plot_type %in% c("bar", "ring") && !is.null(group_by)) { + data <- data %>% + dplyr::group_by(!!!syms(setdiff(colnames(data), c("Sample", ".values")))) %>% + summarise(.values = sum(!!sym(".values")), .groups = "drop") + } + data$.names <- factor(data$.names, levels = name_levels) + + if (plot_type == "bar") { + BarPlot(data, x = group_by %||% "Sample", y = ".values", group_by = ".names", + group_name = "Clonal Group", position = "stack", facet_by = facet_by, split_by = split_by, + xlab = xlab %||% "Sample", ylab = ylab %||% "Relative Abundance", ...) + } else if (plot_type == "ring") { + RingPlot(data, x = group_by %||% "Sample", y = ".values", group_by = ".names", + group_name = "Clonal Group", facet_by = facet_by, split_by = split_by, + xlab = xlab %||% "Sample", ylab = ylab %||% "Relative Abundance", ...) + } else if (plot_type == "box") { + BoxPlot(data, x = ".names", y = ".values", group_by = group_by, + facet_by = facet_by, split_by = split_by, + xlab = xlab %||% "Clonal Group", ylab = ylab %||% "Relative Abundance", ...) + } else { # violin + ViolinPlot(data, x = ".names", y = ".values", group_by = group_by, + facet_by = facet_by, split_by = split_by, + xlab = xlab %||% "Clonal Group", ylab = ylab %||% "Relative Abundance", ...) + } + } else { + group_name <- ifelse(method == "top", "Clonal Indices", "Clonal Sizes") + + if (isTRUE(scale)) { + data <- data %>% + dplyr::group_by(!!!syms(c("Sample", group_by, facet_by, split_by))) %>% + mutate(.values = !!sym(".values") / sum(!!sym(".values"))) + ylab <- ylab %||% "Relative Abundance" + } else { + ylab <- ylab %||% "Abundance" + } + if (plot_type %in% c("bar", "ring") && !is.null(group_by)) { + data <- data %>% + dplyr::group_by(!!!syms(setdiff(colnames(data), c("Sample", ".values")))) %>% + summarise(.values = sum(!!sym(".values")), .groups = "drop") + } + data$.names <- factor(data$.names, levels = name_levels) + + if (plot_type == "bar") { + BarPlot(data, x = group_by %||% "Sample", y = ".values", group_by = ".names", + group_name = group_name, position = "stack", facet_by = facet_by, split_by = split_by, + xlab = xlab, ylab = ylab, ...) + } else if (plot_type == "ring") { + RingPlot(data, x = group_by %||% "Sample", y = ".values", group_by = ".names", + group_name = group_name, facet_by = facet_by, split_by = split_by, + xlab = xlab, ylab = ylab, ...) + } else if (plot_type == "box") { + BoxPlot(data, x = ".names", y = ".values", group_by = group_by, + facet_by = facet_by, split_by = split_by, + xlab = xlab %||% group_name, ylab = ylab, ...) + } else { # violin + ViolinPlot(data, x = ".names", y = ".values", group_by = group_by, + facet_by = facet_by, split_by = split_by, + xlab = xlab %||% group_name, ylab = ylab, ...) + } + } +} + +#' ClonalOverlapPlot +#' +#' @description Plot the overlap of the clones in different samples/groups. +#' @param data The product of [scRepertoire::combineTCR], [scRepertoire::combineTCR], or +#' [scRepertoire::combineExpression]. +#' @param clone_call How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +#' CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +#' in the data +#' @param chain indicate if both or a specific chain should be used - e.g. "both", +#' "TRA", "TRG", "IGH", "IGL" +#' @param group_by The column name in the meta data to group the cells. Default: "Sample" +#' @param group_by_sep The separator used to concatenate the group_by when multiple columns are used. +#' @param full Whether to plot the full heatmap, or just a triangle. Default is TRUE. +#' @param split_by The column name in the meta data to split the plots. Default: NULL +#' @param order The order of the groups. Default is an empty list. +#' It should be a list of values. The names are the column names, and the values are the order. +#' @param method The method to calculate the overlap. Default is "raw". +#' * "overlap" - overlap coefficient +#' * "morisita" - Morisita’s overlap index +#' * "jaccard" - Jaccard index +#' * "cosine" - cosine similarity +#' * "raw" - exact number of overlapping clones +#' See also [scRepertoire::clonalOverlap]. +#' @param palette The color palette to use. Default is "Blues". +#' @param label_accuracy The accuracy of the labels. Default is NULL. +#' If NULL, it will be 1 for "raw" and 0.01 for other methods. +#' @param label_cutoff The cutoff for the labels to show. Default is 1e-3. +#' @param cluster_rows Whether to cluster the rows. Default is FALSE. +#' @param cluster_columns Whether to cluster the columns. Default is FALSE. +#' @param show_row_names Whether to show the row names. Default is TRUE. +#' @param show_column_names Whether to show the column names. Default is TRUE. +#' @param ... Other arguments passed to the specific plot function [plotthis::Heatmap]. +#' @return A ComplexHeatmap object or a list if `combine` is FALSE +#' @importFrom stats as.dist +#' @importFrom rlang syms +#' @importFrom dplyr %>% filter +#' @importFrom tidyr separate pivot_longer unite +#' @importFrom scRepertoire clonalOverlap +#' @export +#' @examples +#' set.seed(8525) +#' data(contig_list, package = "scRepertoire") +#' data <- scRepertoire::combineTCR(contig_list, +#' samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Type", +#' variables = rep(c("B", "L"), 4) +#' ) +#' data <- scRepertoire::addVariable(data, +#' variable.name = "Subject", +#' variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +#' ) +#' +#' ClonalOverlapPlot(data) +#' ClonalOverlapPlot(data, clone_call = "strict", label_cutoff = 0, +#' label_accuracy = 0.001, method = "morisita", full = FALSE) +#' ClonalOverlapPlot(data, group_by = c("Subject", "Type")) +#' ClonalOverlapPlot(data, group_by = "Type", split_by = "Subject") +ClonalOverlapPlot <- function( + data, clone_call = "aa", chain = "both", group_by = "Sample", group_by_sep = "_", full = TRUE, + split_by = NULL, order = list(), method = c("raw", "overlap", "morisita", "jaccard", "cosine"), + palette = "Blues", label_accuracy = NULL, label_cutoff = 1e-3, cluster_rows = FALSE, cluster_columns = FALSE, + show_row_names = TRUE, show_column_names = TRUE, ... +) { + method <- match.arg(method) + + all_groupings <- unique(c(group_by, split_by)) + data <- MergeClonalGroupings(data, all_groupings) + data <- clonalOverlap(data, cloneCall = clone_call, chain = chain, group.by = ".group", + method = method, exportTable = TRUE) + if (isTRUE(full)) { + data[lower.tri(data)] <- data[upper.tri(data)] + } + # B // P17 B // P18 B // P19 + # B // P17 NA 0 0.117 + # B // P18 NA NA 0.001 + # B // P19 NA NA NA + data$.group <- rownames(data) + data <- separate(data, ".group", into = all_groupings, sep = " // ") + data <- data %>% + pivot_longer(cols = -all_groupings, names_to = ".names", values_to = ".values") + + data <- separate(data, ".names", into = paste(".names", all_groupings, sep = "_"), sep = " // ") + if (!is.null(split_by)) { + data <- data %>% + unite(".split", split_by, sep = " // ", remove = FALSE) %>% + unite(".names.split", !!!syms(paste(".names", split_by, sep = "_")), sep = " // ", remove = FALSE) %>% + filter(!!sym(".split") == !!sym(".names.split")) + + data <- data[, setdiff(colnames(data), c(".split", ".names.split", paste(".names", split_by, sep = "_"))), drop = FALSE] + } + + columns_by <- paste(group_by, collapse = group_by_sep) + data <- data %>% + unite("rows", !!!syms(paste(".names", group_by, sep = "_")), sep = group_by_sep) %>% + unite(!!sym(columns_by), !!!syms(group_by), sep = group_by_sep) + rows <- unique(data$rows) + data <- data %>% pivot_wider(names_from = "rows", values_from = ".values", values_fill = 0) + name <- switch(method, + overlap = "Overlap Coefficient", + morisita = "Morisita's Overlap Index", + jaccard = "Jaccard Index", + cosine = "Cosine Similarity", + "# Overlap Clones" + ) + label_accuracy <- label_accuracy %||% ifelse(method == "raw", 1, 0.01) + + clustering_distance <- function(m) { + values <- m[upper.tri(m)] + values <- 1 - scales::rescale(values, to = c(0, 1)) + m[upper.tri(m)] <- values + m[lower.tri(m)] <- m[upper.tri(m)] + diag(m) <- 0 + as.dist(m) + } + + Heatmap(data, rows = rows, columns_by = columns_by, split_by = split_by, + clustering_distance_rows = function(m) { clustering_distance(t(m)) }, + clustering_distance_columns = clustering_distance, label_cutoff = label_cutoff, + rows_name = columns_by, name = name, palette = palette, label_accuracy = label_accuracy, + cluster_rows = cluster_rows, cluster_columns = cluster_columns, cell_type = "label", + show_row_names = show_row_names, show_column_names = show_column_names, ...) +} diff --git a/R/clustreeplot.R b/R/clustreeplot.R index e489d60..e61361d 100644 --- a/R/clustreeplot.R +++ b/R/clustreeplot.R @@ -3,7 +3,7 @@ #' #' @description This function generates a clustree plot from a data frame or a Seurat object. #' @param object The data frame or Seurat object -#' @param ... Other arguments passed to \code{\link{plotthis::ClustreePlot}} +#' @param ... Other arguments passed to [plotthis::ClustreePlot] #' @return A ggplot object or a list if `combine` is FALSE #' @export #' @examples diff --git a/R/data.R b/R/data.R index 07000e1..6cd10f3 100644 --- a/R/data.R +++ b/R/data.R @@ -60,12 +60,15 @@ NULL #' pancreas_sub[["SubCellType"]] <- pancreas_sub[["clusters"]] #' pancreas_sub[["clusters_coarse"]] <- pancreas_sub[["clusters"]] <- NULL #' pancreas_sub[["Phase"]] <- ifelse(pancreas_sub$S_score > pancreas_sub$G2M_score, "S", "G2M") -#' pancreas_sub[["Phase"]][apply(pancreas_sub[[]][, c("S_score", "G2M_score")], 1, max) < 0, ] <- "G1" -#' pancreas_sub[["Phase", drop = TRUE]] <- factor(pancreas_sub[["Phase", drop = TRUE]], levels = c("G1", "S", "G2M")) +#' pancreas_sub[["Phase"]][ +#' apply(pancreas_sub[[]][, c("S_score", "G2M_score")], 1, max) < 0, ] <- "G1" +#' pancreas_sub[["Phase", drop = TRUE]] <- factor(pancreas_sub[["Phase", drop = TRUE]], +#' levels = c("G1", "S", "G2M")) #' pancreas_sub[["PCA"]] <- pancreas_sub[["X_pca"]] #' pancreas_sub[["UMAP"]] <- pancreas_sub[["X_umap"]] #' pancreas_sub[["X_umap"]] <- pancreas_sub[["X_pca"]] <- NULL -#' VariableFeatures(pancreas_sub) <- rownames(pancreas_sub[["RNA"]])[which(pancreas_sub[["RNA"]]@meta.features$highly_variable_genes == "True")] +#' VariableFeatures(pancreas_sub) <- rownames(pancreas_sub[["RNA"]])[ +#' which(pancreas_sub[["RNA"]]@meta.features$highly_variable_genes == "True")] #' pancreas_sub <- NormalizeData(pancreas_sub) #' pancreas_sub <- FindVariableFeatures(pancreas_sub) #' pancreas_sub <- ScaleData(pancreas_sub) @@ -87,3 +90,32 @@ NULL #' } #' @name pancreas_sub NULL + +#' A toy example of CellPhoneDB output from LIANA +#' +#' The dataset is generated using python package LIANA +#' +#' @format A \code{data.frame} with 10 rows and 6 columns +#' @concept data +#' @source \url{https://liana-py.readthedocs.io/en/latest/notebooks/basic_usage.html#Tileplot} +#' @examples +#' \dontrun{ +#' # Python code +#' # # import liana +#' # import liana as li +#' # # needed for visualization and toy data +#' # import scanpy as sc +#' # +#' # from liana.method import cellphonedb +#' # adata = sc.datasets.pbmc68k_reduced() +#' # cellphonedb(adata, +#' # groupby='bulk_labels', +#' # # NOTE by default the resource uses HUMAN gene symbols +#' # resource_name='consensus', +#' # expr_prop=0.1, +#' # verbose=True, key_added='cpdb_res') +#' # cellphonedb_res = adata.uns['cpdb_res'] +#' # cellphonedb_res = cellphonedb_res[cellphonedb_res['cellphone_pvals'] < 0.05] +#' } +#' @name cellphonedb_res +NULL diff --git a/R/enrichmentplot.R b/R/enrichmentplot.R index 85b8bd3..2dc0fbc 100644 --- a/R/enrichmentplot.R +++ b/R/enrichmentplot.R @@ -4,7 +4,7 @@ #' This function generates various types of plots for enrichment (over-representation) analysis. #' #' @param data A data frame with enrichment results generated by \code{clusterProfiler}. -#' If your result is generated by \code{enrichr}, you can use \code{\link{PrepareEnrichrResult}} to convert it to a data frame. +#' If your result is generated by \code{enrichr}, you can use [PrepareEnrichrResult] to convert it to a data frame. #' @param top_term Number of top terms to show in the plot. #' Default is 6 for all plots except "enrichmap" which is 100. #' @param plot_type Type of plot to generate. Options are "bar", "dot", "lollipop", "network", "enrichmap", "wordcloud", "comparison". @@ -29,7 +29,7 @@ #' When the terms are too long, they will be wrapped to fit the width. #' @param expand A numeric vector of length 1, 2 or 4 to expand the plot. Default is NULL. #' Works only for "bar" plot. -#' See also \code{\link{plotthis::BarPlot}}. +#' See also [plotthis::BarPlot]. #' @param word_type The type of word to show in the wordcloud. Options are "term" and "feature". Default is "term". #' Works only for "wordcloud". #' @param split_by A character vector of column names to split the plots. Default is NULL. @@ -41,17 +41,17 @@ #' @param group_by_sep A character to concatenate the group_by columns when there are multiple columns. Default is "_". #' Works only for "comparison" plot. #' @param palette The color palette to use for the plot. Default is "Spectral". -#' See \code{\link{plotthis::show_palettes}} for available palettes. +#' See [plotthis::show_palettes] for available palettes. #' @param xlab The x-axis label. Default is NULL. #' @param ylab The y-axis label. Default is NULL. #' @param ... Other arguments passed to the specific plot function. -#' * For "bar", \code{\link{plotthis::BarPlot}} -#' * For "dot", \code{\link{plotthis::DotPlot}} -#' * For "lollipop", \code{\link{plotthis::LollipopPlot}} -#' * For "network", \code{\link{plotthis::EnrichNetwork}} -#' * For "enrichmap", \code{\link{plotthis::EnrichMap}} -#' * For "wordcloud", \code{\link{plotthis::WordCloudPlot}} -#' * For "comparison", \code{\link{plotthis::DotPlot}} +#' * For "bar", [plotthis::BarPlot] +#' * For "dot", [plotthis::DotPlot] +#' * For "lollipop", [plotthis::LollipopPlot] +#' * For "network", [plotthis::EnrichNetwork] +#' * For "enrichmap", [plotthis::EnrichMap] +#' * For "wordcloud", [plotthis::WordCloudPlot] +#' * For "comparison", [plotthis::DotPlot] #' @importFrom rlang sym syms #' @importFrom stringr str_wrap #' @importFrom dplyr %>% group_by slice_min ungroup @@ -90,18 +90,18 @@ EnrichmentPlot <- function( ) { descr_col <- "Description" plot_type <- match.arg(plot_type) - split_by <- plotthis:::check_columns( + split_by <- check_columns( data, split_by, force_factor = TRUE, allow_multi = TRUE, concat_multi = TRUE, concat_sep = split_by_sep ) - group_by <- plotthis:::check_columns( + group_by <- check_columns( data, group_by, force_factor = TRUE, allow_multi = TRUE, concat_multi = TRUE, concat_sep = group_by_sep ) - facet_by <- plotthis:::check_columns( + facet_by <- check_columns( data, facet_by, force_factor = TRUE, allow_multi = TRUE ) - descr_col <- plotthis:::check_columns(data, descr_col, force_factor = TRUE) + descr_col <- check_columns(data, descr_col, force_factor = TRUE) top_term <- top_term %||% ifelse(plot_type == "enrichmap", 100 , 6) # if (!is.null(cutoff)) { diff --git a/R/featurestatplot.R b/R/featurestatplot.R index 1334baf..24305dc 100644 --- a/R/featurestatplot.R +++ b/R/featurestatplot.R @@ -20,15 +20,15 @@ #' @param ylab The y-axis label. #' @param x_text_angle The angle of the x-axis text. Only used when `plot_type` is "violin", "bar", or "box". #' @param ... Other arguments passed to the plot functions. -#' * For `plot_type` "violin", the arguments are passed to \code{\link{plotthis::ViolinPlot}}. -#' * For `plot_type` "box", the arguments are passed to \code{\link{plotthis::BoxPlot}}. -#' * For `plot_type` "bar", the arguments are passed to \code{\link{plotthis::BarPlot}}. -#' * For `plot_type` "ridge", the arguments are passed to \code{\link{plotthis::RidgePlot}}. -#' * For `plot_type` "dim", the arguments are passed to \code{\link{plotthis::FeatureDimPlot}}. -#' * For `plot_type` "heatmap", the arguments are passed to \code{\link{plotthis::Heatmap}}. -#' * For `plot_type` "cor" with 2 features, the arguments are passed to \code{\link{plotthis::CorPlot}}. -#' * For `plot_type` "cor" with more than 2 features, the arguments are passed to \code{\link{plotthis::CorPairsPlot}}. -#' * For `plot_type` "dot", the arguments are passed to \code{\link{plotthis::Heatmap}} with `cell_type` set to "dot". +#' * For `plot_type` "violin", the arguments are passed to [plotthis::ViolinPlot]. +#' * For `plot_type` "box", the arguments are passed to [plotthis::BoxPlot]. +#' * For `plot_type` "bar", the arguments are passed to [plotthis::BarPlot]. +#' * For `plot_type` "ridge", the arguments are passed to [plotthis::RidgePlot]. +#' * For `plot_type` "dim", the arguments are passed to [plotthis::FeatureDimPlot]. +#' * For `plot_type` "heatmap", the arguments are passed to [plotthis::Heatmap]. +#' * For `plot_type` "cor" with 2 features, the arguments are passed to [plotthis::CorPlot]. +#' * For `plot_type` "cor" with more than 2 features, the arguments are passed to [plotthis::CorPairsPlot]. +#' * For `plot_type` "dot", the arguments are passed to [plotthis::Heatmap] with `cell_type` set to "dot". #' @return A ggplot object or a list if `combine` is FALSE #' @export #' @importFrom rlang %||% syms @@ -253,7 +253,7 @@ FeatureStatPlot <- function( xlab = xlab %||% "", ylab = ylab %||% "", x_text_angle = x_text_angle %||% 45, ...) } else if (plot_type == "bar") { data <- data %>% group_by(!!!syms(unique(c(ident, split_by, ".features")))) %>% - summarise(.value = agg(.value)) + summarise(.value = agg(!!sym(".value"))) BarPlot( data, x = ident, y = ".value", group_by = group_by, split_by = split_by, facet_by = facet_by, xlab = xlab %||% "", ylab = ylab %||% "", x_text_angle = x_text_angle %||% 45, ...) diff --git a/R/utils.R b/R/utils.R new file mode 100644 index 0000000..3a14228 --- /dev/null +++ b/R/utils.R @@ -0,0 +1,19 @@ +#' @keywords internal +#' @importFrom utils getFromNamespace +check_columns <- getFromNamespace("check_columns", "plotthis") + +#' @keywords internal +#' @importFrom utils getFromNamespace +combine_plots <- getFromNamespace("combine_plots", "plotthis") + +# #' Monkey patch a function from a namespace +# #' @keywords internal +# #' @param namespace The namespace where the function is located +# #' @param function_name The name of the function to be patched +# #' @param new_function The new function to be patched +# monkey_patch <- function(namespace, function_name, new_function) { +# ns <- getNamespace(namespace) +# unlockBinding(function_name, ns) +# assign(function_name, new_function, envir = ns) +# lockBinding(function_name, ns) +# } diff --git a/README.md b/README.md index 8f444d3..c885db4 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,12 @@ $ conda install pwwang::r-scplotter ## Credits -`scplotter` is greatly inspired by the [`SCP`][2] package, where the visualization was detached from the analysis and implemented in the [`plotthis`][1] package. The `scplotter` package is built upon the `plotthis` package and provides a set of even higher-level functions to visualize single-cell sequencing data. +`scplotter` draws significant inspiration from the [`SCP`][2] package, which separates visualization from analysis and implements it in the [`plotthis`][1] package. Building on `plotthis`, `scplotter` offers advanced functions for visualizing single-cell sequencing data. Special thanks to the [`scRepertoire`][2] package for its APIs that facilitate the analysis of single-cell TCR/BCR sequencing data. ## Gallery +### scRNA-seq + [`CellDimPlot`][3] ![CellDimPlot](./man/figures/celldimplot.png) @@ -46,6 +48,20 @@ $ conda install pwwang::r-scplotter ![VolcanoPlot](./man/figures/volcanoplot.png) +[`CCCPlot`][10] + +![CCCPlot](./man/figures/cccplot.png) + +### scTCR-seq/scBCR-seq + +[`ClonalVolumePlot`][11] | [`ClonalAbundancePlot`][12] | [`ClonalResidencyPlot`][13] | [`ClonalCompositionPlot`][14] | [`ClonalOverlapPlot`][15] | [`ClonalGeneUsagePlot`][16] + +![clonalstat](./man/figures/clonalstat.png) + +[`ClonalRarefactionPlot`][17] | [`ClonalGeneUsagePlot`][18] | [`ClonalDiversityPlot`][19] | [`ClonalPositionalPlot`][20] + +![clonaldiv](./man/figures/clonaldiv.png) + [1]: https://github.com/pwwang/plotthis [2]: https://zhanghao-njmu.github.io/SCP/index.html [3]: https://pwwang.github.io/scplotter/reference/CellDimPlot.html @@ -55,3 +71,14 @@ $ conda install pwwang::r-scplotter [7]: https://pwwang.github.io/scplotter/reference/EnrichmentPlot.html [8]: https://pwwang.github.io/plotthis/reference/gsea.html [9]: https://pwwang.github.io/plotthis/reference/VolcanoPlot.html +[10]: https://pwwang.github.io/plotthis/reference/CCCPlot.html +[11]: https://pwwang.github.io/scplotter/reference/ClonalVolumePlot.html +[12]: https://pwwang.github.io/scplotter/reference/ClonalAbundancePlot.html +[13]: https://pwwang.github.io/scplotter/reference/ClonalResidencyPlot.html +[14]: https://pwwang.github.io/scplotter/reference/ClonalCompositionPlot.html +[15]: https://pwwang.github.io/scplotter/reference/ClonalOverlapPlot.html +[16]: https://pwwang.github.io/scplotter/reference/ClonalGeneUsagePlot.html +[17]: https://pwwang.github.io/scplotter/reference/ClonalRarefactionPlot.html +[18]: https://pwwang.github.io/scplotter/reference/ClonalGeneUsagePlot.html +[19]: https://pwwang.github.io/scplotter/reference/ClonalDiversityPlot.html +[20]: https://pwwang.github.io/scplotter/reference/ClonalPositionalPlot.html diff --git a/_pkgdown.yml b/_pkgdown.yml index ffa8ce1..8d79ef9 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -3,7 +3,7 @@ template: bootstrap: 5 bootswatch: lumen reference: - - title: scRNA + - title: scRNA-seq desc: Functions for plotting single cell RNA-seq data contents: - CellDimPlot @@ -11,11 +11,27 @@ reference: - ClustreePlot - EnrichmentPlot - FeatureStatPlot + - CCCPlot + - title: scTCR-seq/scBCR-seq + desc: Functions for plotting single cell TCR-seq/BCR-seq data + contents: + - ClonalVolumePlot + - ClonalAbundancePlot + - ClonalLengthPlot + - ClonalResidencyPlot + - ClonalCompositionPlot + - ClonalOverlapPlot + - ClonalDiversityPlot + - ClonalGeneUsagePlot + - ClonalPositionalPlot + - ClonalKmerPlot + - ClonalRarefactionPlot - title: data desc: Built-in data sets contents: - ifnb_sub - pancreas_sub + - cellphonedb_res - title: Re-exports desc: Functions that are re-exported from plotthis contents: diff --git a/conda/meta.yaml b/conda/meta.yaml index b17a4da..4b6f993 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -1,10 +1,10 @@ package: name: r-scplotter - version: 0.0.2 + version: 0.1.0 source: git_url: https://github.com/pwwang/scplotter - git_rev: 0.0.2 # on 10/01/2024 + git_rev: 0.1.0 build: number: 1 @@ -30,6 +30,10 @@ requirements: - r-hexbin - r-igraph - r-scattermore + - r-concaveman + - r-gridgraphics + - r-inext + - r-metap run: - r-base @@ -48,6 +52,10 @@ requirements: - r-hexbin - r-igraph - r-scattermore + - r-concaveman + - r-gridgraphics + - r-inext + - r-metap test: commands: diff --git a/data/cellphonedb_res.rda b/data/cellphonedb_res.rda new file mode 100644 index 0000000..2adb996 Binary files /dev/null and b/data/cellphonedb_res.rda differ diff --git a/data/ifnb_sub.rda b/data/ifnb_sub.rda index c8e276e..6e3fc2b 100644 Binary files a/data/ifnb_sub.rda and b/data/ifnb_sub.rda differ diff --git a/data/pancreas_sub.rda b/data/pancreas_sub.rda index 367b340..0cff018 100644 Binary files a/data/pancreas_sub.rda and b/data/pancreas_sub.rda differ diff --git a/man/CCCPlot.Rd b/man/CCCPlot.Rd new file mode 100644 index 0000000..deb332f --- /dev/null +++ b/man/CCCPlot.Rd @@ -0,0 +1,136 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cccplot.R +\name{CCCPlot} +\alias{CCCPlot} +\title{Cell-Cell Communication Plot} +\usage{ +CCCPlot( + data, + plot_type = c("dot", "network", "chord", "circos", "heatmap", "sankey", "alluvial"), + method = c("aggregation", "interaction"), + magnitude = waiver(), + specificity = waiver(), + weighted = TRUE, + meta_specificity = "sumlog", + split_by = NULL, + x_text_angle = 90, + link_curvature = 0.2, + link_alpha = 0.6, + facet_by = NULL, + show_row_names = TRUE, + show_column_names = TRUE, + ... +) +} +\arguments{ +\item{data}{A data frame with the cell-cell communication data. +A typical data frame should have the following columns: +\itemize{ +\item \code{source} The source cell type. +\item \code{target} The target cell type. +\item \code{ligand} The ligand gene. +\item \code{receptor} The receptor gene. +\item \code{ligand_means} The mean expression of the ligand gene per cell type. +\item \code{receptor_means} The mean expression of the receptor gene per cell type. +\item \code{ligand_props} The proportion of cells that express the entity. +\item \code{receptor_props} The proportion of cells that express the entity. +\item \verb{} The magnitude of the communication. +\item \verb{} The specificity of the communication. +Depends on the \code{plot_type}, some columns are optional. But the \code{source}, \code{target}, +\code{ligand}, \code{receptor} and \verb{} are required. +}} + +\item{plot_type}{The type of plot to use. Default is "dot". +Possible values are "network", "chord", "circos", "heatmap", "sankey", "alluvial", and "dot". +\itemize{ +\item network: A network plot with the source and target cells as the nodes and the communication as the edges. +\item chord: A chord plot with the source and target cells as the nodes and the communication as the chords. +\item circos: Alias of "chord". +\item heatmap: A heatmap plot with the source and target cells as the rows and columns. +\item sankey: A sankey plot with the source and target cells as the nodes and the communication as the flows. +\item alluvial: Alias of "sankey". +\item dot: A dot plot with the source and target cells as the nodes and the communication as the dots. +}} + +\item{method}{The method to determine the plot entities. +\itemize{ +\item aggregation: Aggregate the ligand-receptor pairs interactions for each source-target pair. +Only the source / target pairs will be plotted. +\item interaction: Plot the ligand-receptor pairs interactions directly. +The ligand-receptor pairs will also be plotted. +}} + +\item{magnitude}{The column name in the data to use as the magnitude of the communication. +By default, the second last column will be used. +See \code{li.mt.show_methods()} for the available methods in \code{LIANA}. +or \url{https://liana-py.readthedocs.io/en/latest/notebooks/basic_usage.html#Tileplot}} + +\item{specificity}{The column name in the data to use as the specificity of the communication. +By default, the last column will be used. +If the method doesn't have a specificity, set it to NULL.} + +\item{weighted}{Whether to use the magnitude as the weight of the aggregation interaction strength. +for source-target pairs. Default is TRUE. Otherwise, the number of interactions will be used. +Only used when \code{method} is "aggregation".} + +\item{meta_specificity}{The method to calculate the specificity when there are multiple +ligand-receptor pairs interactions. Default is "sumlog". +It should be one of the methods in the \code{metap} package.} + +\item{split_by}{A character vector of column names to split the plots. Default is NULL.} + +\item{x_text_angle}{The angle of the x-axis text. Default is 90. +Only used when \code{plot_type} is "dot".} + +\item{link_curvature}{The curvature of the links. Default is 0.2. +Only used when \code{plot_type} is "network".} + +\item{link_alpha}{The transparency of the links. Default is 0.6. +Only used when \code{plot_type} is "network".} + +\item{facet_by}{A character vector of column names to facet the plots. Default is NULL. +It should always be NULL.} + +\item{show_row_names}{Whether to show the row names in the heatmap. Default is TRUE. +Only used when \code{plot_type} is "heatmap".} + +\item{show_column_names}{Whether to show the column names in the heatmap. Default is TRUE. +Only used when \code{plot_type} is "heatmap".} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For \code{Network}, see \link[plotthis:Network]{plotthis::Network}. +\item For \code{ChordPlot}, see \link[plotthis:chordplot]{plotthis::ChordPlot}. +\item For \code{Heatmap}, see \link[plotthis:Heatmap]{plotthis::Heatmap}. +\item For \code{SankeyPlot}, see \link[plotthis:sankeyplot]{plotthis::SankeyPlot}. +\item For \code{DotPlot}, see \link[plotthis:dotplot]{plotthis::DotPlot}. +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Plot the cell-cell communication. +See also: +\itemize{ +\item The review: \url{https://www.sciencedirect.com/science/article/pii/S2452310021000081} +\item The LIANA package: \url{https://liana-py.readthedocs.io/en/latest/notebooks/basic_usage.html#Tileplot} +\item The CCPlotR package: \url{https://github.com/Sarah145/CCPlotR} +} +} +\examples{ +set.seed(8525) +data(cellphonedb_res) +CCCPlot(data = cellphonedb_res, plot_type = "network", legend.position = "none", + theme = "theme_blank", theme_args = list(add_coord = FALSE)) +CCCPlot(cellphonedb_res, plot_type = "chord") +CCCPlot(cellphonedb_res, plot_type = "heatmap") +CCCPlot(cellphonedb_res, plot_type = "dot", weighted = FALSE) + +cellphonedb_res_sub <- cellphonedb_res[ + cellphonedb_res$source \%in\% c("Dendritic", "CD14+ Monocyte"),] +CCCPlot(cellphonedb_res_sub, plot_type = "dot", method = "interaction") +CCCPlot(cellphonedb_res_sub, plot_type = "network", method = "interaction", + node_size_by = 1) +CCCPlot(cellphonedb_res_sub, plot_type = "heatmap", method = "interaction") +} diff --git a/man/CellDimPlot.Rd b/man/CellDimPlot.Rd index 03a5231..7018214 100644 --- a/man/CellDimPlot.Rd +++ b/man/CellDimPlot.Rd @@ -13,7 +13,7 @@ CellDimPlot(object, reduction = NULL, graph = NULL, ...) \item{graph}{Specify the graph name to add edges between cell neighbors to the plot.} -\item{...}{Other arguments passed to \code{\link{plotthis::DimPlot}}} +\item{...}{Other arguments passed to \link[plotthis:dimplot]{plotthis::DimPlot}.} } \value{ A ggplot object or a list if \code{combine} is FALSE @@ -68,7 +68,7 @@ CellDimPlot(pancreas_sub, CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", add_mark = TRUE) CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", - add_mark = TRUE, mark_expand = unit(1, "mm")) + add_mark = TRUE, mark_expand = grid::unit(1, "mm")) CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", add_mark = TRUE, mark_alpha = 0.3) CellDimPlot(pancreas_sub, group_by = "SubCellType", reduction = "UMAP", diff --git a/man/CellStatPlot.Rd b/man/CellStatPlot.Rd index 9d41a7f..53e7f4c 100644 --- a/man/CellStatPlot.Rd +++ b/man/CellStatPlot.Rd @@ -18,7 +18,7 @@ CellStatPlot( rows_name = NULL, name = NULL, plot_type = c("bar", "circos", "pie", "pies", "ring", "donut", "trend", "area", - "sankey", "alluvial", "heatmap"), + "sankey", "alluvial", "heatmap", "radar", "spider"), swap = FALSE, ylab = NULL, ... @@ -71,7 +71,7 @@ Only works when \code{group-by} is specified. \item{name}{The name of the 'pies'/'heatmap' plot, shown as the name of the main legend. Default is NULL.} \item{plot_type}{The type of plot to use. Default is "bar". -Possible values are "bar", "circos", "pie", "pies", "ring"/"donut", "trend", "area", "heatmap", and "sankey"/"alluvial". +Possible values are "bar", "circos", "pie", "pies", "ring"/"donut", "trend", "area", "heatmap", "sankey"/"alluvial", "radar" and "spider". 'pie' vs 'pies': 'pie' plot will plot a single pie chart for each group, while 'pies' plot will plot multiple pie charts for each group and split. 'pies' basically is a heatmap with 'cell_type = "pie"'.} @@ -88,15 +88,15 @@ Note that this is different from \code{flip}. \code{flip} is used to transpose t \item{...}{Other arguments passed to the specific plot function. \itemize{ -\item For \code{bar} plot, see \code{\link{plotthis::BarPlot}}. -\item For \code{circos} plot, see \code{\link{plotthis::CircosPlot}}. -\item For \code{pie} chart, see \code{\link{plotthis::PieChart}}. -\item For \code{pies} plot, see \code{\link{plotthis::Heatmap}}. -\item For \code{heatmap} plot, see \code{\link{plotthis::Heatmap}}. -\item For \code{ring}/\code{donut} plot, see \code{\link{plotthis::RingPlot}}. -\item For \code{trend} plot, see \code{\link{plotthis::TrendPlot}}. -\item For \code{area} plot, see \code{\link{plotthis::AreaPlot}}. -\item For \code{sankey}/\code{alluvial} plot, see \code{\link{plotthis::SankeyPlot}}. +\item For \code{bar} plot, see \link[plotthis:barplot]{plotthis::BarPlot}. +\item For \code{circos} plot, see \link[plotthis:chordplot]{plotthis::CircosPlot}. +\item For \code{pie} chart, see \link[plotthis:PieChart]{plotthis::PieChart}. +\item For \code{pies} plot, see \link[plotthis:Heatmap]{plotthis::Heatmap}. +\item For \code{heatmap} plot, see \link[plotthis:Heatmap]{plotthis::Heatmap}. +\item For \code{ring}/\code{donut} plot, see \link[plotthis:RingPlot]{plotthis::RingPlot}. +\item For \code{trend} plot, see \link[plotthis:TrendPlot]{plotthis::TrendPlot}. +\item For \code{area} plot, see \link[plotthis:AreaPlot]{plotthis::AreaPlot}. +\item For \code{sankey}/\code{alluvial} plot, see \link[plotthis:sankeyplot]{plotthis::SankeyPlot}. }} } \value{ @@ -106,7 +106,7 @@ A ggplot object or a list if \code{combine} is FALSE Plot the statistics of the cells. } \examples{ -library(patchwork) +# library(patchwork) data(ifnb_sub) # Bar plot @@ -170,4 +170,9 @@ CellStatPlot(ifnb_sub, plot_type = "pies", group_by = "stim", CellStatPlot(ifnb_sub, plot_type = "heatmap", group_by = "stim", palette = "Blues") CellStatPlot(ifnb_sub, plot_type = "heatmap", group_by = "stim", frac = "group", columns_split_by = "seurat_annotations", swap = TRUE) + +# Radar plot/Spider plot +pr <- CellStatPlot(ifnb_sub, plot_type = "radar", group_by = "stim") +ps <- CellStatPlot(ifnb_sub, plot_type = "spider", group_by = "stim") +pr | ps } diff --git a/man/ClonalAbundancePlot.Rd b/man/ClonalAbundancePlot.Rd new file mode 100644 index 0000000..69ddb2b --- /dev/null +++ b/man/ClonalAbundancePlot.Rd @@ -0,0 +1,101 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{ClonalAbundancePlot} +\alias{ClonalAbundancePlot} +\title{ClonalAbundancePlot} +\usage{ +ClonalAbundancePlot( + data, + clone_call = "aa", + chain = "both", + xtrans = "log10", + ytrans = "identity", + plot_type = c("trend", "histogram", "density"), + binwidth = 0.1, + trend_skip_zero = TRUE, + bw = 0.5, + group_by = "Sample", + group_by_sep = "_", + facet_by = NULL, + split_by = NULL, + order = list(), + xlab = "Abundance", + ylab = NULL, + theme_args = list(), + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +in the data} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{xtrans}{The transformation to apply to the x-axis. Default is "log10".} + +\item{ytrans}{The transformation to apply to the y-axis. Default is "identity".} + +\item{plot_type}{The type of plot to use. Default is "trend". +Possible values are "trend", "histogram" and "density".} + +\item{binwidth}{The binwidth for the histogram plot. Default is 0.1.} + +\item{trend_skip_zero}{Whether to skip the zero values in the trend line. Default is TRUE.} + +\item{bw}{The smoothing bandwidth to be used for density plots. Default is 0.5.} + +\item{group_by}{The column name in the meta data to group the cells. Default: "Sample"} + +\item{group_by_sep}{The separator to use when combining the group_by columns. Default: "_"} + +\item{facet_by}{The column name in the meta data to facet the plots. Default: NULL} + +\item{split_by}{The column name in the meta data to split the plots. Default: NULL} + +\item{order}{The order of the x-axis items or groups. Default is an empty list. +It should be a list of values. The names are the column names, and the values are the order.} + +\item{xlab}{The x-axis label. Default is "Abundance".} + +\item{ylab}{The y-axis label. Default is "Number of Clones" for trend and histogram, and +"Density of Clones" for density.} + +\item{theme_args}{The theme arguments to be passed to the plot function.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For \code{trend} plot, see \link[plotthis:densityhistoplot]{plotthis::Histogram}. +\item For \code{histogram} plot, see \link[plotthis:densityhistoplot]{plotthis::Histogram}. +\item For \code{density} plot, see \link[plotthis:densityhistoplot]{plotthis::DensityPlot}. +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Plot the count or density of the clones at different abundance levels. +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = sample(c("B", "L"), 8, replace = TRUE) +) +data <- scRepertoire::addVariable(data, + variable.name = "Sex", + variables = sample(c("M", "F"), 8, replace = TRUE) +) + +ClonalAbundancePlot(data) +ClonalAbundancePlot(data, ytrans = "log10") +ClonalAbundancePlot(data, plot_type = "histogram") +ClonalAbundancePlot(data, plot_type = "histogram", add_trend = TRUE, trend_skip_zero = TRUE) +ClonalAbundancePlot(data, plot_type = "density") +} diff --git a/man/ClonalCompositionPlot.Rd b/man/ClonalCompositionPlot.Rd new file mode 100644 index 0000000..68f3a62 --- /dev/null +++ b/man/ClonalCompositionPlot.Rd @@ -0,0 +1,106 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{ClonalCompositionPlot} +\alias{ClonalCompositionPlot} +\title{ClonalCompositionPlot} +\usage{ +ClonalCompositionPlot( + data, + clone_call = "aa", + chain = "both", + method = c("homeostasis", "homeo", "rel", "top", "rare"), + clone_split = NULL, + scale = TRUE, + facet_by = NULL, + group_by = NULL, + split_by = NULL, + xlab = NULL, + ylab = NULL, + plot_type = c("bar", "ring", "box", "violin"), + order = list(), + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +in the data} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{method}{The method of plot to use. Default is "homeostasis". +Possible values are "homeostasis", "homeo", "rel", "top", and "rare". +\itemize{ +\item "homeostasis" - Plot the homeostasis/relative abundance of the clones. The \code{clone_split} will +be the fraction of the clones in each sample. +\item "homeo" - Same as "homeostasis". +\item "rel" - Same as "homeostasis". +\item "top" - Plot the top clones. The \code{clone_split} will be indexes to cut the clones. +\item "rare" - Plot the rare clones. The \code{clone_split} will be the clone sizes. +}} + +\item{clone_split}{The split for the clones. Default is NULL. +\itemize{ +\item For "homeostasis", "homeo", "rel" - Default is \code{list(Rare = 1e-04, Small = 0.001, Medium = 0.01, Large = 0.1, Hyperexpanded = 1)}. +\item For "top" - Default is \code{c(10, 100, 1000, 10000, 30000, 100000)}. +\item For "rare" - Default is \code{c(1, 3, 10, 30, 100)}. +}} + +\item{scale}{Whether to scale the number to 1 for each sample. Default is TRUE.} + +\item{facet_by}{The column name in the meta data to facet the plots. Default: NULL} + +\item{group_by}{The column name in the meta data to group the cells. Default: NULL} + +\item{split_by}{The column name in the meta data to split the plots. Default: NULL} + +\item{xlab}{The x-axis label. Default is NULL.} + +\item{ylab}{The y-axis label. Default is NULL.} + +\item{plot_type}{The type of plot to use. Default is "bar". +Possible values are "bar", "ring", "box", and "violin".} + +\item{order}{The order of the x-axis items or groups. Default is an empty list. +It should be a list of values. The names are the column names, and the values are the order.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For \code{bar} plot, see \link[plotthis:barplot]{plotthis::BarPlot}. +\item For \code{ring} plot, see \link[plotthis:RingPlot]{plotthis::RingPlot}. +\item For \code{box} plot, see \link[plotthis:boxviolinplot]{plotthis::BoxPlot}. +\item For \code{violin} plot, see \link[plotthis:boxviolinplot]{plotthis::ViolinPlot}. +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Plot the composition of the clones in different samples/groups. +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) +data <- scRepertoire::addVariable(data, + variable.name = "Subject", + variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +) + +ClonalCompositionPlot(data) +ClonalCompositionPlot(data, method = "top") +ClonalCompositionPlot(data, plot_type = "ring") +ClonalCompositionPlot(data, group_by = "Type", plot_type = "box", comparison = TRUE) +ClonalCompositionPlot(data, group_by = "Type", plot_type = "violin", add_box = TRUE, + add_bg = TRUE) +ClonalCompositionPlot(data, method = "rare") +} diff --git a/man/ClonalDiversity.Rd b/man/ClonalDiversity.Rd new file mode 100644 index 0000000..4f30ec2 --- /dev/null +++ b/man/ClonalDiversity.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonaldivplot.R +\name{ClonalDiversity} +\alias{ClonalDiversity} +\title{Calculate the clonal diversities.} +\usage{ +ClonalDiversity( + input.data, + cloneCall = "gene", + chain = "both", + method = c("shannon", "gini.coeff", "inv.simpson", "norm.entropy", "gini.simpson", + "chao1", "ACE"), + group_by = NULL, + n_boots = 100 +) +} +\description{ +Calculate the clonal diversities. +} +\keyword{internal} diff --git a/man/ClonalDiversityPlot.Rd b/man/ClonalDiversityPlot.Rd new file mode 100644 index 0000000..ebb3452 --- /dev/null +++ b/man/ClonalDiversityPlot.Rd @@ -0,0 +1,85 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonaldivplot.R +\name{ClonalDiversityPlot} +\alias{ClonalDiversityPlot} +\title{ClonalDiversityPlot} +\usage{ +ClonalDiversityPlot( + data, + clone_call = "gene", + chain = "both", + method = c("shannon", "gini.coeff", "inv.simpson", "norm.entropy", "gini.simpson", + "chao1", "ACE"), + plot_type = c("bar", "box", "violin"), + position = "dodge", + group_by = NULL, + facet_by = NULL, + split_by = NULL, + xlab = NULL, + ylab = NULL, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +in the data} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{method}{The method to calculate the diversity. Options are "shannon" (default), +"inv.simpson", "norm.entropy", "gini.simpson", "chao1", "ACE" and "gini.coeff". +See \link[scRepertoire:clonalDiversity]{scRepertoire::clonalDiversity} for details.} + +\item{plot_type}{The type of plot. Options are "bar", "box" and "violin".} + +\item{position}{The position adjustment for the bars. Default is "dodge".} + +\item{group_by}{A character vector of column names to group the samples. Default is NULL.} + +\item{facet_by}{A character vector of column names to facet the plots. Default is NULL.} + +\item{split_by}{A character vector of column names to split the plots. Default is NULL.} + +\item{xlab}{The x-axis label. Default is NULL.} + +\item{ylab}{The y-axis label. Default is NULL.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For "bar", \link[plotthis:barplot]{plotthis::BarPlot} +\item For "box", \link[plotthis:boxviolinplot]{plotthis::BoxPlot} +\item For "violin", \link[plotthis:boxviolinplot]{plotthis::ViolinPlot} +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Plot the clonal diversities of the samples/groups. +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) +data <- scRepertoire::addVariable(data, + variable.name = "Subject", + variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +) + +ClonalDiversityPlot(data) +ClonalDiversityPlot(data, group_by = "Type") +ClonalDiversityPlot(data, group_by = "Type", plot_type = "box") +ClonalDiversityPlot(data, group_by = "Type", plot_type = "violin") +ClonalDiversityPlot(data, group_by = "Type", plot_type = "violin", + method = "gini.coeff", add_box = TRUE) +} diff --git a/man/ClonalGeneUsagePlot.Rd b/man/ClonalGeneUsagePlot.Rd new file mode 100644 index 0000000..8b9ac56 --- /dev/null +++ b/man/ClonalGeneUsagePlot.Rd @@ -0,0 +1,100 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalgeneusageplot.R +\name{ClonalGeneUsagePlot} +\alias{ClonalGeneUsagePlot} +\title{ClonalGeneUsagePlot} +\usage{ +ClonalGeneUsagePlot( + data, + genes = "TRBV", + scale = TRUE, + top = 20, + plot_type = c("bar", "heatmap", "circos", "chord", "alluvial", "sankey"), + group_by = "Sample", + facet_by = NULL, + facet_ncol = 1, + split_by = NULL, + aspect.ratio = 2/top, + theme_args = list(), + ylab = NULL, + row_annotation = NULL, + row_annotation_type = list(), + row_annotation_side = "right", + row_annotation_agg = list(), + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{genes}{The prefix of genes to be plotted. Default is "TRBV". +If two sets of genes are provided (e.g. c("TRBV", "TRBJ")), the second dimension will be the +second set of genes instead of the group_by variable.} + +\item{scale}{Whether to use the proportion that is scaled to the group or the count.} + +\item{top}{The number of top genes/genepairs to be plotted.} + +\item{plot_type}{The type of plot to be generated. Default is "bar". +Options are "bar", "heatmap", "circos" (aka "chord").} + +\item{group_by}{The variable to group the data by. Default is "Sample".} + +\item{facet_by}{A character vector of column names to facet the plots. Default is NULL. +Should not be specified manually.} + +\item{facet_ncol}{The number of columns in the facet grid. Default is 1.} + +\item{split_by}{A character vector of column names to split the plots. Default is NULL.} + +\item{aspect.ratio}{The aspect ratio of the plot. Only available for "bar" plot. Default is 2/top.} + +\item{theme_args}{A list of arguments to be passed to the \link[ggplot2:theme]{ggplot2::theme} function.} + +\item{ylab}{The y-axis label. Default is NULL.} + +\item{row_annotation}{A list of row annotations to be added to the heatmap. Default is NULL.} + +\item{row_annotation_type}{A list of row annotation types.} + +\item{row_annotation_side}{The side of the row annotation. Default is "right".} + +\item{row_annotation_agg}{A list of row annotation aggregation functions.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For "bar", \link[plotthis:barplot]{plotthis::BarPlot} +\item For "heatmap", \link[plotthis:Heatmap]{plotthis::Heatmap} +\item For "circos", \link[plotthis:chordplot]{plotthis::ChordPlot} +\item For "chord", \link[plotthis:chordplot]{plotthis::ChordPlot} +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +ClonalGeneUsagePlot +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) +data <- scRepertoire::addVariable(data, + variable.name = "Subject", + variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +) + +ClonalGeneUsagePlot(data) +ClonalGeneUsagePlot(data, genes = "TRBJ", genes2 = "TRBV") +ClonalGeneUsagePlot(data, top = 40, plot_type = "heatmap") +ClonalGeneUsagePlot(data, genes = c("TRBV", "TRBJ"), plot_type = "heatmap") +ClonalGeneUsagePlot(data, genes = "TRBV", group_by = "Type", plot_type = "chord") +ClonalGeneUsagePlot(data, genes = c("TRBV", "TRBJ"), group_by = "Type", plot_type = "chord") +ClonalGeneUsagePlot(data, genes = c("TRBV", "TRBJ"), plot_type = "alluvial") +} diff --git a/man/ClonalKmerPlot.Rd b/man/ClonalKmerPlot.Rd new file mode 100644 index 0000000..4e63819 --- /dev/null +++ b/man/ClonalKmerPlot.Rd @@ -0,0 +1,88 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalpositionalplot.R +\name{ClonalKmerPlot} +\alias{ClonalKmerPlot} +\title{ClonalKmerPlot} +\usage{ +ClonalKmerPlot( + data, + chain = "TRB", + clone_call = "aa", + k = 3, + top = 25, + group_by = "Sample", + group_by_sep = "_", + facet_by = NULL, + split_by = NULL, + plot_type = c("bar", "line", "heatmap"), + theme_args = list(), + aspect.ratio = NULL, + facet_ncol = NULL, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{chain}{The chain to be analyzed. Default is "TRB".} + +\item{clone_call}{The column name of the clone call. Default is "aa".} + +\item{k}{The length of the k-mer. Default is 3.} + +\item{top}{The number of top k-mers to display. Default is 25.} + +\item{group_by}{The variable to group the data by. Default is "Sample".} + +\item{group_by_sep}{The separator to use when combining groupings. Default is "_".} + +\item{facet_by}{A character vector of column names to facet the plots. Default is NULL.} + +\item{split_by}{A character vector of column names to split the plots. Default is NULL.} + +\item{plot_type}{The type of plot to generate. Default is "bar". +\itemize{ +\item "bar": Bar plot. +\item "line": Line plot. +\item "heatmap": Heatmap. +}} + +\item{theme_args}{A list of arguments to be passed to the \link[ggplot2:theme]{ggplot2::theme} function.} + +\item{aspect.ratio}{The aspect ratio of the plot. Default is NULL.} + +\item{facet_ncol}{The number of columns in the facet grid. Default is NULL.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For "bar", \link[plotthis:barplot]{plotthis::BarPlot} +\item For "line", \link[plotthis:LinePlot]{plotthis::LinePlot} +\item For "heatmap", \link[plotthis:Heatmap]{plotthis::Heatmap} +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Explore the k-mer frequency of CDR3 sequences. +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) +data <- scRepertoire::addVariable(data, + variable.name = "Subject", + variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +) + +ClonalKmerPlot(data) +ClonalKmerPlot(data, group_by = "Type") +ClonalKmerPlot(data, group_by = "Type", plot_type = "line") +ClonalKmerPlot(data, group_by = "Type", plot_type = "heatmap") +} diff --git a/man/ClonalLengthPlot.Rd b/man/ClonalLengthPlot.Rd new file mode 100644 index 0000000..9657cd6 --- /dev/null +++ b/man/ClonalLengthPlot.Rd @@ -0,0 +1,77 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{ClonalLengthPlot} +\alias{ClonalLengthPlot} +\title{ClonalLengthPlot} +\usage{ +ClonalLengthPlot( + data, + clone_call = "aa", + chain = "both", + plot_type = c("bar", "box", "violin", "density"), + x_nbreaks = 10, + group_by = "Sample", + order = list(), + xlab = "Length", + ylab = NULL, + position = "dodge", + facet_by = NULL, + split_by = NULL, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - only "nt" or "aa" is supported.} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRB", "TRD", "TRG", "IGH", or "IGL" to specify a specific chain.} + +\item{plot_type}{The type of plot to use. Default is "bar". +Possible values are "histogram" and "density".} + +\item{x_nbreaks}{The number of breaks for the x-axis. Default is 10.} + +\item{group_by}{The column name in the meta data to group the cells. Default: "Sample"} + +\item{order}{The order of the groups. Default is an empty list. +It should be a list of values. The names are the column names, and the values are the order.} + +\item{xlab}{The x-axis label.} + +\item{ylab}{The y-axis label.} + +\item{position}{The position of the bars for bar plot on the x-axis. Default is "dodge".} + +\item{facet_by}{The column name in the meta data to facet the plots. Default: NULL} + +\item{split_by}{The column name in the meta data to split the plots. Default: NULL} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Plot the length distribution of the CDR3 sequences +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list) +data <- scRepertoire::addVariable(data, variable.name = "Type", + variables = sample(c("B", "L"), 8, replace = TRUE)) +data <- scRepertoire::addVariable(data, variable.name = "Sex", + variables = sample(c("M", "F"), 8, replace = TRUE)) + +ClonalLengthPlot(data) +ClonalLengthPlot(data, plot_type = "box") +ClonalLengthPlot(data, clone_call = "nt", plot_type = "violin", chain = "TRB", + group_by = "Type", comparisons = TRUE) +ClonalLengthPlot(data, plot_type = "density", chain = "TRA") +} diff --git a/man/ClonalOverlapPlot.Rd b/man/ClonalOverlapPlot.Rd new file mode 100644 index 0000000..f5d9bfc --- /dev/null +++ b/man/ClonalOverlapPlot.Rd @@ -0,0 +1,101 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{ClonalOverlapPlot} +\alias{ClonalOverlapPlot} +\title{ClonalOverlapPlot} +\usage{ +ClonalOverlapPlot( + data, + clone_call = "aa", + chain = "both", + group_by = "Sample", + group_by_sep = "_", + full = TRUE, + split_by = NULL, + order = list(), + method = c("raw", "overlap", "morisita", "jaccard", "cosine"), + palette = "Blues", + label_accuracy = NULL, + label_cutoff = 0.001, + cluster_rows = FALSE, + cluster_columns = FALSE, + show_row_names = TRUE, + show_column_names = TRUE, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +in the data} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{group_by}{The column name in the meta data to group the cells. Default: "Sample"} + +\item{group_by_sep}{The separator used to concatenate the group_by when multiple columns are used.} + +\item{full}{Whether to plot the full heatmap, or just a triangle. Default is TRUE.} + +\item{split_by}{The column name in the meta data to split the plots. Default: NULL} + +\item{order}{The order of the groups. Default is an empty list. +It should be a list of values. The names are the column names, and the values are the order.} + +\item{method}{The method to calculate the overlap. Default is "raw". +\itemize{ +\item "overlap" - overlap coefficient +\item "morisita" - Morisita’s overlap index +\item "jaccard" - Jaccard index +\item "cosine" - cosine similarity +\item "raw" - exact number of overlapping clones +See also \link[scRepertoire:clonalOverlap]{scRepertoire::clonalOverlap}. +}} + +\item{palette}{The color palette to use. Default is "Blues".} + +\item{label_accuracy}{The accuracy of the labels. Default is NULL. +If NULL, it will be 1 for "raw" and 0.01 for other methods.} + +\item{label_cutoff}{The cutoff for the labels to show. Default is 1e-3.} + +\item{cluster_rows}{Whether to cluster the rows. Default is FALSE.} + +\item{cluster_columns}{Whether to cluster the columns. Default is FALSE.} + +\item{show_row_names}{Whether to show the row names. Default is TRUE.} + +\item{show_column_names}{Whether to show the column names. Default is TRUE.} + +\item{...}{Other arguments passed to the specific plot function \link[plotthis:Heatmap]{plotthis::Heatmap}.} +} +\value{ +A ComplexHeatmap object or a list if \code{combine} is FALSE +} +\description{ +Plot the overlap of the clones in different samples/groups. +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) +data <- scRepertoire::addVariable(data, + variable.name = "Subject", + variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +) + +ClonalOverlapPlot(data) +ClonalOverlapPlot(data, clone_call = "strict", label_cutoff = 0, + label_accuracy = 0.001, method = "morisita", full = FALSE) +ClonalOverlapPlot(data, group_by = c("Subject", "Type")) +ClonalOverlapPlot(data, group_by = "Type", split_by = "Subject") +} diff --git a/man/ClonalPositionalPlot.Rd b/man/ClonalPositionalPlot.Rd new file mode 100644 index 0000000..8882fd6 --- /dev/null +++ b/man/ClonalPositionalPlot.Rd @@ -0,0 +1,109 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalpositionalplot.R +\name{ClonalPositionalPlot} +\alias{ClonalPositionalPlot} +\title{ClonalPositionalPlot} +\usage{ +ClonalPositionalPlot( + data, + chain = "TRB", + aa_length = 20, + group_by = "Sample", + group_by_sep = "_", + split_by = NULL, + method = c("AA", "shannon", "inv.simpson", "norm.entropy", "Atchley", "Kidera", + "stScales", "tScales", "VHSE"), + plot_type = c("bar", "line", "heatmap", "box", "violin"), + theme_args = list(), + xlab = NULL, + ylab = NULL, + facet_by = NULL, + facet_ncol = NULL, + facet_nrow = NULL, + aspect.ratio = NULL, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{chain}{The chain to be analyzed. Default is "TRB".} + +\item{aa_length}{The length of the amino acid sequence. Default is 20.} + +\item{group_by}{The variable to group the data by. Default is "Sample".} + +\item{group_by_sep}{The separator to use when combining groupings. Default is "_".} + +\item{split_by}{The variable to split the data by. Default is NULL.} + +\item{method}{The method to calculate the positional entropy. Default is "AA". +\itemize{ +\item "AA": Amino acid frequency. +\item "shannon": Shannon entropy. +\item "inv.simpson": Inverse Simpson index. +\item "norm.entropy": Normalized entropy. +\item "Atchley": Atchley factors. +\item "Kidera": Kidera factors. +\item "stScales": stScales factors. +\item "tScales": tScales factors. +\item "VHSE": Vectors of Hydrophobic, Steric, and Electronic properties. +See also \link[scRepertoire:percentAA]{scRepertoire::percentAA}, \link[scRepertoire:positionalEntropy]{scRepertoire::positionalEntropy} and +\link[scRepertoire:positionalProperty]{scRepertoire::positionalProperty}. +}} + +\item{plot_type}{The type of plot to generate. Default is "bar". +\itemize{ +\item "bar": Bar plot. +\item "line": Line plot. +\item "heatmap": Heatmap. +\item "box": Box plot. +\item "violin": Violin plot. +}} + +\item{theme_args}{A list of arguments to be passed to the \link[ggplot2:theme]{ggplot2::theme} function.} + +\item{xlab}{The x-axis label. Default is NULL.} + +\item{ylab}{The y-axis label. Default is NULL.} + +\item{facet_by}{A character vector of column names to facet the plots. Default is NULL.} + +\item{facet_ncol}{The number of columns in the facet grid. Default is NULL.} + +\item{facet_nrow}{The number of rows in the facet grid. Default is NULL.} + +\item{aspect.ratio}{The aspect ratio of the plot. Default is NULL.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For "bar", \link[plotthis:barplot]{plotthis::BarPlot} +\item For "line", \link[plotthis:LinePlot]{plotthis::LinePlot} +\item For "heatmap", \link[plotthis:Heatmap]{plotthis::Heatmap} +\item For "box", \link[plotthis:boxviolinplot]{plotthis::BoxPlot} +\item For "violin", \link[plotthis:boxviolinplot]{plotthis::ViolinPlot} +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Visualize the positional entropy, property or amino acid frequency of CDR3 sequences. +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) + +ClonalPositionalPlot(data) +ClonalPositionalPlot(data, method = "shannon") +ClonalPositionalPlot(data, method = "norm.entropy", plot_type = "heatmap") +ClonalPositionalPlot(data, method = "Atchley", group_by = "Type", plot_type = "bar") +ClonalPositionalPlot(data, method = "Atchley", plot_type = "line") +} diff --git a/man/ClonalRarefactionPlot.Rd b/man/ClonalRarefactionPlot.Rd new file mode 100644 index 0000000..ddee246 --- /dev/null +++ b/man/ClonalRarefactionPlot.Rd @@ -0,0 +1,93 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonaldivplot.R +\name{ClonalRarefactionPlot} +\alias{ClonalRarefactionPlot} +\title{ClonalRarefactionPlot} +\usage{ +ClonalRarefactionPlot( + data, + clone_call = "aa", + chain = "both", + group_by = "Sample", + group_by_sep = "_", + n_boots = 20, + q = 0, + facet_by = NULL, + split_by = NULL, + split_by_sep = "_", + palette = "Paired", + combine = TRUE, + nrow = NULL, + ncol = NULL, + byrow = TRUE, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{group_by}{A character vector of column names to group the samples. Default is "Sample".} + +\item{group_by_sep}{The separator for the group_by column. Default is "_".} + +\item{n_boots}{The number of bootstrap samples. Default is 20.} + +\item{q}{The hill number. Default is 0. +\itemize{ +\item 0 - Species richness +\item 1 - Shannon entropy +\item 2 - Simpson index#' +}} + +\item{facet_by}{A character vector of column names to facet the plots. Default is NULL.} + +\item{split_by}{A character vector of column names to split the plots. Default is NULL.} + +\item{split_by_sep}{The separator for the split_by column. Default is "_".} + +\item{palette}{The color palette to use. Default is "Paired".} + +\item{combine}{Whether to combine the plots into a single plot. Default is TRUE.} + +\item{nrow}{The number of rows in the combined plot. Default is NULL.} + +\item{ncol}{The number of columns in the combined plot. Default is NULL.} + +\item{byrow}{Whether to fill the combined plot by row. Default is TRUE.} + +\item{...}{Other arguments passed to \link[plotthis:RarefactionPlot]{plotthis::RarefactionPlot}.} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Plot the rarefaction curves +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) +data <- scRepertoire::addVariable(data, + variable.name = "Subject", + variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +) + +ClonalRarefactionPlot(data, type = 1, q = 0, n_boots = 2) +ClonalRarefactionPlot(data, type = 2, q = 0, n_boots = 2) +ClonalRarefactionPlot(data, type = 3, q = 0, n_boots = 2) +ClonalRarefactionPlot(data, q = 1, n_boots = 2) +ClonalRarefactionPlot(data, q = 1, n_boots = 2, group_by = "Type") +ClonalRarefactionPlot(data, n_boots = 2, split_by = "Type") +} diff --git a/man/ClonalResidencyPlot.Rd b/man/ClonalResidencyPlot.Rd new file mode 100644 index 0000000..206905d --- /dev/null +++ b/man/ClonalResidencyPlot.Rd @@ -0,0 +1,105 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{ClonalResidencyPlot} +\alias{ClonalResidencyPlot} +\title{ClonalResidencyPlot} +\usage{ +ClonalResidencyPlot( + data, + clone_call = "aa", + chain = "both", + plot_type = c("scatter", "venn", "upset"), + group_by = "Sample", + groups = NULL, + facet_by = NULL, + split_by = NULL, + split_by_sep = "_", + scatter_cor = "pearson", + scatter_size_by = c("max", "total"), + order = list(), + combine = TRUE, + nrow = NULL, + ncol = NULL, + byrow = TRUE, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +in the data} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{plot_type}{The type of plot to use. Default is "scatter". +Possible values are "scatter", "venn", and "upset".} + +\item{group_by}{The column name in the meta data to group the cells. Default: "Sample"} + +\item{groups}{The groups to compare. Default is NULL. +If NULL, all the groups in \code{group_by} will be compared. +Note that for "scatter" plot, only two groups can be compared.} + +\item{facet_by}{The column name in the meta data to facet the plots. Default: NULL} + +\item{split_by}{The column name in the meta data to split the plots. Default: NULL} + +\item{split_by_sep}{The separator used to concatenate the split_by when multiple columns are used.} + +\item{scatter_cor}{The correlation method to use for the scatter plot. Default is "pearson".} + +\item{scatter_size_by}{The size of the points in the scatter plot. Default is "max". +Possible values are "max" and "total". +\itemize{ +\item "max" - The max size of the clone in the two groups. +\item "total" - The total size of the clone in the two groups. +}} + +\item{order}{The order of the x-axis items or groups. Default is an empty list. +It should be a list of values. The names are the column names, and the values are the order.} + +\item{combine}{Whether to combine the plots into a single plot. Default is TRUE.} + +\item{nrow}{The number of rows in the combined plot. Default is NULL.} + +\item{ncol}{The number of columns in the combined plot. Default is NULL.} + +\item{byrow}{Whether to fill the combined plot by row. Default is TRUE.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For \code{scatter} plot, see \link[plotthis:ScatterPlot]{plotthis::ScatterPlot}. +\item For \code{venn} plot, see \link[plotthis:venndiagram1]{plotthis::VennDiagram}. +\item For \code{upset} plot, see \link[plotthis:upsetplot1]{plotthis::UpsetPlot}. +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +Plot the residency of the clones in different samples. +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B","P19L", "P20B", "P20L")) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = rep(c("B", "L"), 4) +) +data <- scRepertoire::addVariable(data, + variable.name = "Subject", + variables = rep(c("P17", "P18", "P19", "P20"), each = 2) +) + +ClonalResidencyPlot(data, groups = c("P18B", "P18L")) +ClonalResidencyPlot(data, group_by = "Type", split_by = "Subject") +ClonalResidencyPlot(data, plot_type = "venn", groups = c("B", "L"), group_by = "Type", + split_by = "Subject") +ClonalResidencyPlot(data, plot_type = "upset", groups = c("P18B", "P18L")) +} diff --git a/man/ClonalSizeData.Rd b/man/ClonalSizeData.Rd new file mode 100644 index 0000000..57eb6ec --- /dev/null +++ b/man/ClonalSizeData.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{ClonalSizeData} +\alias{ClonalSizeData} +\title{ClonalSizeData} +\usage{ +ClonalSizeData(data, clone_call, chain, groupings) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +in the data} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{groupings}{The column names in the meta data to group the cells.} +} +\description{ +Function to get the clonal size data for all group_by values. +} +\keyword{internal} diff --git a/man/ClonalVolumePlot.Rd b/man/ClonalVolumePlot.Rd new file mode 100644 index 0000000..aeec713 --- /dev/null +++ b/man/ClonalVolumePlot.Rd @@ -0,0 +1,101 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{ClonalVolumePlot} +\alias{ClonalVolumePlot} +\title{ClonalVolumePlot} +\usage{ +ClonalVolumePlot( + data, + clone_call = "aa", + chain = "both", + scale = FALSE, + plot_type = c("bar", "box", "violin"), + x = "Sample", + group_by = NULL, + facet_by = NULL, + split_by = NULL, + order = list(), + ylab = NULL, + ... +) +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{clone_call}{How to call the clone - VDJC gene (gene), CDR3 nucleotide (nt), +CDR3 amino acid (aa), VDJC gene + CDR3 nucleotide (strict) or a custom variable +in the data} + +\item{chain}{indicate if both or a specific chain should be used - e.g. "both", +"TRA", "TRG", "IGH", "IGL"} + +\item{scale}{Whether to use clone proportion or clone size for the plot.} + +\item{plot_type}{The type of plot to use. Default is "bar". +Possible values are "bar", "box", and "violin". +When "box" or "violin" is used, the data will be broken down by the Sample and plotted +for each group.} + +\item{x}{The column name in the meta data to use as the x-axis. Default: "Sample"} + +\item{group_by}{The column name in the meta data to group the cells. Default: NULL} + +\item{facet_by}{The column name in the meta data to facet the plots. Default: NULL} + +\item{split_by}{The column name in the meta data to split the plots. Default: NULL} + +\item{order}{The order of the x-axis items or groups. Default is an empty list. +It should be a list of values. The names are the column names, and the values are the order.} + +\item{ylab}{The y-axis label.} + +\item{...}{Other arguments passed to the specific plot function. +\itemize{ +\item For \code{bar} plot, see \link[plotthis:barplot]{plotthis::BarPlot}. +\item For \code{box} plot, see \link[plotthis:boxviolinplot]{plotthis::BoxPlot}. +\item For \code{violin} plot, see \link[plotthis:boxviolinplot]{plotthis::ViolinPlot}. +}} +} +\value{ +A ggplot object or a list if \code{combine} is FALSE +} +\description{ +ClonalVolumePlot +} +\examples{ +set.seed(8525) +data(contig_list, package = "scRepertoire") +data <- scRepertoire::combineTCR(contig_list) +data <- scRepertoire::addVariable(data, + variable.name = "Type", + variables = sample(c("B", "L"), 8, replace = TRUE) +) +data <- scRepertoire::addVariable(data, + variable.name = "Sex", + variables = sample(c("M", "F"), 8, replace = TRUE) +) + +ClonalVolumePlot(data) +ClonalVolumePlot(data, x = "Type") +ClonalVolumePlot(data, x = "Type", order = list(Type = c("L", "B"))) +ClonalVolumePlot(data, x = c("Type", "Sex"), scale = TRUE) +ClonalVolumePlot(data, x = "Type", group_by = "Sex", position = "stack") +ClonalVolumePlot(data, + plot_type = "box", x = "Type", comparisons = TRUE, + group_by = "Sex" +) +ClonalVolumePlot(data, plot_type = "violin", x = "Type", add_box = TRUE) + +# on a Seurat object +data(scRep_example, package = "scRepertoire") +data(contig_list, package = "scRepertoire") +combined <- scRepertoire::combineTCR(contig_list, + samples = c("P17B", "P17L", "P18B", "P18L", "P19B", "P19L", "P20B", "P20L") +) +sobj <- scRepertoire::combineExpression(combined, scRep_example) +ClonalVolumePlot(sobj) +ClonalVolumePlot(sobj, x = "seurat_clusters") +ClonalVolumePlot(sobj, group_by = "seurat_clusters") +ClonalVolumePlot(sobj, x = "seurat_clusters", plot_type = "box") +} diff --git a/man/ClustreePlot.Rd b/man/ClustreePlot.Rd index ab0e00e..3c60c33 100644 --- a/man/ClustreePlot.Rd +++ b/man/ClustreePlot.Rd @@ -9,7 +9,7 @@ ClustreePlot(object, ...) \arguments{ \item{object}{The data frame or Seurat object} -\item{...}{Other arguments passed to \code{\link{plotthis::ClustreePlot}}} +\item{...}{Other arguments passed to \link[plotthis:ClustreePlot]{plotthis::ClustreePlot}} } \value{ A ggplot object or a list if \code{combine} is FALSE diff --git a/man/DummyClonalScatterPlot.Rd b/man/DummyClonalScatterPlot.Rd new file mode 100644 index 0000000..6d4bc13 --- /dev/null +++ b/man/DummyClonalScatterPlot.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{DummyClonalScatterPlot} +\alias{DummyClonalScatterPlot} +\title{DummyClonalScatterPlot} +\usage{ +DummyClonalScatterPlot(df, title, group_by, scatter_cor, size_by, ...) +} +\arguments{ +\item{df}{The data frame with the clonal data. +The data frame should have the columns: group_by, 'count', 'fraction'.} +} +\value{ +A ggplot object +} +\description{ +Function to plot the scatter plot of the clonal data for a dummy group pair. +} +\keyword{internal} diff --git a/man/EnrichmentPlot.Rd b/man/EnrichmentPlot.Rd index 0778241..214328d 100644 --- a/man/EnrichmentPlot.Rd +++ b/man/EnrichmentPlot.Rd @@ -32,7 +32,7 @@ EnrichmentPlot( } \arguments{ \item{data}{A data frame with enrichment results generated by \code{clusterProfiler}. -If your result is generated by \code{enrichr}, you can use \code{\link{PrepareEnrichrResult}} to convert it to a data frame.} +If your result is generated by \code{enrichr}, you can use \link{PrepareEnrichrResult} to convert it to a data frame.} \item{top_term}{Number of top terms to show in the plot. Default is 6 for all plots except "enrichmap" which is 100.} @@ -56,7 +56,7 @@ When the terms are too long, they will be wrapped to fit the width.} \item{expand}{A numeric vector of length 1, 2 or 4 to expand the plot. Default is NULL. Works only for "bar" plot. -See also \code{\link{plotthis::BarPlot}}.} +See also \link[plotthis:barplot]{plotthis::BarPlot}.} \item{word_type}{The type of word to show in the wordcloud. Options are "term" and "feature". Default is "term". Works only for "wordcloud".} @@ -89,7 +89,7 @@ When specified: }} \item{palette}{The color palette to use for the plot. Default is "Spectral". -See \code{\link{plotthis::show_palettes}} for available palettes.} +See \link[plotthis:show_palettes]{plotthis::show_palettes} for available palettes.} \item{xlab}{The x-axis label. Default is NULL.} @@ -97,13 +97,13 @@ See \code{\link{plotthis::show_palettes}} for available palettes.} \item{...}{Other arguments passed to the specific plot function. \itemize{ -\item For "bar", \code{\link{plotthis::BarPlot}} -\item For "dot", \code{\link{plotthis::DotPlot}} -\item For "lollipop", \code{\link{plotthis::LollipopPlot}} -\item For "network", \code{\link{plotthis::EnrichNetwork}} -\item For "enrichmap", \code{\link{plotthis::EnrichMap}} -\item For "wordcloud", \code{\link{plotthis::WordCloudPlot}} -\item For "comparison", \code{\link{plotthis::DotPlot}} +\item For "bar", \link[plotthis:barplot]{plotthis::BarPlot} +\item For "dot", \link[plotthis:dotplot]{plotthis::DotPlot} +\item For "lollipop", \link[plotthis:dotplot]{plotthis::LollipopPlot} +\item For "network", \link[plotthis:enrichmap1]{plotthis::EnrichNetwork} +\item For "enrichmap", \link[plotthis:enrichmap1]{plotthis::EnrichMap} +\item For "wordcloud", \link[plotthis:WordCloudPlot]{plotthis::WordCloudPlot} +\item For "comparison", \link[plotthis:dotplot]{plotthis::DotPlot} }} } \description{ diff --git a/man/FeatureStatPlot.Rd b/man/FeatureStatPlot.Rd index 5210485..c2adac7 100644 --- a/man/FeatureStatPlot.Rd +++ b/man/FeatureStatPlot.Rd @@ -66,15 +66,15 @@ If TRUE, the cells are split by the features.} \item{...}{Other arguments passed to the plot functions. \itemize{ -\item For \code{plot_type} "violin", the arguments are passed to \code{\link{plotthis::ViolinPlot}}. -\item For \code{plot_type} "box", the arguments are passed to \code{\link{plotthis::BoxPlot}}. -\item For \code{plot_type} "bar", the arguments are passed to \code{\link{plotthis::BarPlot}}. -\item For \code{plot_type} "ridge", the arguments are passed to \code{\link{plotthis::RidgePlot}}. -\item For \code{plot_type} "dim", the arguments are passed to \code{\link{plotthis::FeatureDimPlot}}. -\item For \code{plot_type} "heatmap", the arguments are passed to \code{\link{plotthis::Heatmap}}. -\item For \code{plot_type} "cor" with 2 features, the arguments are passed to \code{\link{plotthis::CorPlot}}. -\item For \code{plot_type} "cor" with more than 2 features, the arguments are passed to \code{\link{plotthis::CorPairsPlot}}. -\item For \code{plot_type} "dot", the arguments are passed to \code{\link{plotthis::Heatmap}} with \code{cell_type} set to "dot". +\item For \code{plot_type} "violin", the arguments are passed to \link[plotthis:boxviolinplot]{plotthis::ViolinPlot}. +\item For \code{plot_type} "box", the arguments are passed to \link[plotthis:boxviolinplot]{plotthis::BoxPlot}. +\item For \code{plot_type} "bar", the arguments are passed to \link[plotthis:barplot]{plotthis::BarPlot}. +\item For \code{plot_type} "ridge", the arguments are passed to \link[plotthis:RidgePlot]{plotthis::RidgePlot}. +\item For \code{plot_type} "dim", the arguments are passed to \link[plotthis:dimplot]{plotthis::FeatureDimPlot}. +\item For \code{plot_type} "heatmap", the arguments are passed to \link[plotthis:Heatmap]{plotthis::Heatmap}. +\item For \code{plot_type} "cor" with 2 features, the arguments are passed to \link[plotthis:CorPlot]{plotthis::CorPlot}. +\item For \code{plot_type} "cor" with more than 2 features, the arguments are passed to \link[plotthis:CorPairsPlot]{plotthis::CorPairsPlot}. +\item For \code{plot_type} "dot", the arguments are passed to \link[plotthis:Heatmap]{plotthis::Heatmap} with \code{cell_type} set to "dot". }} } \value{ diff --git a/man/MergeClonalGroupings.Rd b/man/MergeClonalGroupings.Rd new file mode 100644 index 0000000..30647c6 --- /dev/null +++ b/man/MergeClonalGroupings.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/clonalstatplot.R +\name{MergeClonalGroupings} +\alias{MergeClonalGroupings} +\title{MergeClonalGroupings} +\usage{ +MergeClonalGroupings(data, groupings, sep = " // ") +} +\arguments{ +\item{data}{The product of \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, \link[scRepertoire:combineTCR]{scRepertoire::combineTCR}, or +\link[scRepertoire:combineExpression]{scRepertoire::combineExpression}.} + +\item{groupings}{A list of the clonal groupings. Each element is a column in the data.} +} +\value{ +The data with the combined groupings (\code{.group}) +} +\description{ +Merge the multiple clonal groupings into a single grouping. +} +\details{ +Because \link[scRepertoire:clonalQuant]{scRepertoire::clonalQuant} and families don't support mutliple groupings, +this is trying to merge the multiple groupings into a single grouping. And then +later restore the original groupings. +} +\keyword{internal} diff --git a/man/cellphonedb_res.Rd b/man/cellphonedb_res.Rd new file mode 100644 index 0000000..a6d8830 --- /dev/null +++ b/man/cellphonedb_res.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\name{cellphonedb_res} +\alias{cellphonedb_res} +\title{A toy example of CellPhoneDB output from LIANA} +\format{ +A \code{data.frame} with 10 rows and 6 columns +} +\source{ +\url{https://liana-py.readthedocs.io/en/latest/notebooks/basic_usage.html#Tileplot} +} +\description{ +The dataset is generated using python package LIANA +} +\examples{ +\dontrun{ +# Python code +# # import liana +# import liana as li +# # needed for visualization and toy data +# import scanpy as sc +# +# from liana.method import cellphonedb +# adata = sc.datasets.pbmc68k_reduced() +# cellphonedb(adata, +# groupby='bulk_labels', +# # NOTE by default the resource uses HUMAN gene symbols +# resource_name='consensus', +# expr_prop=0.1, +# verbose=True, key_added='cpdb_res') +# cellphonedb_res = adata.uns['cpdb_res'] +# cellphonedb_res = cellphonedb_res[cellphonedb_res['cellphone_pvals'] < 0.05] +} +} +\concept{data} diff --git a/man/figures/cccplot.png b/man/figures/cccplot.png new file mode 100644 index 0000000..37b2429 Binary files /dev/null and b/man/figures/cccplot.png differ diff --git a/man/figures/clonaldiv.png b/man/figures/clonaldiv.png new file mode 100644 index 0000000..40de607 Binary files /dev/null and b/man/figures/clonaldiv.png differ diff --git a/man/figures/clonalstat.png b/man/figures/clonalstat.png new file mode 100644 index 0000000..c1f4c7a Binary files /dev/null and b/man/figures/clonalstat.png differ diff --git a/man/pancreas_sub.Rd b/man/pancreas_sub.Rd index 126e1a6..686de52 100644 --- a/man/pancreas_sub.Rd +++ b/man/pancreas_sub.Rd @@ -29,12 +29,15 @@ if (interactive()) { pancreas_sub[["SubCellType"]] <- pancreas_sub[["clusters"]] pancreas_sub[["clusters_coarse"]] <- pancreas_sub[["clusters"]] <- NULL pancreas_sub[["Phase"]] <- ifelse(pancreas_sub$S_score > pancreas_sub$G2M_score, "S", "G2M") - pancreas_sub[["Phase"]][apply(pancreas_sub[[]][, c("S_score", "G2M_score")], 1, max) < 0, ] <- "G1" - pancreas_sub[["Phase", drop = TRUE]] <- factor(pancreas_sub[["Phase", drop = TRUE]], levels = c("G1", "S", "G2M")) + pancreas_sub[["Phase"]][ + apply(pancreas_sub[[]][, c("S_score", "G2M_score")], 1, max) < 0, ] <- "G1" + pancreas_sub[["Phase", drop = TRUE]] <- factor(pancreas_sub[["Phase", drop = TRUE]], + levels = c("G1", "S", "G2M")) pancreas_sub[["PCA"]] <- pancreas_sub[["X_pca"]] pancreas_sub[["UMAP"]] <- pancreas_sub[["X_umap"]] pancreas_sub[["X_umap"]] <- pancreas_sub[["X_pca"]] <- NULL - VariableFeatures(pancreas_sub) <- rownames(pancreas_sub[["RNA"]])[which(pancreas_sub[["RNA"]]@meta.features$highly_variable_genes == "True")] + VariableFeatures(pancreas_sub) <- rownames(pancreas_sub[["RNA"]])[ + which(pancreas_sub[["RNA"]]@meta.features$highly_variable_genes == "True")] pancreas_sub <- NormalizeData(pancreas_sub) pancreas_sub <- FindVariableFeatures(pancreas_sub) pancreas_sub <- ScaleData(pancreas_sub)