Nothing
#' Plots cape results as a circular network
#'
#' This script plots cape results in a circular network.
#' The chromosomes are arranged in a circle. Main effects
#' are shown in concentric circles around the chromosomes,
#' with each trait in its own circle. Main effects can
#' either be colored as negative or positive, or with parental
#' allele colors for multi-parent populations.
#'
#' Interaction effects are shown as arrows linking chromosomal
#' positions. They are colored based on whether they are positive
#' or negative.
#'
#' @param data_obj a \code{\link{Cape}} object
#' @param marker_pairs a two-column matrix identifying which marker pairs should be plotted.
#' This is particularly useful if the network is very dense. The default value, NULL, plots
#' all marker pairs.
#' @param collapsed_net A logical value indicating whether to plot all individual SNPs
#' or linkage blocks calculated by \code{\link{linkage_blocks_network}}.
#' @param trait A character vector indicating which traits to plot. The default NULL
#' value plots all traits.
#' @param trait_labels A character vector indicating the names of the traits in case
#' the names from the data object are not great for plotting.
#' @param color_scheme A character value of either "DO/CC" or other indicating the
#' color scheme of main effects. If "DO/CC" allele effects can be plotted with the
#' DO/CC colors.
#' @param main_lwd A numeric value indicating the line width for the main effect lines
#' @param inter_lwd A numeric value indicating the line width for the interaction lines
#' @param label_cex A numeric value indicating the size of the labels
#' @param percent_bend A numeric value indicating the amount that the arrows for the
#' interaction effects should be bent. A value of 0 will plot straight lines.
#' @param chr_gap A numeric value indicating the size of the gap plotted between chromosomes.
#' @param label_gap A numeric value indicating the size of the gap the chromosomes and their labels.
#' @param positive_col One of c("green", "purple", "red", "orange", "blue", "brown", "yellow", "gray")
#' indicating the color for positive interactions.
#' @param negative_col One of c("green", "purple", "red", "orange", "blue", "brown", "yellow", "gray")
#' indicating the color for negative interactions.
#' show_alleles A logical value indicating whether to color main effects by their allele values (TRUE)
#' or by whether they are positive or negative (FALSE)
#' @param show_alleles boolean, default is TRUE
#'
#' @importFrom shape Arrowhead
#' @importFrom graphics xspline
#'
#' @examples
#' \dontrun{
#' plot_network(data_obj)
#' }
#' @export
plot_network <- function(data_obj, marker_pairs = NULL, collapsed_net = TRUE,
trait = NULL, trait_labels = NULL, color_scheme = c("DO/CC", "other"),
main_lwd = 4, inter_lwd = 3, label_cex = 1.5, percent_bend = 15,
chr_gap = 1, label_gap = 5, positive_col = "brown",
negative_col = "blue", show_alleles = TRUE){
oldPar <- par(no.readonly = TRUE)
on.exit(oldPar)
if(collapsed_net){
adj_mat <- data_obj$collapsed_net
blocks <- data_obj$linkage_blocks_collapsed
}else{
adj_mat <- data_obj$full_net
blocks <- data_obj$linkage_blocks_full
}
if(is.null(adj_mat)){
stop("get_network() must be run before plotting the collapsed network.")
}
pos_col <- get_color(positive_col)[3]
neg_col <- get_color(negative_col)[3]
circle_dens = 0.0005
center_x = 1; center_y = 1; radius = 2
# chr_rel_length <- c(98.5, 103.9, 82.7, 88.6, 90.2, 79, 89.1, 76.2, 75.1, 77.9, 88, 63.9, 67.3, 66.4, 59, 57.8, 61.3, 59.4, 56.9)
chr_rel_length <- c(1)
all_chr <- data_obj$chromosome
all_pos <- data_obj$marker_location
all_pos[which(all_pos == 0)] <- 1 #we can't place markers as position 0. Change any 0s to 1.
chr <- unique(all_chr)
num_true_chr = length(chr)
names(chr) <- rep("chr", num_true_chr)
covar_info <- get_covar(data_obj)
covar_names <- covar_info$covar_names
if(length(covar_names) > 1){
covar_pos1 <- segment_region(0, 0.9, length(covar_names))
covar_pos2 <- segment_region(0.1, 1, length(covar_names))
}else{
covar_pos1 <- 0.5
covar_pos2 <- 0.5
}
if(length(covar_names) > 0){
names(covar_names) <- rep("covar", length(covar_names))
chr <- c(covar_names, chr)
}
if(length(chr_rel_length) != num_true_chr){
# warning("The relative lengths of the chromosomes will not be plotted.")
rel_length <- rep(1, num_true_chr)
}else{
rel_length <- chr_rel_length/max(chr_rel_length)
}
if(length(covar_names) > 0){
rel_length <- c(rep(0.2, length(covar_names)), rel_length)
}
#============================================================================================
# internal functions
#============================================================================================
get_block_coord <- function(radius_coord, start, pts_per_chr, block_rel_locale, idx, chr_blocks_locale, ch){
coord_x <- radius_coord$x[start:(start+pts_per_chr[idx]-1)] #get the relative x and y coordinates for the block
coord_y <- radius_coord$y[start:(start+pts_per_chr[idx]-1)]
x_coord <- coord_x[round(length(coord_x)*as.numeric(block_rel_locale[2])):round(length(coord_x)*as.numeric(block_rel_locale[3]))]
y_coord <- coord_y[round(length(coord_x)*as.numeric(block_rel_locale[2])):round(length(coord_y)*as.numeric(block_rel_locale[3]))]
return(cbind(rep(names(chr_blocks_locale)[ch], length(x_coord)), x_coord, y_coord))
}
#assign a chromosome and relative position to each block
get_chr_pos <- function(block){
split_names <- strsplit(block, "_")
just_locus <- unlist(lapply(split_names, function(x) x[1]))
marker_locale <- which(data_obj$geno_names[[3]] %in% just_locus)
if(length(marker_locale) == 0){ #if this marker isn't in the names, it's probably a covariate
#look in the covariates
marker_locale <- which(covar_names == block)
return(c(0, covar_pos1[marker_locale], covar_pos2[marker_locale]))
}
chr <- unique(all_chr[marker_locale])
if(length(chr) > 1){
chr_char <- paste(chr, collapse = ", ")
stop(paste("There is linkage between markers on chromosomes ", chr_char,". Please try a high r2.thresh.", sep = ""))
}
min_pos <- min(as.numeric(all_pos[marker_locale]))
max_pos <- max(as.numeric(all_pos[marker_locale]))
total_length <- max(as.numeric(all_pos[all_chr == chr]), na.rm =TRUE)
return(c(chr, min_pos/total_length, max_pos/total_length))
}
get_block_col <- function(block, allele_colors){
markers <- blocks[[block]]
#blocks are split by allele, so we only need to look at the first entry
allele <- strsplit(markers, "_")[[1]][2]
allele_locale <- which(allele_colors[,2] == allele)
return(allele_colors[allele_locale,3])
}
#============================================================================================
# end internal functions
#============================================================================================
#get coordinates for the concentric circles we will use
chr_radius <- get_circle(radius, dens = circle_dens) #circle for chromosomes
#these need to change if we are showing allele colors
inner_bar_radius = get_circle(radius*0.98, dens = circle_dens) #circle for chr blocks if no alleles are shown
#divide into chromosomes
num_chr = length(chr)
gap = round((length(chr_radius$x)*chr_gap)/100) #number of values to skip for gap between chromosomes
label_gap <- round((length(chr_radius$x)*label_gap)/100)
full_length <- length(chr_radius$x) - (gap*num_chr) - label_gap
full_chr <- rel_length*(1/sum(rel_length))
pts_per_chr <- floor(full_length*full_chr)
if(!is.null(marker_pairs)){
source_chr <- names(blocks)[sapply(marker_pairs[,1], function(x) grep(x, blocks))]
target_chr <- names(blocks)[sapply(marker_pairs[,2], function(x) grep(x, blocks))]
marker_pairs <- cbind(source_chr, target_chr)
}else{
#if no marker pairs are specified, take all of them
just_int <- adj_mat[,1:nrow(adj_mat)]
marker_pairs <- which(just_int != 0, arr.ind = TRUE)
marker_pairs[,1] <- rownames(just_int)[as.numeric(marker_pairs[,1])]
marker_pairs[,2] <- rownames(just_int)[as.numeric(marker_pairs[,2])]
}
if(collapsed_net){
blocks <- data_obj$linkage_blocks_collapsed
#remove the blocks that were never tested
not_tested <- setdiff(names(blocks), rownames(adj_mat))
if(length(not_tested) > 0){
not_tested_locale <- match(not_tested, names(blocks))
blocks <- blocks[-not_tested_locale]
# TODO which line below is correct? The commented out line causes a bug in plot_full_network.R
# TODO if the collapsed_net line is correct, then the setdiff on line 140 should be reversed
# TODO and the collapsed_net object built again like it is in get_network line 152 .. 172
#data_obj$collapsed_net <- blocks
data_obj$linkage_blocks_collapsed <- blocks
}
}else{
blocks <- data_obj$linkage_blocks_full
#remove the blocks that were never tested
not_tested <- setdiff(names(blocks), rownames(adj_mat))
if(length(not_tested) > 0){
not_tested_locale <- match(not_tested, names(blocks))
blocks <- blocks[-not_tested_locale]
# TODO same as above...
#data_obj$full_net <- blocks
data_obj$linkage_blocks_full <- blocks
}
}
chr_pos <- t(sapply(blocks, get_chr_pos))
colnames(chr_pos) <- c("chromosome", "min_position", "max_position")
#and each phenotype
if(is.null(trait)){
pheno <- names(data_obj$max_var_to_pheno_influence)
}else{
pheno <- trait
trait_locale <- which(trait %in% names(data_obj$max_var_to_pheno_influence))
if(length(trait_locale) < length(trait)){
not_found <- which(!trait %in% names(data_obj$max_var_to_pheno_influence))
warning("I couldn't find the following traits:", paste(trait[not_found], collapse = ", "))
stop()
}
}
if(is.null(trait_labels)){
pheno_names <- pheno
}else{
pheno_names <- trait_labels
if(length(trait_labels) != length(pheno)){
stop("I'm detecting the wrong number of phenotype labels.")
}
}
#get the circle coordinates for each trait
#circles are generated from inner-most to
#outer-most
gap_rad = 0.05
start_rad <- radius + gap_rad
trait_circ <- get_concent_circ(pheno_names, start_rad = start_rad, gap_rad = gap_rad)
new_start_rad <- start_rad + (gap_rad*length(pheno_names))
#also get circles and colors for the different
#alleles if we are going to show them
alleles <- unique(sapply(strsplit(rownames(adj_mat), "_"), function(x) x[2]))
if(show_alleles){
allele_colors <- get_allele_colors(color_scheme, alleles)
}
label_radius = get_circle(new_start_rad+(gap_rad*3))
#if we need to filter chr_pos and adj_mat to include
#only the phenotypes we are including
all_block_pheno <- c(rownames(chr_pos), pheno)
adj_mat <- adj_mat[,colnames(adj_mat) %in% all_block_pheno, drop = FALSE]
main_effect_mat <- adj_mat[,which(colnames(adj_mat) %in% pheno), drop = FALSE]
chr_coord_table <- NULL #the coordinates of the chromosomes
block_coord_table <- NULL #the coordinates of the blocks for plotting interaction polygons
inner_bar_coord_table <- NULL #the coordinates of the blocks for plotting target bars
plot.new()
#give the right margin a bit more room to write covariate names
plot.window(xlim = c(min(label_radius$x), max(label_radius$x)*1.25), ylim = c(min(label_radius$y), max(label_radius$y)))
par(mar = c(2,2,2,0))
plot_dim <- par("usr")
#segment the y coordinates of the gap region
#in the outermost circle to evenly space the
#label sticks and gaps
#add trait circles
plot_trait_circ(trait_circ, label_gap, plot_dim, main_lwd)
start = label_gap
for(i in 1:length(chr)){
chr_x_coord <- chr_radius$x[start:(start+pts_per_chr[i]-1)]
chr_y_coord <- chr_radius$y[start:(start+pts_per_chr[i]-1)]
points(chr_x_coord, chr_y_coord, type = "l", lwd = main_lwd)
chr_coord_table <- rbind(chr_coord_table, cbind(rep(chr[i], length(chr_x_coord)), chr_x_coord, chr_y_coord))
if(names(chr)[i] == "covar"){
text_adj = 0
}else{
text_adj = 0.5
}
text(mean(label_radius$x[start:(start+pts_per_chr[i]-1)]), mean(label_radius$y[start:(start+pts_per_chr[i]-1)]), chr[i], adj = text_adj, cex = label_cex)
# get the number of blocks on this chromosome
if(names(chr)[i] == "covar"){
chr_blocks_locale <- which(rownames(chr_pos) == chr[i])
names(chr_blocks_locale) <- chr[i]
}else{
chr_blocks_locale <- which(chr_pos[,1] == chr[i])
}
if(length(chr_blocks_locale) > 0){
for(ch in 1:length(chr_blocks_locale)){
main_effects <- main_effect_mat[chr_blocks_locale[ch],,drop=FALSE]
block_rel_locale <- chr_pos[chr_blocks_locale[ch],,drop=FALSE]
for(ph in 1:length(pheno)){
if(main_effects[ph] != 0){ #if there are significant effects of this block, add them to the circle
if(show_alleles && names(chr)[i] == "chr"){
trait_col <- get_block_col(names(chr_blocks_locale)[ch], allele_colors = allele_colors)
}else{
if(main_effects[ph] < 0){trait_col = neg_col}else{trait_col = pos_col}
}
block_coord <- get_block_coord(radius_coord = trait_circ[[ph]], start, pts_per_chr, block_rel_locale, i, chr_blocks_locale, ch)
if(nrow(block_coord) > 1){
points(as.numeric(block_coord[,2]), as.numeric(block_coord[,3]), type = "l", lwd = main_lwd, col = trait_col)
}else{
points(as.numeric(block_coord[,2]), as.numeric(block_coord[,3]), type = "p", pch = 16, col = trait_col, cex = 0.7)
}
}
#collect positions of the blocks for polygons and inner target bars on slightly smaller circles
block_coord <- get_block_coord(inner_bar_radius, start, pts_per_chr, block_rel_locale, i, chr_blocks_locale, ch)
block_coord_table <- rbind(block_coord_table, block_coord)
inner_bar_coord_table <- rbind(inner_bar_coord_table, block_coord)
} #end looping through phenotypes
} #end looping through blocks
} #end case for if there are blocks on this chromosome
start = start + pts_per_chr[i] + gap
}
#add the interactions
just_m <- adj_mat[,-which(colnames(adj_mat) %in% pheno), drop = FALSE]
if(!is.null(just_m)){
new_mat <- matrix(0, nrow = nrow(just_m), ncol = ncol(just_m))
colnames(new_mat) <- colnames(just_m)
rownames(new_mat) <- rownames(just_m)
source_ind <- match(marker_pairs[,1], rownames(new_mat))
target_ind <- match(marker_pairs[,2], colnames(new_mat))
for(i in 1:nrow(marker_pairs)){
new_mat[source_ind[i], target_ind[i]] <- just_m[source_ind[i], target_ind[i]]
}
just_m <- new_mat
}
for(i in 1:nrow(just_m)){
sig_locale <- which(just_m[i,] != 0)
if(length(sig_locale) > 0){
for(s in 1:length(sig_locale)){
if(just_m[i,sig_locale[s]] > 0){edge_col <- pos_col}else{edge_col = neg_col}
#find the start and stop positions
start_block <- rownames(just_m)[i]
start_block_coord <- block_coord_table[which(block_coord_table[,1] == start_block),,drop=FALSE]
end_block <- colnames(just_m)[sig_locale[s]]
end_block_coord <- block_coord_table[which(block_coord_table[,1] == end_block),,drop=FALSE]
# #figure out the allele color(s) for the blocks
# start_block_alleles <- strsplit(start_block, "_")[[1]]
# end_block_alleles <- strsplit(end_block, "_")[[1]]
# start_block.id <- match(start_block_alleles, alleles)
# end_block.id <- match(end_block_alleles, alleles)
#draw a polygon to connect the start block and stop position
start_inter_x <- as.numeric(start_block_coord[,2])
start_inter_y <- as.numeric(start_block_coord[,3])
end_inter_x <- as.numeric(end_block_coord[,2])
end_inter_y <- as.numeric(end_block_coord[,3])
#sort the corners of the polygon according to their distance from the center of the circle
start_mat <- matrix(c(start_inter_x[1], start_inter_y[1], start_inter_x[length(start_inter_x)], start_inter_y[length(start_inter_y)]), ncol = 2, byrow = TRUE)
end_mat <- matrix(c(end_inter_x[1], end_inter_y[1], end_inter_x[length(end_inter_x)], end_inter_y[length(end_inter_y)]), ncol = 2, byrow = TRUE)
rownames(start_mat) <- c("dist.start.min", "dist.start.max")
rownames(end_mat) <- c("dist.end.min", "dist.end.max")
#add a bar to indicate the block at the source end of the interaction
start_bar_coord <- inner_bar_coord_table[which(inner_bar_coord_table[,1] == start_block),,drop=FALSE]
points(as.numeric(start_bar_coord[,2]), as.numeric(start_bar_coord[,3]), type = "l", lwd = main_lwd, col = "darkgray")
#add a bar to indicate the block at the target end of the interaction
end_bar_coord <- inner_bar_coord_table[which(inner_bar_coord_table[,1] == end_block),,drop=FALSE]
points(as.numeric(end_bar_coord[,2]), as.numeric(end_bar_coord[,3]), type = "l", lwd = main_lwd, col = "darkgray")
#find the midpoint of the line between source and target
mid_point_x <- mean(c(mean(start_inter_x), mean(end_inter_x)))
mid_point_y <- mean(c(mean(start_inter_y), mean(end_inter_y)))
#get the points on the line between the midpoint and the center of the circle
pts_to_center <- get_line(mid_point_x, mid_point_y, center_x, center_y, dens = circle_dens)
#find the point that makes this line the correct percentage long
shifted_x <- pts_to_center$x[round(((percent_bend/100))*length(pts_to_center$x))]
shifted_y <- pts_to_center$y[round(((percent_bend/100))*length(pts_to_center$y))]
if(length(shifted_x) == 0){shifted_x = mid_point_x}
if(length(shifted_y) == 0){shifted_y = mid_point_y}
#make a spline curve based on the start and stop of the line plus this shifted midpoint
inter_curve <- xspline(c(mean(start_inter_x), shifted_x, mean(end_inter_x)), y = c(mean(start_inter_y), shifted_y, mean(end_inter_y)), shape = 1, draw = FALSE)
points(inter_curve$x, inter_curve$y, col = edge_col, type = "l", lwd = inter_lwd)
#add an arrowhead pointed at the target
arrow_rad <- atan2((mean(end_inter_y)-shifted_y), (mean(end_inter_x)-shifted_x))
arrow_deg <- arrow_rad*(180/pi)
shape::Arrowhead(x0 = mean(end_inter_x), y0 = mean(end_inter_y), arr.col = edge_col, arr.adj = 1, lcol = edge_col, angle = arrow_deg, arr.lwd = inter_lwd)
}
} #end case for when there are significicant interactions in this row
}
legend("bottomright", lty = 1, lwd = inter_lwd, col = c(pos_col, neg_col), legend = c("Positive", "Negative"))
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.