Sam
Sam

Reputation: 319

how to prevent an overlapped segments in geom_segment

I'm trying to map different ranges (lines) into different regions in the plot (see below) using geom_segment but some of the ranges overlap and can't be shown at all. This is a minimal example for a dataframes:

start = c(1, 5,8, 14)
end =c(3, 6,12, 16)
regions = c(1,2,3, 4)

regions = data_frame(regions, start, end)

from = c(1,2, 5.5, 13.5)
to = c(3,2.5,6, 15)

lines = data_frame(from, to)

I plotted the regions with geom_rect and then plot the ranges (lines) with geom_segment. This is the plot:

plot_splice <- ggplot() +
  scale_x_continuous(breaks = seq(1,16)) +
  scale_y_continuous() +
  geom_hline(yintercept = 1.6,
             size = 20,
             alpha = 0.1) +
  geom_rect(
    data = regions,
    mapping = aes(
      xmin = start,
      xmax = end,
      ymin = 1.5,
      ymax = 1.8,
    )) +
  geom_segment(
    data = lines,
    x = (lines$from),
    xend = (lines$to),
    y = 1.48,
    yend = 1.48,
    colour = "red",
    size = 3
    ) +
  ylim(1.0, 2.2) + 
  xlab("") +
  theme_minimal() 

The first plot is the one generated with the code whereas the second one is the desired plot.

enter image description here

As you can see, the second line overlaps with the first one, so you can't see the second line at all.

How can I change the code to produce the second plot?

I'm trying to use ifelse statement but not sure what is test argument should be: I want it to check for each range (line) if it is overlapped with any previous range (line) to change the y position by around .05, so it doesn't overlap.

lines <- lines %>% 
  dplyr::arrange(desc(from))

new_y$lines = ifelse(from[1] < to[0], 1.48, 1.3)

geom_segment(
    data = lines,
    x = (lines$from),
    xend = (lines$to),
    y = new_y,
    yend = new_y,
    colour = "red",
    size = 3
    )

Upvotes: 2

Views: 1171

Answers (1)

Allan Cameron
Allan Cameron

Reputation: 173813

Your geom_segment call isn't using any aesthetic mapping, which is how you normally get ggplot elements to change position based on a particular variable (or set of variables).

The stacking of the geom_segment based on the number of overlapping regions is best calculated ahead of the call to ggplot. This allows you to pass the x and y values into an aesthetic mapping:

# First ensure that the data frame is ordered by the start time
lines <- lines[order(lines$from),]

# Now iterate through each row, calculating how many previous rows have
# earlier starts but haven't yet finished when the current row starts.
# Multiply this number by a small negative offset and add the 1.48 baseline value
lines$offset <- 1.48 - 0.03 * sapply(seq(nrow(lines)), function(i) {
   with(lines[seq(i),], length(which(from < from[i] & to > from[i])))
   })

Now do the same plot but using aesthetic mapping inside geom_segment:

 ggplot() +
  scale_x_continuous(breaks = seq(1,16), name = "") +
  scale_y_continuous(limits = c(1, 2.2), name = "") +
  geom_hline(yintercept = 1.6,
             size = 20,
             alpha = 0.1) +
  geom_rect(
    data = regions,
    mapping = aes(
      xmin = start,
      xmax = end,
      ymin = 1.5,
      ymax = 1.8,
    )) +
  geom_segment(
    data = lines,
    mapping = aes(
      x = from, 
      xend = to, 
      y = offset, 
      yend = offset),
    colour = "red",
    size = 3
    ) +
  theme_minimal()

enter image description here

Upvotes: 1

Related Questions