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 ()
}