Как остановить перемещение ярлыков ggrepel между кадрами gganimate в R / ggplot2? - PullRequest
1 голос
/ 17 апреля 2019

Я хотел бы добавить метки в конец строк в ggplot, избегать их наложения и избегать их перемещения во время анимации.

Пока что я могу поместить метки в нужном месте и держать их статичными, используя geom_text, но метки перекрываются, или я могу предотвратить их наложение, используя geom_text_repel, но метки не появляются там, где я хочу, и затем потанцуйте, когда сюжет анимирован (последняя версия приведена в коде ниже).

Я думал, что решение может включать создание статического слоя в ggplot (p1 ниже), а затем добавление анимированного слоя (p2 ниже), но это не так.

Как мне сохранить некоторые элементы константы графика (то есть статические) в анимированном ggplot? (В этом случае метки в конце строк.)

Кроме того, при geom_text метки появляются так, как я хочу - в конце каждой строки, вне графика - но при geom_text_repel все метки перемещаются в области построения. Почему это?

Вот некоторые примеры данных:

library(dplyr)
library(ggplot2)
library(gganimate)
library(ggrepel)

set.seed(99)

# data
static_data <- data.frame(
  hline_label = c("fixed_label_1", "fixed_label_2", "fixed_label_3", "fixed_label_4", 
                  "fixed_label_5", "fixed_label_6", "fixed_label_7", "fixed_label_8", 
                  "fixed_label_9", "fixed_label_10"), 
  fixed_score = c(2.63, 2.45, 2.13, 2.29, 2.26, 2.34, 2.34, 2.11, 2.26, 2.37))

animated_data <- data.frame(condition = c("a", "b")) %>% 
  slice(rep(1:n(), each = 10)) %>% 
  group_by(condition) %>% 
  mutate(time_point = row_number()) %>% 
  ungroup() %>% 
  mutate(score = runif(20, 2, 3))

и это код, который я использую для своего анимированного сюжета:

# colours for use in plot
condition_colours <- c("red", "blue")

# plot static background layer 
p1 <- ggplot(static_data, aes(x = time_point)) +
  scale_x_continuous(breaks = seq(0, 10, by = 2), expand = c(0, 0)) + 
  scale_y_continuous(breaks = seq(2, 3, by = 0.10), limits = c(2, 3), expand = c(0, 0)) + 
  # add horizontal line to show existing scores
  geom_hline(aes(yintercept = fixed_score), alpha = 0.75) + 
  # add fixed labels to the end of lines (off plot)
  geom_text_repel(aes(x = 11, y = fixed_score, label = hline_label), 
                  hjust = 0, size = 4, direction = "y", box.padding = 1.0) +
  coord_cartesian(clip = 'off') +
  guides(col = F) +
  labs(title = "[Title Here]", x = "Time", y = "Mean score") + 
  theme_minimal() + 
  theme(panel.grid.minor = element_blank(),
        plot.margin = margin(5.5, 120, 5.5, 5.5))

# animated layer
p2 <- p1 + 
  geom_point(data = animated_data, 
             aes(x = time_point, y = score, colour = condition, group = condition)) +
  geom_line(data = animated_data, 
            aes(x = time_point, y = score, colour = condition, group = condition), 
            show.legend = FALSE) +
  scale_color_manual(values = condition_colours) + 
  geom_segment(data = animated_data, 
               aes(xend = time_point, yend = score, y = score, colour = condition),
               linetype = 2) + 
  geom_text(data = animated_data, 
            aes(x = max(time_point) + 1, y = score, label = condition, colour = condition), 
            hjust = 0, size = 4) + 
  transition_reveal(time_point) +
  ease_aes('linear') 

# render animation 
animate(p2, nframes = 50, end_pause = 5, height = 1000, width = 1250, res = 120)

1 Ответ

1 голос
/ 17 апреля 2019

Предложения к рассмотрению:

  1. Конкретное направление отталкивания / количество / и т.д. в geom_text_repel определяется случайным начальным числом. Вы можете установить seed на постоянное значение , чтобы получить одинаковые отталкиваемые позиции в каждом кадре анимации.

  2. Я не думаю, что отталкивающий текст может выйти за пределы области печати, даже если вы отключите обрезку и задаете некоторый диапазон отражения за пределами графика. Весь смысл этого пакета заключается в том, чтобы текстовые метки были удалены друг от друга, оставаясь в области графика. Однако вы можете расширить область печати и использовать geom_segment вместо geom_hline для построения горизонтальных линий, так что эти линии останавливаются до того, как они достигнут отталкиваемых текстовых меток.

  3. Поскольку существует больше слоев geom, использующих animated_data в качестве источника данных, было бы чище поставить animated_data и связанные с ними общие эстетические отображения на верхнем уровне ggplot() call , а не static_data.

Вот возможная реализация. Пояснения в аннотациях:

p3 <- ggplot(animated_data,
       aes(x = time_point, y = score, colour = condition, group = condition)) +

  # static layers (assuming 11 is the desired ending point)
  geom_segment(data = static_data,
               aes(x = 0, xend = 11, y = fixed_score, yend = fixed_score), 
               inherit.aes = FALSE, colour = "grey25") +
  geom_text_repel(data = static_data,
                  aes(x = 11, y = fixed_score, label = hline_label), 
                  hjust = 0, size = 4, direction = "y", box.padding = 1.0, inherit.aes = FALSE, 
                  seed = 123,           # set a constant random seed
                  xlim = c(11, NA)) +   # specify repel range to be from 11 onwards

  # animated layers (only specify additional aesthetic mappings not mentioned above)
  geom_point() +
  geom_line() +
  geom_segment(aes(xend = time_point, yend = score), linetype = 2) +
  geom_text(aes(x = max(time_point) + 1, label = condition),
            hjust = 0, size = 4) +

  # static aesthetic settings (limits / expand arguments are specified in coordinates
  # rather than scales, margin is no longer specified in theme since it's no longer
  # necessary)
  scale_x_continuous(breaks = seq(0, 10, by = 2)) +
  scale_y_continuous(breaks = seq(2, 3, by = 0.10)) + 
  scale_color_manual(values = condition_colours)  +
  coord_cartesian(xlim = c(0, 13), ylim = c(2, 3), expand = FALSE) +
  guides(col = F) +
  labs(title = "[Title Here]", x = "Time", y = "Mean score") + 
  theme_minimal() + 
  theme(panel.grid.minor = element_blank())  + 

  # animation settings (unchanged)
  transition_reveal(time_point) +
  ease_aes('linear') 

animate(p3, nframes = 50, end_pause = 5, height = 1000, width = 1250, res = 120)

result

...