ggplot2: creating a linear map (of transcription factor binding sites) consist with a line and filled boxes by using geom_segment and geom_text_repel

Viewed 204

I am trying to create a map of transcription factor binding sites on the promoter DNA sequence of some gene by using ggplot2 with geom_segment and geom_text_repel.

This is a a snippet of my data:

mincle.tf <- structure(list(
  Matrix.ID = c(
    "MA0007.3", "PB0118.1", "MA0608.1",
    "MA0474.1", "MA0608.1", "MA0098.2", "MA0007.3", "PB0012.1"
  ),
  Name = structure(c(1L, 5L, 2L, 4L, 2L, 6L, 1L, 3L), .Label = c(
    "Ar",
    "Creb3l2", "Elf3_1", "Erg", "Esrra_2", "Ets1"
  ), class = "factor"),
  Score = c(
    13.8552, 13.7763, 13.6251, 13.0501, 12.9081, 12.7074,
    12.6526, 12.4201
  ), Relative.score = c(
    0.8831708, 0.9177318,
    0.9736072, 0.9454676, 0.9613641, 0.8977955, 0.8696497, 0.9363881
  ), Sequence.ID = c(
    "seq1", "seq1", "seq1", "seq1", "seq1",
    "seq1", "seq1", "seq1"
  ), Start = c(
    603L, 505L, 28L, 873L,
    29L, 872L, 603L, 873L
  ), End = c(
    619L, 521L, 36L, 883L, 37L,
    886L, 619L, 885L
  ), Strand = c(
    "-", "+", "+", "-", "-", "+",
    "+", "-"
  ), Predicted.sequence = c(
    "gggaacataatgtgcat", "gtaaaggggtcagattc",
    "tccacgtgt", "agaggaaatga", "gacacgtgg", "atcatttcctctttt",
    "atgcacattatgttccc", "aaagaggaaatga"
  )
), row.names = c(
  NA,
  -8L
), class = "data.frame")

This is my code:

ggplot(mincle.tf, aes(Start, 3, label=Name)) + 
  geom_segment(aes(x=Start, y=3, xend=End, yend=3), size=10, data=mincle.tf[1:8], color=cbp2, alpha=0.5) + 
  geom_segment(aes(x = 0, y = 3, xend = 1200, yend = 3), size=1, color="black") + 
  geom_text_repel(data=mincle.tf[1:8],nudge_y = 0.1,point.padding = 0, box.padding = 0, direction = "x", angle=90, hjust=0.5, vjust=0.5)

This is the plot I get:

enter image description here

Basically, I am using geom_segment to draw a line of DNA (black) and colored boxes for TF binding sites, and using geom_text_repel to label each box according to the Name column in the data.

My issue is that the label lines start at the beginning of each box, instead of the center of the box, and I can't quite shorten the length of label lines by point.padding and box.padding. Also, some boxes overlap and I can't figure out the best way to show the overlap. I tried with transparency but was not successful (see a figure below).

enter image description here

At this point, I would really appreciate any suggestions and pointers to make improvements in this plot.

2 Answers

To adress the last question first, one way to show overlapping features is to put disjoint ranged features into distinct bins. You can calculate this with Bioconductor's IRanges package.

mincle.tf$bin <- with(mincle.tf, IRanges::disjointBins(IRanges::IRanges(Start, End))) - 1

You can use the calculated bin as an offset from the y positions. Note below the y = 3 + bin * 0.06, wherein the 0.06 is simply a scaling factor that fits with the plot.

To put the labels in the middle of the TFBSs you can set the x-position to (Start + End) / 2.

The reason you didn't seem able to shorten the lines of the labels, is because essentially your entire plot had near-0 width (in dataspace) on the y-axis. Expanding the y-axis allows you to shorten the label lines.

ggplot(mincle.tf, aes(Start, 3, label=Name)) + 
  geom_segment(aes(x=Start, y=3 + bin * 0.06, 
                   xend=End, yend=3 + bin * 0.06, colour = Matrix.ID), 
               size=10, data=mincle.tf[1:8,], alpha=0.5) + 
  geom_segment(aes(x = 0, y = 3, xend = 1200, yend = 3), size=1, color="black") + 
  geom_text_repel(data=mincle.tf[1:8,],
                  aes(x = (Start + End) / 2,
                      y = 3 + bin * 0.06),
                  nudge_y = 0.1,
                  point.padding = 0, box.padding = 0.5, direction = "x", 
                  angle=90, hjust=0.5, vjust=0.5) +
  scale_y_continuous(limits = c(2.5, 3.5))

enter image description here

Note that for the example above I didn't have your cbp2 variable so I substituted the PWM/PFM IDs (at least that is my guess what they are).

Update:

I expanded mincle.tf ;

str(mincle.tf)
Classes ‘data.table’ and 'data.frame':  49 obs. of  10 variables:
 $ Matrix ID         : chr  "PB0093.1" "PB0093.1" "MA0608.1" "MA0626.1" ...
 $ Name              : chr  "Zfp105_1" "Zfp105_1" "Creb3l2" "Npas2" ...
 $ Score             : num  14.6 14.2 13.6 13.5 13 ...
 $ Relative score    : num  0.968 0.959 0.974 0.993 0.963 ...
 $ Sequence ID       : chr  "seq1" "seq1" "seq1" "seq1" ...
 $ Start             : int  708 705 28 28 205 29 29 205 616 616 ...
 $ End               : int  722 719 36 37 215 37 36 215 627 627 ...
 $ Strand            : chr  "+" "+" "+" "+" ...
 $ Predicted sequence: chr  "aaaaaacaacaagaa" "aacaaaaaacaacaa" "tccacgtgt" "tccacgtgtc" ...
 $ bin               : num  1 0 2 3 0 4 0 1 1 2 ...
 - attr(*, ".internal.selfref")=<externalptr> 
 - attr(*, "index")= int(0) 
  ..- attr(*, "__Strand")= int [1:49] 1 2 3 4 5 7 8 9 12 16 ...

and have made a few changes to the ggplot 2 script as follows

    mycolors <- colorRampPalette(brewer.pal(8, "Set2"))(27)
    ggplot(mincle.tf, aes(Start, 3, label=Name, color=Name)) 
        +geom_segment(aes(x=-Start, y=2.8, xend=-End, yend=2.8), size=10, data=mincle.tf[Strand=="-" & Score > 9], alpha=0.5)
        +geom_segment(aes(x=-Start, y=3.2, xend=-End, yend=3.2), size=10, data=mincle.tf[Strand=="+" & Score > 9], alpha=0.5)
        +scale_color_manual(values = mycolors)+geom_segment(aes(x = 0, y = 3, xend = -1000, yend = 3), size=1, color="black")
        +geom_text_repel(data=mincle.tf[Strand=="+" & Score > 9],aes(-Start, 3.2), nudge_y = 0.15, point.padding = 0.8, box.padding = 0.8, direction = "x", angle=90, hjust=0.5, vjust=1)
        +geom_text_repel(data=mincle.tf[Strand=="-" & Score > 9],aes(-Start, 2.8), nudge_y = -0.1, point.padding = 0.5, box.padding = 0.5, direction = "x", angle=90, hjust=0.5, vjust=1)+ylim(2.5, 3.5)
        +geom_segment(aes(x=-pos, y=2.98, xend=-pos, yend=3.02), size=0.5, color="black", data = dt3, inherit.aes = FALSE)
        +annotate("text", x=-dt3$pos, y=2.95, label=-dt3$pos, size=3)
        +annotate("text", x=-500, y=2.55, label="- Strand", size=6)
            +guides(color = FALSE, size = FALSE)+annotate("text", x=-500, y=3.45, label="+ Strand", size=6)
            +guides(color = FALSE, size = FALSE)+theme_void()

A resulting map of TF within the promoter sequence in question is following; enter image description here

I am interested in only TFBS(predicted score) > 9 and separated TFs based on whether they bind + or - strand.

I incorporated @teunbrand's suggestion to generate "bins" by using IRange to stack up overlapping TF binding sites as follows;

ggplot(mincle.tf, aes(Start, 3, label=Name, color=Name)) 
+geom_segment(aes(x=-Start, y=2.6+bin*0.03, xend=-End, yend=2.6+bin*0.03), size=10, data=mincle.tf[Strand=="-" & Score > 9], alpha=0.5)
+geom_segment(aes(x=-Start, y=3.2+bin*0.06, xend=-End, yend=3.2+bin*0.06), size=10, data=mincle.tf[Strand=="+" & Score > 9], alpha=0.5)
+scale_color_manual(values = mycolors)+geom_segment(aes(x = 0, y = 3, xend = -1000, yend = 3), size=1, color="black")
+geom_text_repel(data=mincle.tf[Strand=="+" & Score > 9],aes(-Start, 3.2), nudge_y = 0.15, point.padding = 0.8, box.padding = 0.8, direction = "x", angle=90, hjust=0.5, vjust=1)
+geom_text_repel(data=mincle.tf[Strand=="-" & Score > 9],aes(-Start, 2.6+bin*0.03), nudge_y = -0.1, point.padding = 0.5, box.padding = 0.5, direction = "x", angle=90, hjust=0.5, vjust=1)+ylim(2.5, 3.5)
+geom_segment(aes(x=-pos, y=2.98, xend=-pos, yend=3.02), size=0.5, color="black", data = dt3, inherit.aes = FALSE)
+annotate("text", x=-dt3$pos, y=2.95, label=-dt3$pos, size=3)
+annotate("text", x=-500, y=2.1, label="- Strand", size=6)
+guides(color = FALSE, size = FALSE)+annotate("text", x=-500, y=3.6, label="+ Strand", size=6)
+guides(color = FALSE, size = FALSE)+theme_void()

and a resulting plot is as follows;

enter image description here

I still have to make a few mods to make it look better but it looks pretty decent. @teunbrand, I really appreciate your help.

Related