- Rscript make_circos_from_deseq.R
###############################
# Settings
###############################
# Your files
deseq_file <- "Mitomycin_18h_vs_Untreated_18h-all_annotated.csv"
bed_file <- "CP052959_m.bed"
options(scipen = 999)
# Output directory for Circos
out_dir <- "circos2"
dir.create(out_dir, showWarnings = FALSE)
dir.create(file.path(out_dir, "data"), showWarnings = FALSE)
# Thresholds for "strong" regulation
padj_cutoff <- 0.05
lfc_cutoff <- 1 # |log2FC| >= 1
# Bin size for density (bp)
bin_size <- 1e4 # 100 kb; adjust if you like
###############################
# 1. Read data
###############################
deseq <- read.csv(deseq_file, stringsAsFactors = FALSE)
# Check required columns in your DESeq2 table
stopifnot(all(c("GeneID", "log2FoldChange", "padj") %in% colnames(deseq)))
# Read BED (12-column BED from your file)
bed_cols <- c("chr","start","end","gene_id","score",
"strand","thickStart","thickEnd",
"itemRgb","blockCount","blockSizes","blockStarts")
annot <- read.table(bed_file, header = FALSE, sep = "\t",
stringsAsFactors = FALSE, col.names = bed_cols)
###############################
# 2. Merge DESeq2 + annotation
###############################
# Match DESeq2 GeneID (e.g. "gene-HJI06_09365") to BED gene_id
merged <- merge(
deseq,
annot[, c("chr", "start", "end", "gene_id")],
by.x = "GeneID",
by.y = "gene_id"
)
# Midpoint of each gene
merged$mid <- round((merged$start + merged$end) / 2)
###############################
# 3. Classify genes (up / down / ns)
###############################
merged$regulation <- "ns"
merged$regulation[merged$padj < padj_cutoff & merged$log2FoldChange >= lfc_cutoff] <- "up"
merged$regulation[merged$padj < padj_cutoff & merged$log2FoldChange <= -lfc_cutoff] <- "down"
###############################
# 4. Scatter file (per-gene log2FC)
###############################
scatter <- merged[merged$regulation != "ns", ]
scatter_file <- file.path(out_dir, "data", "genes_scatter.txt")
# Circos: chr start end value
scatter_out <- scatter[, c("chr", "mid", "mid", "log2FoldChange")]
write.table(scatter_out, scatter_file,
quote = FALSE, sep = "\t",
row.names = FALSE, col.names = FALSE)
###############################
# 5. Density files (binned counts)
###############################
bin_chr <- function(df_chr, bin_size, direction = c("up", "down")) {
direction <- match.arg(direction)
df_chr <- df_chr[df_chr$regulation == direction, ]
if (nrow(df_chr) == 0) return(NULL)
chr <- df_chr$chr[1]
max_pos <- max(df_chr$mid)
breaks <- seq(0, max_pos + bin_size, by = bin_size)
bins <- cut(df_chr$mid, breaks = breaks,
right = FALSE, include.lowest = TRUE)
counts <- tapply(df_chr$mid, bins, length)
starts <- head(breaks, -1)
ends <- starts + bin_size
data.frame(
chr = chr,
start = as.integer(starts),
end = as.integer(ends),
value = as.integer(ifelse(is.na(counts), 0, counts)),
stringsAsFactors = FALSE
)
}
chr_list <- split(merged, merged$chr)
density_up_list <- lapply(chr_list, bin_chr, bin_size = bin_size, direction = "up")
density_down_list <- lapply(chr_list, bin_chr, bin_size = bin_size, direction = "down")
density_up <- do.call(rbind, density_up_list)
density_down <- do.call(rbind, density_down_list)
density_up_file <- file.path(out_dir, "data", "density_up.txt")
density_down_file <- file.path(out_dir, "data", "density_down.txt")
write.table(density_up[, c("chr", "start", "end", "value")],
density_up_file,
quote = FALSE, sep = "\t",
row.names = FALSE, col.names = FALSE)
write.table(density_down[, c("chr", "start", "end", "value")],
density_down_file,
quote = FALSE, sep = "\t",
row.names = FALSE, col.names = FALSE)
- touch circos/karyotype.txt
chr - CP052959.1 CP052959.1 0 2706926 grey
- circos/circos.conf
karyotype = karyotype.txt
<ideogram>
<spacing>
default = 0.005r
</spacing>
radius = 0.80r
thickness = 20p
fill = yes
show_label = yes
label_radius = 1.05r
label_size = 30p
label_font = bold
label_parallel = yes
</ideogram>
# --- Wichtig: Schalter auf Top-Level, NICHT im
<ticks>-Block ---
show_ticks = yes
show_tick_labels = yes
<ticks>
# Starte direkt an der äußeren Ideogramm-Kante
radius = dims(ideogram,radius_outer)
orientation = out # Ticks nach außen zeichnen (oder 'in' für nach innen)
color = black
thickness = 2p
size = 8p
# kleine Ticks alle 100 kb, ohne Label
<tick>
spacing = 50000
size = 8p
thickness = 3p
color = black
show_label = no
</tick>
# große Ticks alle 500 kb, mit Label in Mb
<tick>
spacing = 100000
size = 12p
thickness = 5p
color = black
show_label = yes
label_size = 18p
label_offset = 6p
multiplier = 0.000001 # in Mb
format = %.1f
suffix = " Mb"
</tick>
</ticks>
<plots>
# Density of up-regulated genes
<plot>
show = yes
type = histogram
file = data/density_up.txt
r0 = 0.88r
r1 = 0.78r
fill_color = red
thickness = 1p
</plot>
# Density of down-regulated genes
<plot>
show = yes
type = histogram
file = data/density_down.txt
r0 = 0.78r
r1 = 0.68r
fill_color = blue
thickness = 1p
</plot>
# Scatter of individual significantly DE genes
<plot>
show = yes
type = scatter
file = data/genes_scatter.txt
r0 = 0.46r
r1 = 0.76r
glyph = circle
glyph_size = 5p
stroke_thickness = 0
min = -15
max = 15
<rules>
<rule>
condition = var(value) > 0
color = red
</rule>
<rule>
condition = var(value) < 0
color = blue
</rule>
</rules>
</plot>
</plots>
<image>
<<include etc/image.conf>>
</image>
<<include etc/colors_fonts_patterns.conf>>
<<include etc/housekeeping.conf>>
4.
mamba activate circos-env
#mv circos circos_Mitomycin_18h_vs_Untreated_18h
mv circos circos_Moxi_18h_vs_Untreated_18h
cd circos_Moxi_18h_vs_Untreated_18h
circos -conf circos.conf
#/home/jhuang/mambaforge/envs/circos-env/bin/circos -conf /mnt/md1/DATA/Data_JuliaFuchs_RNAseq_2025/results/star_salmon/degenes/circos/circos.conf 
