Pipeline to Create Circos DE Plots from DESeq2 Output (Data_JuliaFuchs_RNAseq_2025)

  • circos_Moxi_18h_vs_Untreated_18h
  • circos_Mitomycin_18h_vs_Untreated_18h
  1. 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)
  1. touch circos/karyotype.txt
    chr - CP052959.1 CP052959.1 0 2706926 grey
  1. 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

Leave a Reply

Your email address will not be published. Required fields are marked *