make_circlize_plot <- function(dat, resolution = 15000, save_to = NULL) {
stage_list <- read_excel(configs$data$PCR$data_dict, sheet = "Stage")$Name
layer_list <- read_excel(configs$data$PCR$data_dict, sheet = "Layer")$Name |> discard(\(x) x == "Whole")
pathway_families <- read_excel(configs$data$PCR$data_dict, sheet = "Pathway_family")$Name
crossed_layer_order <- (crossing(Stage = stage_list, Layer = layer_list) |>
mutate(across(c(Stage, Layer), \(x) to_factor(x, configs$data$PCR$data_dict))) |>
arrange(Stage, Layer) |>
unite(c(Stage, Layer), col = "Layer", sep = "_", remove = FALSE) |>
pull(Layer))
circlize_data <- (dat |>
filter(p.value <= .05) |>
select(Stage, Layer, Gene, Pathway, Pathway_family, Fold, p.value, Expression) |>
unite(c(Stage, Layer), col = "Layer", sep = "_", remove = FALSE) |>
mutate(Layer = factor(Layer, levels = crossed_layer_order)) |>
arrange(Layer) |>
droplevels())
df_chord <- (circlize_data |>
distinct(Layer, Pathway, Gene) |>
summarize(N_reg = n(), .by = c(Layer, Pathway)) |>
mutate(Layer = as.character(Layer)))
mat_chord <- (xtabs(
N_reg ~ Layer + Pathway,
data = df_chord |>
mutate(
Layer = factor(Layer, levels = crossed_layer_order),
Pathway = to_factor(Pathway, configs$data$PCR$data_dict)
) |>
arrange(Layer),
drop.unused.levels = TRUE
))
df_layer <- (tibble(Sector = rownames(mat_chord)) |>
mutate(N_Connection = rowSums(mat_chord)) |>
filter(N_Connection > 0))
df_pathway <- (tibble(Sector = colnames(mat_chord)) |>
mutate(N_Connection = colSums(mat_chord)) |>
filter(N_Connection > 0))
qual_col_pals <- RColorBrewer::brewer.pal.info[RColorBrewer::brewer.pal.info$category == 'qual', ]
col_vector <- unlist(mapply(RColorBrewer::brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals)))
df_extra <- (bind_rows(df_layer, df_pathway) |>
mutate(
Color = col_vector[1:n()],
Layer = ifelse(str_detect(Sector, "P[0-9]+_"), str_remove(Sector, "P[0-9]+_"), NA),
Stage = str_extract(Sector, "P[0-9]+")
) |>
select(Sector, Layer, Stage, Color, N_Connection))
get_genes_barplot <- function(index, theta) {
return(
circlize_data |>
filter(Layer == index) |>
distinct(Gene, Fold, p.value, Expression) |>
mutate(
Orientation = theta,
Label = ifelse(
Orientation < 90 | Orientation > 270,
as.character(str_glue("{Gene} ({round(Fold, 2)}) {gtools::stars.pval(p.value)}")),
as.character(str_glue("{gtools::stars.pval(p.value)} ({round(Fold, 2)}) {Gene}"))
),
Fold = ifelse(
Fold >= 1,
scales::rescale(Fold, to = c(0, 1), from = c(1, max(circlize_data$Fold))),
scales::rescale(Fold, to = c(-1, 0), from = c(min(circlize_data$Fold), 1))
),
Color = ifelse(str_detect(Expression, regulation_type$UPREG), colors_fold[2], colors_fold[1])
) |>
arrange(Gene) |>
select(Fold, Label, Color)
)
}
# Drawing the plot
if (!is.null(save_to)) {
png(save_to, width = resolution, height = resolution)
}
circos.clear()
circos.par(cell.padding = c(0, 0, 0, 0), points.overflow.warning = FALSE, start.degree = 180)
colors <- c(
"#03937e",
"#02ccae",
"#86e3d5",
"#b9f0e7",
"#0264cc",
"#3680cf",
"#66a0de",
"#95bce6",
"#bed1e6",
"#5204b3",
"#7835cc",
"#9965db",
"#ac85de",
"#d1bbed",
"#76187a",
"#b625bb",
"#e37ae6",
"#eeb1f0",
"#8c013b",
"#b8024e",
"#f589b6",
"#f02b00",
"#faa491",
"#f59002",
"#ffda8f",
"#81c700",
"#d1eb52",
"#01944b",
"#78e3a1"
)
## Chord diagram
h0 <- resolution / 220
h1 <- resolution / 180
h2 <- resolution / 90
chord_plot <- circlize::chordDiagram(
as.matrix(mat_chord),
grid.col = colors,
directional = 1,
diffHeight = mm_h(h0 * 1.1),
target.prop.height = mm_h(h0),
direction.type = c("diffHeight", "arrows"),
link.arr.type = "big.arrow",
# big.gap = 10,
small.gap = 0.15,
transparency = 0.20,
annotationTrack = "grid",
preAllocateTracks = list(
list(
track.height = mm_h(h1)
), # Track 1 (or 3 ?)
list(
track.height = mm_h(h2),
track.margin = c(mm_h(h2), 0)
), # Track 2
list(
track.height = mm_h(h1)
) # Track 3 (or 1 ?)
),
annotationTrackHeight = mm_h(h1), # Track 4
scale = TRUE
)
## Track 4 : Layer/Stage legend
track_chord <- 4
cex_chord <- resolution / 1350
circos.track(
track.index = track_chord,
panel.fun = function(x, y) {
theta <- (circlize(mean(CELL_META$xlim), 1.3)[1, 1] %% 360)[[1]]
facing <- ifelse(theta > 220 && theta < 320, c("outside", "bending.outside"), c("inside", "bending.inside"))
circos.text(
CELL_META$xcenter,
CELL_META$ycenter,
str_remove(CELL_META$sector.index, "P[0-9]+_"),
col = "black",
font = 1,
cex = cex_chord,
adj = c(0.5, 0.5),
facing = facing
)
},
bg.border = NA
)
## Track 3 : Stage & Pathway family legend
track_families <- 3
cex_families <- resolution / 1200
colors.stages <- c("#03937e", "#0264cc", "#5204b3", "#76187a", "#8c013b")
for (stage in stage_list) {
highlight.sector(
sector.index = df_extra |> filter(Stage == stage) |> pull(Sector), # purrr::keep(df_extra$Sector, \(x) str_detect(x, "P4"))
track.index = track_families,
col = colors.stages[which(stage_list == stage)[[1]]],
text = stage,
cex = cex_families,
font = 2,
text.col = "white",
facing = "bending.inside",
niceFacing = FALSE
)
}
colors.pf <- c("#eb4f2d", "#83c108", "#f5a402", "#2cc969")
for (pf in pathway_families) {
circlize_data |> filter(Pathway_family == pf) |> distinct(Pathway) |> pull()
highlight.sector(
sector.index = circlize_data |> filter(Pathway_family == pf) |> distinct(Pathway) |> pull(),
track.index = track_families,
col = colors.pf[which(pathway_families == pf)[[1]]],
text = pf,
cex = cex_families,
font = 2,
text.col = "white",
facing = "bending.outside",
niceFacing = TRUE
)
}
## Track 2 : barplot
track_barplot <- 2
cex_barplot <- resolution / 2000
circos.track(
track.index = track_barplot,
panel.fun = \(x, y) {
if (str_detect(CELL_META$sector.index, "P[0-9]+_")) {
circos.segments(
x0 = 0,
x1 = 1,
y0 = 0,
y1 = 0,
lty = "solid"
)
draw.sector(
get.cell.meta.data("cell.start.degree", sector.index = CELL_META$sector.index),
get.cell.meta.data("cell.end.degree", sector.index = CELL_META$sector.index),
rou1 = get.cell.meta.data("cell.top.radius", track.index = track_barplot),
rou2 = get.cell.meta.data("cell.top.radius", track.index = track_barplot + 1) + mm_h(2)
)
theta <- (circlize(mean(CELL_META$xlim), 1.3)[1, 1] %% 360)[[1]]
genes_barplot <- get_genes_barplot(CELL_META$sector.index, theta)
# xpos <- seq(from = 0.1, to = 0.9, length.out = nrow(genes_barplot))
# xamp <- abs(CELL_META$cell.xlim[2] - CELL_META$cell.xlim[1])
xpos <- seq(
from = CELL_META$cell.xlim[1] + 0.05,
to = CELL_META$cell.xlim[2] - 0.05,
length.out = nrow(genes_barplot)
)
facing <- ifelse(theta < 90 || theta > 270, "clockwise", "reverse.clockwise")
adjust <- ifelse(theta < 90 || theta > 270, c(0, 0.5), c(1, 0.5))
if (nrow(genes_barplot) > 0) {
circos.barplot(
pos = xpos,
value = genes_barplot$Fold,
bar_width = 0.025,
col = genes_barplot$Color,
border = genes_barplot$Color
)
circos.text(
x = xpos,
y = get.cell.meta.data("cell.top.radius", track.index = track_barplot) + 0.2,
labels = genes_barplot$Label,
col = genes_barplot$Color,
font = 2,
cex = cex_barplot,
adj = c(adjust),
facing = c(facing)
)
}
}
},
bg.border = NA
)
## Track 1 : Empty (gene labels)
if (!is.null(save_to)) dev.off()
}