Последовательность цветовых блоков внутри области графика ggplot2 по определенным координатам

У меня есть простой линейный график, на котором я показываю последовательность ДНК по оси X, сделанную следующим образом с помощью ggplot2:

myseq <- "AGAATATTATACATTCATCT"
set.seed(123)
mydata <- data.frame(time=1:100, value=rnorm(100, mean=10, sd=2))
indices <- seq(5, 100, length.out=20)
seqsplit <- unlist(strsplit(myseq, ""))
ind_df <- data.frame(call=seqsplit, time=indices)
final_df <- dplyr::left_join(mydata, ind_df, by = "time")
xcolors <- ifelse(seqsplit= = "A", "green", ifelse(seqsplit= = "C", "blue", ifelse(seqsplit= = "G", "black", "red")))
P <- ggplot2::ggplot(final_df, ggplot2::aes(x=time, y=value)) +
  ggplot2::geom_line(linewidth=0.5) +
  ggplot2::scale_x_continuous(breaks=indices, labels=seqsplit) +
  ggplot2::scale_y_continuous(limits=c(5,17)) +
  ggplot2::theme_light() +
  ggplot2::theme(axis.title.x=ggplot2::element_blank(),
                 axis.text.x=ggtext::element_markdown(face = "bold", color=xcolors))
grDevices::pdf(file = "test.pdf", height=3, width=10)
print(P)
grDevices::dev.off()

который производит:

Теперь у меня есть связанная аминокислотная последовательность длиной 4, для которой я знаю начальное и конечное положения последовательности ДНК. Каждая буква аминокислотной последовательности соответствует 3 буквам последовательности ДНК.

aaseq <- "WXYZ"
start <- 5
end <- 17

Здесь аминокислотная последовательность WXYZ начинается на T-5 и заканчивается на C-17 последовательности ДНК, указанной выше, и я хочу построить их вместе.

Это будет моя конечная цель (это могут быть просто квадраты вместо «стрелок»):

Есть ли простой способ сделать это в ggplot2?


2
51
2

Ответы:

Решено

Самый простой вариант без стрелок — использовать geom_segment:

geom_segment(
  data = df_arrows,
  aes(x = x, xend = xend, y = 16, yend = 16, color = I(color)),
  linewidth = 8
)

Но если вам нужны стрелки, я бы посоветовал использовать geom_polygon, что, однако, требует некоторых усилий для создания кадра данных с координатами многоугольника:

library(ggplot2)
library(dplyr)

aaseq <- "WXYZ"
start <- 5
end <- 17

df_arrows <- data.frame(
  x = indices[seq(start, end - 3, 3)],
  xend = indices[seq(start + 3, end, 3)],
  y = 16, yend = 16,
  color = c("blue", "green", "orange", "purple"),
  label = strsplit(aaseq, "")[[1]]
)

df_polygon <- df_arrows |>
  dplyr::mutate(label = factor(label, rev(unique(label)))) |>
  dplyr::reframe(
    data.frame(
      x = c(x, xend, xend, xend, x) + c(0, 0, 4, 0, 0),
      y = y + .5 * c(1, 1, 0, -1, -1),
      color = color,
      label = label
    ),
    .by = label
  )

ggplot(final_df, aes(x = time, y = value)) +
  scale_x_continuous(breaks = indices, labels = seqsplit) +
  scale_y_continuous(limits = c(5, 17)) +
  theme_light() +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = ggtext::element_markdown(face = "bold", color = xcolors)
  ) +
  annotate(
    "rect",
    xmin = indices[start], xmax = indices[end],
    ymin = -Inf, ymax = Inf,
    fill = "grey", alpha = .4
  ) +
  geom_polygon(
    data = df_polygon,
    aes(x = x, y = y, fill = I(color), group = label)
  ) +
  geom_text(
    data = df_arrows,
    aes(x = (x + xend) / 2 + 1, y = y, label = label),
    color = "white", fontface = "bold"
  ) +
  geom_line(linewidth = 0.5)


Основываясь на ответе @stefan, я решил, что мне все еще нужно направление стрелки, но с моими реальными данными многоугольник стал настолько сложным...

Это могло бы войти в приложение Shiny, которое принимает в качестве входных данных разные данные, а значения y могут сильно различаться, поэтому определение многоугольника может стать путаницей.

Я поигрался с аргументом arrowgeom_segment, но не получил того, что хотел. Однако простое построение geom_point на xend с ромбовидной формой и размером, равным linewidth в geom_segment, делает идеальный трюк очень простым способом.

myseq <- "AGAATATTATACATTCATCT"
set.seed(123)
mydata <- data.frame(time=1:100, value=rnorm(100, mean=10, sd=2))
indexes <- seq(5, 100, length.out=20)
seqsplit <- unlist(strsplit(myseq, ""))
ind_df <- data.frame(call=seqsplit, time=indexes)
final_df <- dplyr::left_join(mydata, ind_df, by = "time")
xcolors <- ifelse(seqsplit= = "A", "green", ifelse(seqsplit= = "C", "blue", ifelse(seqsplit= = "G", "black", "red")))
#
#aa sequence
aaseq <- "WXYZ"
start <- 5
end <- 17
df_arrows <- data.frame(x=indexes[seq(start, end-3, 3)] -2.5,
                        xend=indexes[seq(start+3, end, 3)] -2.5,
                        y=16, yend=16,
                        color=c("blue", "green", "orange", "purple"),
                        label=strsplit(aaseq, "")[[1]])
##
P <- ggplot2::ggplot(final_df, ggplot2::aes(x=time, y=value)) +
  ggplot2::annotate("rect", xmin=indexes[start]-2.5, xmax=indexes[end]-2.5, ymin=-Inf, ymax=Inf, fill = "grey", alpha=0.25) +
  ggplot2::geom_vline(data=df_arrows[-1,], ggplot2::aes(xintercept=x), color = "grey", linetype=2, linewidth=0.5) +
  ggplot2::geom_line(linewidth=0.5) +
  ggplot2::scale_x_continuous(breaks=indexes, labels=seqsplit) +
  ggplot2::scale_y_continuous(limits=c(5,17)) +
  ggplot2::geom_segment(data=df_arrows, ggplot2::aes(x=x, xend=xend, y=16, yend=16, color=I(color)), linewidth=8) +
  ggplot2::geom_point(data=df_arrows, ggplot2::aes(x=xend, y=16, color=I(color)), shape=18, size=8) +
  ggplot2::geom_text(data=df_arrows, ggplot2::aes(x=(x+xend)/2+1, y=y, label=label), color = "white", fontface = "bold") +
  ggplot2::theme_light() +
  ggplot2::theme(axis.title.x=ggplot2::element_blank(),
                 axis.text.x=ggtext::element_markdown(face = "bold", color=xcolors))
grDevices::pdf(file = "test.pdf", height=3, width=10)
print(P)
grDevices::dev.off()

который производит: