Отсутствует легенда для ggplot - PullRequest
2 голосов
/ 03 августа 2020

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

Date        Actual Fcst1 Fcst2 Fcst3 Fcst4
2015-01-01  500    600   700   400   450
2015-02-01  600    610   630   480   600
2015-03-01  700    234   875   754   733
..........  ...    ...   ...   ...   ...

В настоящее время я использую этот код:

ggplot(df, aes(x = Date)) +
  geom_line(aes(y = Fcst1), color = "red", size = 1) +
  geom_line(aes(y = Fcst2),
    color = "blue",
    size = 1
  ) +
  geom_line(aes(y = Fcst3),
    color = "green",
    size = 1
  ) +
  geom_line(aes(y = Fcst4),
    color = "yellow",
    size = 1
  ) +
  geom_line(aes(y = Fcst5),
    color = "purple",
    size = 1
  ) +
  geom_line(aes(y = Fcst6), color = "orange", size = 1) +
  geom_line(aes(y = Actual), color = "black", size = 1.2) +
  ggtitle(label = "Actuals vs 2015 Forecasts", subtitle = fname) +
  ylab("Balance") +
  scale_y_continuous(labels = comma)

Я могу ' не добиться правильного отображения легенды, несмотря ни на что, даже когда я пытаюсь использовать расплав. Кто-нибудь может мне помочь?

1 Ответ

5 голосов
/ 03 августа 2020

ggplot2 предпочитает вещи в длинном формате и имеет тенденцию «puni sh» (усложнять), делая то, что вы делаете сейчас. Давайте изменим форму (я буду использовать tidyr::pivot_longer, другие будут работать так же).

library(ggplot2)
ggplot(tidyr::pivot_longer(df, Fcst1:Fcst4),
       aes(Date, value, color = name)) +
  geom_line()

basic ggplot2

As you can tell, using color= within an aesthetic varies the colors accordingly. If you want to control the colors, there are many themes available (e.g., viridis and many with color-blind profiles), but doing it manually is done with scale_color_manual, I'll demo below. Finally, I'll tweak the names and such a little.

ggplot(tidyr::pivot_longer(df, Actual:Fcst4, names_to = "Forecast", names_prefix = "Fcst"),
       aes(Date, value, color = Forecast)) +
  geom_line(size = 1) +
  scale_color_manual(values = c("Actual" = "black", "1" = "red", "2" = "blue",
                                "3" = "green", "4" = "yellow", "5" = "purple",
                                "6" = "orange")) +
  ggtitle(label = "Actuals vs 2015 Forecasts", subtitle = "(unk filename)") +
  ylab("Balance") +
  scale_y_continuous(labels = scales::comma)

Ручные цвета не обязательно быть идеальным совпадением, как вы можете видеть, если 5 определено, но не используется (на основе вашей выборки данных). Отсутствующие цвета в названном векторе values= будут удалены с графика (с предупреждением).

same ggplot2, updated theme

Finally, a common question is ordering the components in the legend. This can be done with factors:

df_long <- tidyr::pivot_longer(df, Actual:Fcst4, names_to = "Forecast", names_prefix = "Fcst")
df_long$Forecast <- relevel(factor(df_long$Forecast), "Actual")
ggplot(df_long, aes(Date, value, color = Forecast)) +
  geom_line(size = 1) +
  scale_color_manual(values = c("Actual" = "black", "1" = "red", "2" = "blue",
                                "3" = "green", "4" = "yellow", "5" = "purple",
                                "6" = "orange")) +
  ggtitle(label = "Actuals vs 2015 Forecasts", subtitle = "(unk filename)") +
  ylab("Balance") +
  scale_y_continuous(labels = scales::comma)

same ggplot2, reordered legend

I used stats::relevel to move one factor "to the front", otherwise it tends to be alphabetic (as shown in the second graphic above). There are many tools for working with factors, the forcats package is a popular one (esp among tidyverse users).

This processing could easily have been handled within a dplyr-pipe.


Since you mentioned plotting batches of forecasts at a time, here are a couple of approaches. I'll augment the data by copying the Fcst columns into another set of 4:

df <- cbind(df, setNames(df[,3:6], paste0("Fcst", 5:8)))
df_long <- tidyr::pivot_longer(df, Actual:Fcst8, names_to = "Forecast", names_prefix = "Fcst")
df_long$Forecast <- relevel(factor(df_long$Forecast), "Actual")

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

  1. Отдельные графики, фильтровать по одному и строить график ит.

    ggplot(df_long[df_long$Forecast %in% c("Actual", "1", "3", "5", "7"),],
           aes(Date, value, color = Forecast)) +
      geom_line(size = 1)
    
  2. Фацетирование. Я покажу метод грубой силы для этого примера, а затем более гибкий (возможно) способ. Я использую здесь dplyr, потому что он упрощает просмотр и понимание некоторых операций (как только вы привыкнете к синтаксису dplyr-esque). (Я часто нахожу, что сохранение контрольной линии «Фактическое», другого цвета / толщины, чем у других, помогает укрепить сравнения по граням. Перед вами.)

    library(dplyr)
    df_rest <- df_long %>%
      filter(! Forecast == "Actual") %>%
      mutate(grp = cut(as.integer(as.character(Forecast)), c(0, 5, 9), labels = FALSE))
    
    df_combined <- df_long %>%
      filter(Forecast == "Actual") %>%
      select(-grp) %>%
      crossing(., unique(select(df_rest, grp))) %>%
      bind_rows(df_rest)
    
    ggplot(df_combined, aes(Date, value, color = Forecast)) +
      geom_line(size = 1) +
      facet_grid(grp ~ .)
    

    expanded data, ggplot2 faceted

  3. Faceting, but with a more maintainable set of facets. I'll use a simple data.frame to control which lines are included in which $grp. This makes it much easier (imo) to "cherry pick" specific lines for specific facets.

    grps <- tibble::tribble(
      ~grp, ~Forecast
      ,1, "Actual"
      ,1, "1"
      ,1, "3"
      ,1, "5"
      ,2, "Actual"
      ,2, "2"
      ,2, "4"
      ,2, "6"
      ,2, "7"
      ,2, "8"
    )
    ggplot(left_join(df_long, grps, by = "Forecast"),
           aes(Date, value, color = Forecast)) +
      geom_line(size = 1) +
      facet_grid(grp ~ .)
    

    В этом случае я использовал tribble исключительно для того, чтобы было легче увидеть, что идет вместе; любой data.frame будет работать. Я также демонстрирую, что $grp размеры не обязательно должны быть равными, включая все, что вы хотите.

  4. Используйте рамку из # 3 выше для соединения, а затем просто отфильтруйте их, как в

    left_join(df_long, grps, by = "Forecase") %>%
      filter(grp == 1) %>%
      ggplot(., aes(Date, value, color = Forecast)) +
      geom_line(size = 1) +
      facet_grid(grp ~ .)
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...