Frequency plots are commonly used in copy number analysis in bulk sequencing studies to show how frequently different regions of the genome are altered. A similar approach can be done in single cell data by pseudobulking the cells and then thresholding the values to label each region as a loss or gain for the pseudobulked values.

By default, the makeFrequencyPlot() function will group all cells based on their sample of origin (or whatever metadata column is provided to the group parameter) and take the mean copy number value for that group at each genomic bin. It will then perform thresholding to label regions as loss/neutral/gain. As a primary example, we can begin with the example data and use the default values for the function.

library(scCNVis)
#Create object using provided example data
obj <- scCNVis::createPlotObject(example.matrix,
                                 example.cells,
                                 example.granges,
                                 example.meta)
fp.result <- scCNVis::makeFrequencyPlot(obj)

#scCNVis::saveCustomPlot(fp.result, "~/path/to/plot.png")
Example 1a. Frequency Plot

Example 1a. Frequency Plot

As with standard frequency plots, colored regions above 0 indicate that a percentage of our pseudobulked samples have gains in those regions, while colored regions below 0 indicate that a percentage of our pseudbulked samples have losses in those regions. Since our example only has 1 sample, you can see that all values are 0% or 100%.

While the default above is the standard use for a frequency plot, makeFrequencyPlot() includes some additional features that may help provide additional insights. For example, the pseudobulked values do not necessarily have to represent samples. In the below example, they represent subclones from out metadata. The below example also shows how users can provide custom filtering thresholds to define gains and losses and how to color those values with custom colors.


#Defining a custom thresholding function
#The function will be passed a data.frame with the following format
#Columns: "chrm" "middle" "pos.index" "name" "value" 
#Where chrm = seqname from obj@GRanges
#      middle = Middle of genomic position (bp)
#      pos.index = Numerical index of row
#      name = Name of sample (or other pseudobulked group)
#      value = Copy number value for the pseudobulked group for this region
#The output of the function is expected to be the a data frame
#that includes these same columns, although this is explored
#further in a later example
custom.data.func <- function(data) {
      sd.value <- sd(data$value, na.rm = TRUE)
      mean.value <- mean(data$value, na.rm = TRUE)
      #Any value > mean + 0.5sd will be considered a gain
      data[data$value > mean.value + (sd.value * 0.5),"value"] <- 2 #Gain == 2
      #Any value < mean - 0.5sd will be considered a loss
      data[data$value < mean.value - (sd.value * 0.5),"value"] <- 0 #Loss == 0
      #All other values will be considered neutral
      data[data$value != 2 & data$value != 0,"value"] <- 1 #Neutral == 1
      return(data)
    }

fp.result <- scCNVis::makeFrequencyPlot(obj,
                                        group = "Subclone",
                                        data.func = custom.data.func,
                                        gain.color = "green",
                                        loss.color = "purple")

#scCNVis::saveCustomPlot(fp.result, "~/path/to/plot.png")
Example 1b. Frequency Plot, pseudobulked subclones

Example 1b. Frequency Plot, pseudobulked subclones

As our data had 4 defined subclones, we can see that the available values in our frequency plot are now 0%, 25%, 50%, 75% and 100%. We can observe that 100% of our subclones had loss on parts of chr1 and chr19, while only half of them had loss on chr4.

In some cases, it may be desirable to group values together. In the below hypothetical example, we will pretend that each of our subclones represent a sample, and that two of those samples have Phenotype A and two of them have Phenotype B.


new.meta <- obj@Meta

#Pretend that subclones represent samples, and assign phenotypes to those samples
new.meta$NewSample <- new.meta$Subclone
subclone.names <- unique(obj@Meta$Subclone)
new.meta$NewPhenotype <- ifelse(new.meta$Subclone == subclone.names[1] | 
                                  new.meta$Subclone == subclone.names[2], "Phenotype A", "Phenotype B")


#Add the hypothetical metadata to our scCNVisObject
obj <- scCNVis::addMetaData(obj, new.meta$NewSample, "NewSample")
obj <- scCNVis::addMetaData(obj, new.meta$NewPhenotype, "NewPhenotype")

#Frequencies are calculated by 'NewSample' and 'NewPhenotype' groupings
fp.result <- scCNVis::makeFrequencyPlot(obj,
                                        group = "NewSample",
                                        secondary.group = "NewPhenotype")

#scCNVis::saveCustomPlot(fp.result, "~/path/to/plot.png")
Example 1c. Frequency Plot, pseudobulked subclones grouped by arbitrary phenotype

Example 1c. Frequency Plot, pseudobulked subclones grouped by arbitrary phenotype

Our results are now grouped by phenotype. Since each (arbitrary) phenotype had two hypothetical samples assigned to them, we see that all values are either 0%, 50%, or 100%. We can observe that losses on chr12 are only found in Phenotype B, while all “samples” that have Phenotype A appear to have a focal loss on chr17.

Finally, like the makeLinePlot() function, the output plot is a ggplot object, which means that users can freely alter the output like they would a standard ggplot figure. Additionaly, fp.result$PlotData contains the data.frame used to generate the plot, so users can create the plot from scratch if they desired. An example of this customization is shown below.


fp.result <- scCNVis::makeFrequencyPlot(obj)

#I have selected a random gene/location here, but manual inspection
#of fp.result$PlotData can be done to find the "pos.index" value
#that overlaps with the location of genes of interest and used to add
#marker gene labels
pos.index.example.values <- c(300, 7950)
marker.gene.names <- c("GeneA", "GeneB")

final.plot <- fp.result$Plot +
  ggplot2::scale_x_continuous(breaks = pos.index.example.values,
                              labels = marker.gene.names) +
  ggplot2::labs(x="Genome",
                y="Frequency of Alteration",
                title="Genome Alteration Frequencies") +
  ggplot2::theme_bw() +
  ggplot2::theme(panel.spacing.x = ggplot2::unit(0.1, "lines"),
                 panel.spacing.y = ggplot2::unit(1, "lines"),
                 plot.title = ggplot2::element_text(hjust = 0.5))

#fp.result$Plot <- final.plot
#scCNVis::saveCustomPlot(fp.result, "~/path/to/plot.png")
#or simply ggplot2::ggsave(final.plot)
Example 1d. Frequency Plot with customization

Example 1d. Frequency Plot with customization