R/plotCircle.r

Defines functions c.lines circ plotCircle

Documented in plotCircle

####################################################################
## Author: Gro Nilsen, Knut Liestøl and Ole Christian Lingjærde.
## Maintainer: Gro Nilsen <gronilse@ifi.uio.no>
## License: Artistic 2.0
## Part of the copynumber package
## Reference: Nilsen and Liestøl et al. (2012), BMC Genomics
####################################################################

# Function that plots the frequency of deletions and amplifications given a threshold on the genome represented by a circle. Can also add arc to represent connections between different
# part of the genome

## Input:
### segments: segmentations results from pcf or multipcf
### thres.gain,thres.loss: a vector with threshold(s) to be applied for abberration calling
### pos.unit: unit used for positions (bp,kbp,mbp)
### freq.colors: colors used to plot gain and loss frequencies
### alpha: a scalar between 0 and 1 that sets the amount of scaling of frequencies in order to fit in circle
### arcs: a data frame with 5 columns. The first four columns gives chromosome numbers and local positions for start point and end point of arc, respectively, while the last column should contain a vector of numbers 1,2,... indication that the arcs belong to different classes. Each class of arcs will then be plotted in a different color
### arc.colors: colors used to plot the lines representing the different classes in arcs. Must be longer or equal to the number of classes found in arcs.
### d: a scalar > 0 representing the distance from the genome circle to the starting points of the arcs. Set d=0 to make arcs start at the genome circle
### assembly: which assembly to use, must be one of hg19, hg18, hg17 or hg16

## Required by: none

### Requires:
## getGlobPos
## numericChrom
## circ
## c.lines
## pullOutContent
## getFreqData


plotCircle <- function(segments, thres.gain, thres.loss = -thres.gain, pos.unit = "bp", freq.colors = c("red", "limegreen"), alpha = 1 / 7, arcs = NULL, arc.colors = c("goldenrod1", "dodgerblue"), d = 0.3, assembly = "hg19") {
  delta <- 20000000 # defines the amount of space to be plotted between each chromosome

  # Empty plot
  par(mar = c(0, 0, 0, 0))
  plot(0, 0, xlim = c(-1.2, 1.2), ylim = c(-1.2, 1.2), axes = F, xlab = "", ylab = "", asp = 1, type = "n")

  outer.circ <- 0 # location of outer circle
  inner.circ <- 0.1 # location of inner circle

  # Plot cytoband

  cytoband <- get(assembly)
  cyto.chr <- substring(cytoband[, 1], 4)
  cyto.chr[grep("X", cyto.chr)] <- 23
  cyto.chr[grep("Y", cyto.chr)] <- 24
  cyto.chr <- as.numeric(cyto.chr)
  o <- order(cyto.chr)
  cyto.chr <- cyto.chr[o]
  cytoband <- cytoband[o, ]

  chr.end <- diff(cyto.chr) > 0
  cyto.start <- getGlobPos(cyto.chr, cytoband[, 2], pos.unit, cytoband, delta = delta)
  cyto.end <- getGlobPos(cyto.chr, cytoband[, 3], pos.unit, cytoband, delta = delta)
  cyto.end[chr.end[1]:length(cyto.end)] <- cyto.end[chr.end[1]:length(cyto.end)] + delta # need to add delta between first and second chromosome

  cyto.stain <- cytoband[, 5]
  xmin <- cyto.start[1]
  xmax <- cyto.end[length(cyto.end)]
  ngrid <- 5000
  gridpts <- seq(xmin, xmax, length = ngrid)
  x0 <- gridpts
  x1 <- x0
  y0 <- rep(0, ngrid)
  y1 <- rep(inner.circ, ngrid)
  tmp0 <- circ(x0, y0, xmax)
  tmp1 <- circ(x1, y1, xmax)

  # Get colors for the cytoband
  c0 <- rep(0, ngrid)
  chr.stop.pos <- c(cyto.end[chr.end], cyto.end[length(cyto.end)])
  delta.gridpts <- c()
  for (i in 1:ngrid) {
    grid.chr <- which(chr.stop.pos >= gridpts[i])[1]
    # Check if we are within chromosome or in the delta-region added at the end of each chrom
    if (gridpts[i] <= (chr.stop.pos[grid.chr] - delta)) {
      band <- which(cyto.end >= gridpts[i])[1]
      c0[i] <- switch(cyto.stain[band],
        "gneg" = "white",
        "gpos100" = "black",
        "gpos75" = "gray25",
        "gpos50" = "gray50",
        "gpos25" = "gray75",
        "stalk" = "gray90",
        "gvar" = "grey",
        "acen" = "yellow"
      )
    } else {
      delta.gridpts <- c(delta.gridpts, i)
      # Between chromosomes the color should be white
      c0[i] <- "white"
    }
  }
  # Plot cytobands:
  segments(tmp0$x[-delta.gridpts], tmp0$y[-delta.gridpts], tmp1$x[-delta.gridpts], tmp1$y[-delta.gridpts], col = c0[-delta.gridpts], lwd = 2)

  # Plot outer circle
  x <- seq(0, 1, len = 2000)
  y <- rep(outer.circ, 2000)
  c.lines(x, y, xmax = 1)

  # Plot inner circle
  x <- seq(0, 1, len = 2000)
  y <- rep(inner.circ, 2000)
  c.lines(x, y, xmax = 1)

  # Plot the white area between each chromosome:
  segments(tmp0$x[delta.gridpts], tmp0$y[delta.gridpts], tmp1$x[delta.gridpts], tmp1$y[delta.gridpts], col = c0[delta.gridpts], lwd = 2)

  # Plot chromosome borders
  borders <- c(chr.stop.pos, chr.stop.pos - delta)
  x0 <- borders
  x1 <- x0
  y0 <- rep(outer.circ - 0.04, length(borders)) # How much lines separating chromosomes will point out of the outer circle
  y1 <- rep(inner.circ, length(borders))
  tmp0 <- circ(x0, y0, xmax)
  tmp1 <- circ(x1, y1, xmax)
  segments(tmp0$x, tmp0$y, tmp1$x, tmp1$y, lwd = 3)


  # Plot chromosome numbers
  chrmiddle <- (chr.stop.pos[1:23] + (chr.stop.pos - delta)[2:24]) / 2
  chrmiddle <- c((chr.stop.pos[1] - delta) / 2, chrmiddle) # add middle of chrom 1
  x0 <- chrmiddle
  y0 <- rep(outer.circ - 0.1, length(chrmiddle))
  tmp <- circ(x0, y0, xmax)
  text(tmp$x, tmp$y, c(1:22, "X", "Y"))

  # Plot frequency of aberration
  # Check data input:
  # Make sure data input is a data frame (could be a list if it comes from segmentation algorithm or winsorize). Could also be a segments data frame or a data frame with original data.
  # data <- pullOutContent(res=data,what="estimates")
  data <- segments
  if (is.data.frame(data)) {
    # Check if segments or data:
    data <- getFreqData(data)
  } else {
    data <- pullOutContent(res = data, what = "estimates")
  }
  stopifnot(ncol(data) >= 3) # something is missing in input data

  # Make sure that chromosomes in data are numeric:
  chr <- numericChrom(data[, 1])
  pos <- data[, 2]
  yhat <- data[, -c(1:2)]
  freq.circ <- 1 / 7 + inner.circ # placement of frequencies-circle
  m <- freq.circ + (freq.circ - inner.circ) # max space for amp frequencies
  AmpFreq <- apply(yhat > thres.gain, 1, mean)
  glob.pos <- getGlobPos(chr, pos, pos.unit, cytoband, delta = delta)
  x0 <- glob.pos
  y0 <- rep(freq.circ, length(x0))
  x1 <- x0
  y1 <- freq.circ + 0.01 + AmpFreq * alpha # Add 0.01 to avoid overlapping colors
  y1[y1 > m] <- m # make sure that frequencies don't exceed the maximum space; truncate large values to this max
  tmp0 <- circ(x0, y0, xmax)
  tmp1 <- circ(x1, y1, xmax)
  segments(tmp0$x, tmp0$y, tmp1$x, tmp1$y, col = freq.colors[1], lwd = 2)


  m <- freq.circ - (freq.circ - inner.circ) # max space for del frequencies
  DelFreq <- apply(yhat < thres.loss, 1, mean)
  x0 <- glob.pos
  y0 <- rep(freq.circ, length(x0))
  x1 <- x0
  y1 <- freq.circ - 0.01 - DelFreq * alpha # Subtract 0.01 to avoid overlapping colors
  y1[y1 < m] <- m # make sure that frequencies don't exceed the maximum space; truncate large values to this max
  tmp0 <- circ(x0, y0, xmax)
  tmp1 <- circ(x1, y1, xmax)
  segments(tmp0$x, tmp0$y, tmp1$x, tmp1$y, col = freq.colors[2], lwd = 2)

  # Plot x-axis (x-circle)
  x <- seq(0, 1, len = 2000)
  y <- rep(freq.circ, 2000)
  c.lines(x, y, xmax = 1)



  # Plot arcs if specified
  if (!is.null(arcs)) {
    cl <- arcs[, 5]
    u.cl <- sort(unique(cl))
    if (length(arc.colors) < length(u.cl)) {
      arc.colors <- rep(arc.colors, length(u.cl))
      warning("Number of colors in 'arc.colors' is fewer than number of unique classes in 'arcs'. Colors are reused.", call.s = FALSE)
    }
    chr0 <- arcs[, 1]
    chr1 <- arcs[, 3]
    pos0 <- arcs[, 2]
    pos1 <- arcs[, 4]
    x0 <- getGlobPos(chr0, pos0, pos.unit, cytoband, delta = delta)
    x1 <- getGlobPos(chr1, pos1, pos.unit, cytoband, delta = delta)
    m <- d + inner.circ # determine where arcs should start
    for (i in 1:length(x0)) {
      tmp <- circ(c(x0[i], 0, x1[i]), c(0.1, 1.0, 0.1), xmax)
      tmp <- circ(c(x0[i], 0, x1[i]), c(m, 1.0, m), xmax)
      a0 <- xspline(tmp$x, tmp$y, shape = c(0, 1, 0), draw = F)
      lines(a0$x, a0$y, col = arc.colors[which(u.cl == cl[i])])
    }
  } # endif
}

# Circos transformation

circ <- function(x, y, xmax) {
  x <- x * 2 * pi / xmax
  xnew <- (1 - y) * cos(-x + pi / 2)
  ynew <- (1 - y) * sin(-x + pi / 2)
  list(x = xnew, y = ynew)
}


c.lines <- function(x, y, xmax, ...) {
  tmp <- circ(x, y, xmax)
  lines(tmp$x, tmp$y, ...)
}
igordot/copynumber documentation built on Feb. 23, 2025, 1:47 a.m.