Circular heat maps in R? - r

Similar questions have been asked here and here, however, none of the other answers solve my problem.
Im trying to join together two (or more) separate heat maps and turn them into a circle. Im trying to achieve something like the image below (which I made by following the circlize package tutorial found here:
In my data, I have multiple matrices, where each matrix represents a different year. I want to try and create a circular heat map (like the one in the image) where each section of the circular heatmap is a single year.
In my example below, I am just using 2 years (so 2 heat maps) but I cant seem to get it to work:
library(circlize)
# create matrix
mat1 <- matrix(runif(80), 10, 8)
mat2 <- matrix(runif(80), 10, 8)
rownames(mat1) <- rownames(mat2) <- paste0('a', 1:10)
colnames(mat1) <- colnames(mat2) <- paste0('b', 1:8)
# join together
matX <- cbind(mat1, mat2)
# set splits
split <- c(rep('a', 8), rep('b', 8))
split = factor(split, levels = unique(split))
# create circular heatmap
col_fun1 = colorRamp2(c(0, 0.5, 1), c("blue", "white", "red"))
circos.heatmap(matX, split = split, col = col_fun1, rownames.side = "inside")
circos.clear()
The above code makes:
Im not sure where I am going wrong!? As when I use the ComplexHeatmap package, I am splitting the matrices correctly, as shown below:
# using ComplexHeatmap package
library(ComplexHeatmap)
Heatmap(matX, column_split = split, show_row_dend = F, show_column_dend = F)
Any suggestions as to how I could achieve this?

Related

Phylogenetic tree ape too small

I am building a phylogenetic tree using data from NCBI taxonomy. The tree is quite simple, its aims is to show the relationship among a few Arthropods.
The problem is the tree looks very small and I can't seem to make its branches longer. I would also like to color some nodes (Ex: Pancrustaceans) but I don't know how to do this using ape.
Thanks for any help!
library(treeio)
library(ape)
treeText <- readLines('phyliptree.phy')
treeText <- paste0(treeText, collapse="")
tree <- read.tree(text = treeText) ## load tree
distMat <- cophenetic(tree) ## generate dist matrix
plot(tree, use.edge.length = TRUE,show.node.label = T, edge.width = 2, label.offset = 0.75, type = "cladogram", cex = 1, lwd=2)
Here are some pointers using the ape package. I am using a random tree as we don't have access to yours, but these examples should be easily adaptable to your problem. If your provide a reproducible example of a specific question, I could take another look.
First me make a random tree, add some species names, and plot it to show the numbers of nodes (both terminal and internal)
library(ape)
set.seed(123)
Tree <- rtree(10)
Tree$tip.label <- paste("Species", 1:10, sep="_")
plot.phylo(Tree)
nodelabels() # blue
tiplabels() # yellow
edgelabels() # green
Then, to color any node or edge of the tree, we can create a vector of colors and provide it to the appropriate *labels() function.
# using numbers for colors
node_colors <- rep(5:6, each=5)[1:9] # 9 internal nodes
edge_colors <- rep(3:4, each=9) # 18 branches
tip_colors <- rep(c(11,12,13), 4)
# plot:
plot.phylo(Tree, edge.color = edge_colors, tip.color = tip_colors)
nodelabels(pch = 21, bg = node_colors, cex=2)
To label just one node and the clade descending from it, we could do:
Nnode(Tree)
node_colors <- rep(NA, 9)
node_colors[7] <- "green"
node_shape <- ifelse(is.na(node_colors), NA, 21)
edge_colors <- rep("black", 18)
edge_colors[13:18] <- "green"
plot(Tree, edge.color = edge_colors, edge.width = 2, label.offset = .1)
nodelabels(pch=node_shape, bg=node_colors, cex=2)
Without your tree, it is harder to tell how to adjust the branches. One way is to reduce the size of the tip labels, so they take up less space. Another way might be to play around when saving the png or pdf.
There are other ways of doing these embellishments of trees, including the ggtree package.

How to combine state distribution plot and separate legend in traminer?

Plotting several clusters using seqdplot in TraMineR can make the legend messy, especially in combination with numerous states. This calls for additional options for modifying the legend which is available with the function seqlegend. However, I have a hard time combining a state distribution plot (seqdplot) with a separate modified legend (seqlegend). Ideally one wants to plot the clusters (e.g. 9) without a legend and then add the separate legend in the available bottom right row, but instead the separate legend is generating a new plot window. Can anyone help?
Here's an example using the biofam data. With the data I use in my own research the legend becomes much more messy since I have 11 states.
#Data
library(TraMineR)
library(WeightedCluster)
data(biofam)
biofam.seq <- seqdef(biofam[501:600, 10:25])
#OM distances
biofam.om <- seqdist(biofam.seq, method = "OM", indel = 3, sm = "TRATE")
#9 clusters
wardCluster <- hclust(as.dist(biofam.om), method = "ward.D2")
cluster9 <- cutree(wardCluster, k = 9)
#State distribution plot
seqdplot(biofam.seq, group = cluster9, with.legend = F)
#Separate legend
seqlegend(biofam.seq, title = "States", ncol = 2)
#Combine state distribution plot and separate legend
#??
Thank you.
The seqplot function does not allow to control the number of columns of the legend, nor does it allow to add a legend title. So you have to compose the plot yourself by generating a separated plot for each group with the legend disabled and adding the legend afterwards. Here is how you can do that:
cluster9 <- factor(cluster9)
levc <- levels(cluster9)
lev <- length(levc)
par(mfrow=c(5,2))
for (i in 1:lev)
seqdplot(biofam.seq[cluster9 == levc[i],], border=NA, main=levc[i], with.legend=FALSE)
seqlegend(biofam.seq, ncol=4, cex = 1.2, title='States')
========================
Update, Oct 1, 2018 =================
Since TraMineR V 2.0-9, the seqplot family of functions now support (when applicable) the argument ncol to control the number of columns in the legend. To add a title to the legend, you still have to proceed as shown above.
AFAIK seqlegend() doesn't work when the other plots you are plotting utilizes the groups arguments. In your case the only thing seqlegend() is adding is a title "States". If you are looking to add a legend so you can customize what is in the legend and so forth, you can accomplish that by providing the corresponding alphabet and states that are used in your analysis.
The package's website has several walkthroughs and guides enumerating the various options and so forth: Link to their webiste
#Data
library(TraMineR)
library(WeightedCluster)
data(biofam)
## Generate alphabet and states
alphabet <- 0:7
states <- letters[seq_along(alphabet)]
biofam.seq <- seqdef(biofam[501:600, 10:25], states = states, alphabet = alphabet)
#OM distances
biofam.om <- seqdist(biofam.seq, method = "OM", indel = 3, sm = "TRATE")
#9 clusters
wardCluster <- hclust(as.dist(biofam.om), method = "ward.D2")
cluster9 <- cutree(wardCluster, k = 9)
#State distribution plot
seqdplot(biofam.seq, group = cluster9, with.legend = TRUE)

Add elements to a previous subplot within an active base R graphics device?

Let's say I generate 9 groups of data in a list data and plot them each with a for loop. I could use *apply here too, whichever you prefer.
data = list()
layout(mat = matrix(1:9, nrow = 3))
for(i in 1:9){
data[[i]] = rnorm(n = 100, mean = i, sd = 1)
plot(data[[i]])
}
After creating all the data, I want to decide which one is best:
best_data = which.min(sapply(data, sd))
Now I want to highlight that best data on the plot to distinguish it. Is there a plotting function that lets me go back to a specified sub-plot in the active device and add an element (maybe a title)?
I know I could make a second for loop: for loop 1 generates the data, then I assess which is best, then for loop 2 creates the plots, but this seems less efficient and more verbose.
Does such a plotting function exist for base R graphics?
#rawr's answer is simple and easy. But I thought I'd point out another option that allows you to select the "best" data set before you plot, in case you want more flexibility to plot the "best" data set differently from the rest.
For example:
# Create the data
data = lapply(1:9, function(i) rnorm(n = 100, mean = i, sd = 1))
par(mar=c(4,4,1,1))
layout(mat = matrix(1:9, nrow = 3))
rng = range(data)
# Plot each data frame
lapply(1:9, function(i) {
# Select data frame with lowest SD
best = which.min(sapply(data, sd))
# Highlight data frame with lowest SD by coloring points red
plot(data[[i]], col=ifelse(best==i,"red","black"), pch=ifelse(best==i, 3, 1), ylim=rng)
})

Heatmap vs image function in R

I just noticed that the plots from using heatmap() function and image() function look different even though I'm using the same data matrix. I have the following code:
set.seed(12345)
dataMatrix <- matrix(rnorm(400), nrow =40)
set.seed(678910)
for(i in 1:40) {
# flip a coin
coinFlip <- rbinom(1, size =1, prob =0.5)
# if coin is heads add a common pattern to that row
if(coinFlip) {
dataMatrix[i, ] <- dataMatrix[i, ] + rep(c(0,3), each =5)
}}
hh <- hclust(dist(dataMatrix))
dataMatrixOrdered <- dataMatrix[hh$order, ]
image(t(dataMatrixOrdered)[, nrow(dataMatrixOrdered):1])
heatmap(t(dataMatrixOrdered)[, nrow(dataMatrixOrdered):1])
As far as I have understood, the only difference between the 2 functions is that, the heatmap() function does a cluster analysis to produce dendrograms for rows and columns. Hence the plots should look the same. Can someone please tell me why I see this difference? I have attached the plots from the 2 functions.
Indeed the two functions produce consistently different patterns. I have no idea how that happens, but this happens whether you reorder the data or not.
Just to facilitate visualizing the differences, I turned dataMatrix into a distance matrix. The heatmap displaying this distance matrix should be fully symetrical. Image does give a symetric heatmap, heatmap does not.
set.seed(12345)
dataMatrix <- matrix(rnorm(400), nrow =40)
set.seed(678910)
for(i in 1:40) {
# flip a coin
coinFlip <- rbinom(1, size =1, prob =0.5)
# if coin is heads add a common pattern to that row
if(coinFlip) {
dataMatrix[i, ] <- dataMatrix[i, ] + rep(c(0,3), each =5)
}}
dataMatrix <- as.matrix(dist(dataMatrix))
image(dataMatrix)
heatmap(dataMatrix, Rowv = NA, Colv = NA)
What happens is that heatmap is scaling the rows before doing the coloring. If you avoid this scaling, the two functions give the same heatmap.
heatmap(dataMatrix, Rowv = NA, Colv = NA, scale = "none")
You can read more about this here

Printing plot depending on variable conditions on 2 pdf pages

I'am trying to print a plot, depending on a variable with 12 terms. This plot is the result of cluster classification on sequences, using OM distance.
I print this plot on one pdf page :
pdf("YYY.pdf", height=11,width=20)
seqIplot(XXX.seq, group=XXX$variable, cex.legend = 2, cex.plot = 1.5, border = NA, sortv =XXX.om)
dev.off()
But the printing is to small ... so i try to print this on 2 pages, like this :
pdf("YYY.pdf", height=11,width=20)
seqIplot(XXX.seq, group=XXX$variable, variable="1":"6", cex.legend = 2, cex.plot = 1.5, border = NA, sortv =XXX.om)
seqIplot(XXX.seq, group=XXX$variable, variable="7":"12", cex.legend = 2, cex.plot = 1.5, border = NA, sortv = XXX.om)
dev.off()
But it doesn't work ... Do you know how I can ask R to separate terms' variables into two groups, so as to print 6 graphics per pdf page ?
The solution is to plot separately the subset of groups you want on each page. Here is an example using the biofam data provided by TraMineR. The group variable p02r04 is religious participation which takes 10 different values.
library(TraMineR)
data(biofam)
bs <- seqdef(biofam[,10:25])
group <- factor(biofam$p02r04)
lv <- levels(group)
sel <- (group %in% lv[1:6])
seqIplot(bs[sel,], group=group[sel], sortv="from.end", withlegend=FALSE)
seqIplot(bs[!sel,], group=group[!sel], sortv="from.end")
If you are sorting the index plot with a variable you should indeed take the same subset of the sort variable, e.g. sortv=XXX.om[sel] in your case.
I don't know if I understood your question, you could post some data in order to help us reproduce what you want, maybe this helps. To plot six graphs in one page you should adjust the mfrow parameter, is that what you wanted?
pdf("test.pdf")
par(mfrow=c(3,2))
plot(1:10, 21:30)
plot(1:10, 21:30, pch=20)
hist(rnorm(1000))
barplot(VADeaths)
...
dev.off()

Resources