R - Как построить ggplot2 с двумя осями y в разных масштабах * с переменными времени - PullRequest
0 голосов
/ 13 октября 2019

Я могу сделать ggplot2 график с той же осью X (скажем, год), но с разными осями Y (в очень разных масштабах. Можно ли использовать gganimate для анимации двух линий, каждая из которых соответствуетего собственная ось Y. Мне удалось создать две линии, используя одну и ту же ось Y, но я не могу понять, как использовать две оси.

Я думаю, что проблема в моем конкретном случае может быть связана ск тому факту, что мои переменные по оси Y имеют формат POSIX.

Скажем, я создаю набор данных a следующим образом:

library(ggplot2)
library(gganimate)
library(htmltab)
library(lubridate)

#marathon
data0 <- htmltab("https://en.wikipedia.org/wiki/Marathon_world_record_progression",1)
data <- data0[,c(1,4)]
#remove ones that are ARRS only
data <- data[-c(9,12,13,22,27,33,34,35,36,51),]
#data <- data %>% mutate(time = Time %>% hms())
data$time2 <- as.POSIXct(data$Time, format = "%H:%M:%S")
data$date <- mdy(data$Date)
data$race <- "Marathon"

#mile
mile0 <- htmltab("https://en.wikipedia.org/wiki/Mile_run_world_record_progression",4)
mile <- mile0[,c(1,4)]
#mile <- mile0 %>% mutate(time = Time %>% ms())
mile$time2 <-  as.POSIXct(mile$Time, format = "%M:%S")
mile$date <- dmy(mile$Date)
mile$race <- "Mile"

marathon <- data[,c(3,4)]
names(marathon)[1]<-"marathon"

mile2 <- mile[,c(3,4)]
names(mile2)[1]<-"mile"
a <- merge(marathon, mile2, by="date", all=TRUE)

Затем я могу получить gganimate анимацию дляработать следующим образом:

ggplot(a) +
    geom_point(aes(x=date, y=marathon, group=date, color="blue")) +
    geom_point(aes(x=date, y=mile, group=date, color="red")) +
    scale_y_continuous(sec.axis = sec_axis(~./152, name = "CDF"), breaks=seq(0,150,25))
    transition_reveal(date)

Проблема заключается в том, что эти два находятся в очень разных масштабах (один составляет около 2-3 часов, а другой около 2,5-3,5 минут). Как я могу получить их натот же масштаб? Если бы они были в обычном формате, я мог бы сделать что-то вроде следующего:

ggplot(a) +
    geom_point(aes(x=date, y=marathon, group=date, color="blue")) +
    geom_point(aes(x=date, y=mile*65, group=date, color="red")) +
    scale_y_continuous(sec.axis = sec_axis(~./65, name = "Mile"), breaks=seq(0,150,25)) +
    transition_reveal(date)

Однако я получаю ошибку из-за формата POSIX, в котором находятся переменные y. Что я должен делать? (В идеале, я хотел бы получить их на весах, чтобыДиапазон калибровки каждой переменной в основном заполняет вертикальное расстояние.)

Для справки, вот результат из графика, который я хочу исправить:

enter image description here

Боюсь, что это невозможно. См. https://ggplot2.tidyverse.org/reference/sec_axis.html:

"Начиная с v3.1, шкалы даты и даты-времени имеют ограниченные возможности для вспомогательных осей. В отличие от других непрерывных шкал, преобразования вторичной оси для шкал даты и даты-времени должны соответствовать их основной структуре данных POSIX. Это означает, чтоони могут быть преобразованы только путем сложения или вычитания, например, ~. + hms :: hms (days = 8) или ~. - 8 * 60 * 60. Нелинейные преобразования возвращают ошибку. Для создания вторичного события время с момента событияВ этом контексте пользователи могут подумать об адаптации меток вторичной оси. "

1 Ответ

1 голос
/ 13 октября 2019

Один из подходов - преобразовать время в десятичные часы (или минуты и т. Д.) И настроить метки шкалы:

library(dplyr);  library(lubridate)
a %>%
  # tidyr::gather(type, time, -date) %>% 
  tidyr::pivot_longer(-date, "type", "time") %>%   # Preferred syntax since tidyr 1.0.0
  mutate(time_dec = hour(value) + minute(value)/60 + second(value)/3600,
         time_scaled = time_dec * if_else(type == "mile", 30, 1)) %>% 
  ggplot() +
  geom_point(aes(x=date, y=time_scaled, group=value, color = type)) +
  scale_y_continuous(breaks = 0:3, 
                     labels = c("0", "1:00", "2:00", "3:00"),
                     name = "Marathon",
                     sec.axis = sec_axis(~./30, 
                                         name = "Mile", 
                                         breaks = (1/60)*0:100,
                                         labels = 0:100)) +
  expand_limits(y = c(1.5,3)) +
  transition_reveal(date)

enter image description here

...