Circular heatmap using circos.heatmap in R
1
0
Entering edit mode
16 days ago
Yukta • 0

I have this R script that helps generate a circular heatmap, but as I'm running it, I keep getting this gap.degree error. I am not able to understand what this error means. I tried searching for a solution, but I haven't found one that I can understand. Can someone please help me solve this issue? or understand it?

library(circlize)
library(ComplexHeatmap)
library(dendextend)
# Read VST counts
vst_data <- read.csv("ALL/L2FC1/VST_counts_genes_of_interest.csv", row.names = 1)

head(vst_data)

head(vst_data) result

# Convert to matrix and z-score scale by row
vst_matrix <- as.matrix(vst_data)
vst_matrix_scaled <- t(scale(t(vst_matrix)))  # z-score normalization

head(vst_matrix_scaled)

head(vst_matrix_scaled) result

Transpose: genes as columns, samples as rows for circular heatmap

mat_t <- t(vst_matrix_scaled)

head(mat_t)

heat(mat_t) result

Color gradient function

col_fun <- colorRamp2(c(-3, 0, 3), c("blue", "white", "red"))
na_col <- "grey80"  # Color for missing values
# Create dendrogram of genes
dend <- as.dendrogram(hclust(dist(vst_matrix_scaled)))
gene_order <- order.dendrogram(dend)
# Reorder matrix by gene clustering
mat_t <- mat_t[, gene_order]
# Fix: set gap.after based on number of samples (rows in mat_t)
sample_count <- nrow(mat_t)

head(sample_count)

[1] 15

circos.clear()
circos.par(start.degree = 90, gap.after = c(rep(2, sample_count - 1), 10))
# Draw circular heatmap
circos.heatmap(
  mat_t,
  col = col_fun,
  na.col = na_col,
  cluster = FALSE,
  dend.side = "inside",
  dend.track.height = 0.15,
  track.height = 0.08,
  rownames.side = "outside",
  show.sector.labels = TRUE,
  heatmap_limits = c(-3, 3)
)

Error: Since gap.degree parameter has length larger than 1, it should have same length as the number of sectors.

R circos heatmap circularheatmap • 597 views
ADD COMMENT
3
Entering edit mode
16 days ago

I think you have to tell the function where to make your cuts. Split your matrix by label and pass it to circos.heatmap.

split = rownames(mat_t)

circos.clear()
circos.par(start.degree = 90, gap.after = c(rep(2, sample_count - 1), 10))

# Draw circular heatmap
circos.heatmap(
  mat_t,
  split = split,
  col = col_fun,
  na.col = na_col,
  cluster = FALSE,
  dend.side = "inside",
  dend.track.height = 0.15,
  track.height = 0.08,
  rownames.side = "outside",
  show.sector.labels = TRUE,
  heatmap_limits = c(-3, 3)
)
ADD COMMENT
0
Entering edit mode

Thank you @Bastien Hervé I tried the same thing in my own way. The 'split' argument i tried using in circos.heatmap function only, it solved the graph.degree error but gave another new error.

Also I didn't want to transpose my data, so I went ahead without transposing it.

here is the script that I used:

# Step 1: Clear previous plots
circos.clear()

# Step 2: Set gap.after for 17 groups of 3 sectors (51 sectors total)
# 17 groups × 3 genes = 51 sectors
group_gaps <- rep(c(2, 2, 10), 17)  # Gives 51 elements: 2, 2, 10, 2, 2, 10, ..., 10
length(group_gaps)

# Step 3: Set circos parameters
circos.par(start.degree = 90, gap.after = group_gaps)

# Step 4: Draw the circular heatmap
circos.heatmap(
  vst_matrix_scaled,
  col = col_fun,
  na.col = na_col,
  cluster = FALSE,
  dend.side = "inside",
  dend.track.height = 0.15,
  track.height = 0.08,
  rownames.side = "outside",
  show.sector.labels = TRUE,
  heatmap_limits = c(-3, 3),
  split = rownames(vst_matrix_scaled)  # this ensures sectors align correctly
)

but there is new error pop up:

Error in circos.trackPlotRegion(...) : 
  unused argument (heatmap_limits = c(-3, 3))

and this is the heatmap generated :

heatmap result

ADD REPLY
0
Entering edit mode

What do you want to do ?

vst_data = rbind(cbind(matrix(rnorm(50*5, mean = 1), nr = 50), 
                   matrix(rnorm(50*5, mean = -1), nr = 50)),
             cbind(matrix(rnorm(50*5, mean = -1), nr = 50), 
                   matrix(rnorm(50*5, mean = 1), nr = 50))
            )
rownames(vst_data) = paste0("R", 1:100)
colnames(vst_data) = paste0("C", 1:10)

vst_data = vst_data[sample(100, 100), ]
vst_data = vst_data[1:15,]

vst_matrix <- as.matrix(vst_data)
vst_matrix_scaled <- t(scale(t(vst_matrix)))

dend <- as.dendrogram(hclust(dist(vst_matrix_scaled)))
gene_order <- order.dendrogram(dend)
mat_t <- mat_t[gene_order, ]

circos.clear()
circos.par(start.degree = 90, gap.after = rep(c(2, 2, 10), 5))

circos.heatmap(
  mat_t,
  split = rownames(mat_t),
  col = colorRamp2(c(-3, 0, 3), c("blue", "white", "red")),
  na.col = "grey80",
  cluster = FALSE,
  rownames.side = "outside"
)

circos

ADD REPLY
0
Entering edit mode

I've referred to this same example of circos.heatmap and generated the code, but what I need is a heatmap where the gene names should be displayed on the outer circle, and the treatments to be shown on one side of the heatmap, defining the different columns for expression.

Like this a reference heatmap that I generated from SR plot :

reference heatmap

ADD REPLY
2
Entering edit mode

Let's say this is your original dataframe :

vst_data = rbind(cbind(matrix(rnorm(50*5, mean = 1), nr = 50), 
                   matrix(rnorm(50*5, mean = -1), nr = 50)),
             cbind(matrix(rnorm(50*5, mean = -1), nr = 50), 
                   matrix(rnorm(50*5, mean = 1), nr = 50))
            )
rownames(vst_data) = paste0("R", 1:100)
colnames(vst_data) = paste0("C", 1:10)

vst_data = vst_data[sample(100, 100), ]
vst_data = vst_data[1:30,]

vst_matrix <- as.matrix(vst_data)
vst_matrix_scaled <- scale(t(vst_matrix))

mat_t <- t(vst_matrix_scaled)

dend <- as.dendrogram(hclust(dist(mat_t)))
gene_order <- order.dendrogram(dend)

mat_t <- mat_t[gene_order,]

sample_count <- nrow(mat_t)

col_fun <- colorRamp2(c(-3, 0, 3), c("blue", "white", "red"))

You were trying to use gap.after to leave white space between your genes. But as far as I know it is not possible without modifying circos.heatmap code.

One way to circonvent this is to split your matrix for each gene. However splitting your matrix that way trigger the clustering method of circos.heatmap on each individual split, raising an error message because you are trying to cluster each individual gene. It is still possible to plot it that way turning off the clustering.

circos.clear()
circos.par(start.degree = 0, gap.after = c(rep(2, sample_count - 1), 10))

circos.heatmap(
  mat_t,
  split = rownames(mat_t),
  col = col_fun,
  na.col = "grey80",
  clustering.method = "complete",
  distance.method = "euclidean",
  cluster = FALSE,
  dend.side = "inside",
  rownames.side = "outside",
  track.height = 0.5,
)
circos.track(track.index = get.current.track.index(), panel.fun = function(x, y) {
    if(CELL_META$sector.numeric.index == sample_count) { # the last sector
        cn = colnames(mat_t)
        n = length(cn)
        circos.text(rep(CELL_META$cell.xlim[2], n) + convert_x(1, "mm"), 
            1:n - 0.5, cn, 
            cex = 0.5, adj = c(0, 0.5), facing = "inside")
    }
}, bg.border = NA)

lgd = Legend(title = "mat", col_fun = col_fun)
grid.draw(lgd)

circos1

A second way is to forget about white spaces between genes, then the clustering is based on all your genes and your don't need to split anything.

circos.clear()
circos.par(start.degree = 0, gap.after = 10)

circos.heatmap(
  mat_t,
  col = col_fun,
  na.col = "grey80",
  clustering.method = "complete",
  distance.method = "euclidean",
  cluster = TRUE,
  dend.side = "inside",
  rownames.side = "outside",
  track.height = 0.5,
)
circos.track(track.index = 2, panel.fun = function(x, y) {
    if(CELL_META$sector.numeric.index == 1) { # the last sector
        cn = colnames(mat_t)
        n = length(cn)
        circos.text(rep(CELL_META$cell.xlim[2], n) + convert_x(1, "mm"), 
            1:n - 0.5, cn, 
            cex = 0.5, adj = c(0, 0.5), facing = "inside")
    }
}, bg.border = NA)

lgd = Legend(title = "mat", col_fun = col_fun)
grid.draw(lgd)

circos2

ADD REPLY
0
Entering edit mode

@Bastien Herve thank you for the help, I understood the gap.degree and the clustering method. The code worked for me with my data also.

ADD REPLY
1
Entering edit mode

Please consider accepting the original answer (green check mark) to provide closure to this thread).

ADD REPLY

Login before adding your answer.

Traffic: 2811 users visited in the last hour
Help About
FAQ
Access RSS
API
Stats

Use of this site constitutes acceptance of our User Agreement and Privacy Policy.

Powered by the version 2.3.6