Анимированная гистограмма: проблема с перекрытием баров - PullRequest
0 голосов
/ 28 февраля 2019

Я создал анимированную гистограмму, которая отображает забитые голы некоторых игроков.Ниже весь код отображается как я пришел к выводу.

Анимация работает как хотелось бы.Тем не менее, бары с одинаковым значением перекрываются.

Я бы хотел предотвратить перекрытие баров.В лучшем случае игрок, набравший первый балл, будет отображаться над другими игроками того же ранга.

Порядок игроков, набравших одинаковые очки в начале анимации, не имеет значения.

library(tidyverse)
library(gganimate)
theme_set(theme_classic())

df <- data.frame(Player = rep(c("Aguero", "Salah", "Aubameyang", "Kane"), 6), 
                 Team = rep(c("ManCity", "Liverpool", "Arsenal", "Tottenham"), 6), 
                 Gameday = c(1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6),
                 Goals = c(0,1,2,0,1,1,3,1,2,1,3,2,2,2,4,3,3,2,4,5,5,3,5,6),
                 stringsAsFactors = F)

gap <- df %>%
  group_by(Gameday) %>%
  mutate(rank = min_rank(-Goals) * 1,
     Value_rel = Goals/Goals[rank==1],
     Value_lbl = paste0(" ", Goals)) %>%
  filter(rank <=10) %>%
  ungroup()

p <- ggplot(gap, aes(rank, group = Player, stat = "identity",
                 fill = as.factor(Player), color = as.factor(Player))) +
  geom_tile(aes(y = Goals/2,
            height = Goals,
            width = 0.9), alpha = 0.8, color = NA) +
  geom_text(aes(y = 0, label = paste(Player, " ")), vjust = 0.2, hjust = 1) +
  geom_text(aes(y=Goals,label = Value_lbl, hjust=0)) +
  coord_flip(clip = "off", expand = FALSE) +
  scale_y_continuous(labels = scales::comma) +
  scale_x_reverse() +
  guides(color = FALSE, fill = FALSE) +
  labs(title = "Gameday {closest_state}", x="", y = "Goals scored") +
  theme(plot.title = element_text(hjust = 0, size = 22),
       axis.ticks.y = element_blank(),  # These relate to the axes post-flip
       axis.text.y  = element_blank(),  # These relate to the axes post-flip
       plot.margin = margin(1,1,1,4, "cm")) +
  transition_states(Gameday, transition_length = 4, state_length = 1) +
  ease_aes('cubic-in-out')

p

Код выводит следующий график:

enter image description here

Дополнительное примечание:

В концестолбцы должны отображаться в соответствии с примером ниже.Желательно, чтобы планки не были на одной высоте, чтобы повысить удобочитаемость.

enter image description here

Большое спасибо за ваши усилия!

1 Ответ

0 голосов
/ 01 марта 2019

Отредактированное решение на основе уточнения :

new plot

gap %>%

  # for each player, note his the rank from his previous day
  group_by(Player) %>%
  arrange(Gameday) %>%
  mutate(prev.rank = lag(rank)) %>%
  ungroup() %>%

  # for every game day,
  # sort players by rank & break ties by previous day's rank
  group_by(Gameday) %>%
  arrange(rank, prev.rank) %>%
  mutate(x = seq(1, n())) %>%
  ungroup() %>%

  ggplot(aes(x = x, y = Goals, fill = Player, color = Player)) +
  # geom_tile(aes(y = Goals/2, height = Goals, width = width)) +
  geom_col() +
  geom_text(aes(y = 0, label = Player), hjust = 1) +
  geom_text(aes(label = Value_lbl), hjust = 0) +

  # rest of the code below is unchanged from the question
  coord_flip(clip = "off", expand = FALSE) +
  scale_y_continuous(labels = scales::comma) +
  scale_x_reverse() +
  guides(color = FALSE, fill = FALSE) +
  labs(title = "Gameday {closest_state}", x="", y = "Goals scored") +
  theme(plot.title = element_text(hjust = 0, size = 22),
        axis.ticks.y = element_blank(), 
        axis.text.y  = element_blank(),
        plot.margin = margin(1,1,1,4, "cm")) +
  transition_states(Gameday, transition_length = 4, state_length = 1) +
  ease_aes('cubic-in-out')

Исходное решение :

plot

gap %>%

  # for each player, note his the rank from his previous day
  group_by(Player) %>%
  arrange(Gameday) %>%
  mutate(prev.rank = lag(rank)) %>%
  ungroup() %>%

  # for every game day & every rank,
  # reduce tile width if there are multiple players sharing that rank, 
  # sort players in order of who reached that rank first, 
  # & calculate the appropriate tile midpoint depending on how many players are there
  group_by(Gameday, rank) %>%
  mutate(n = n_distinct(Player)) %>%
  mutate(width = 0.9 / n_distinct(Player)) %>%
  arrange(prev.rank) %>%
  mutate(x = rank + 0.9 * (seq(1, 2 * n() - 1, by = 2) / 2 / n() - 0.5)) %>%
  ungroup() %>%

  ggplot(aes(x = x, fill = Player, color = Player)) +
  geom_tile(aes(y = Goals/2, height = Goals, width = width)) +
  geom_text(aes(y = 0, label = Player), hjust = 1) +
  geom_text(aes(y = Goals, label = Value_lbl), hjust = 0) +

  # rest of the code below is unchanged from the question
  coord_flip(clip = "off", expand = FALSE) +
  scale_y_continuous(labels = scales::comma) +
  scale_x_reverse() +
  guides(color = FALSE, fill = FALSE) +
  labs(title = "Gameday {closest_state}", x="", y = "Goals scored") +
  theme(plot.title = element_text(hjust = 0, size = 22),
        axis.ticks.y = element_blank(), 
        axis.text.y  = element_blank(),
        plot.margin = margin(1,1,1,4, "cm")) +
  transition_states(Gameday, transition_length = 4, state_length = 1) +
  ease_aes('cubic-in-out')

Примечание: это не идеально.Я полагаю, что приведенная выше простая логика для определения порядка игроков в тот же день / ранг не будет идеальной, если слишком много игроков / слишком много дней, так как она смотрит назад только на один день.Но это работает для этого примера, и я не знаю достаточно о футболе (по крайней мере, я думаю это футбол?), Чтобы экстраполировать ваш вариант использования.

...