2

I have multiple graphs that I am plotting with ggplot and then sending to plotly. I set the legend order based the most recent date, so that one can easily interpret the graphs. Everything works great in generating the ggplot, but once I send it through ggplotly() the legend order reverts to the original factor level. I tried resetting the factors but this creates a new problem - the colors are different in each graph.

Here's the code:
Data:

Country  <-  c("CHN","IND","INS","PAK","USA")
  a <- data.frame("Country" = Country,"Pop" = c(1400,1300,267,233,330),Year=rep(2020,5))
  b <- data.frame("Country" = Country,"Pop" = c(1270,1000,215,152,280),Year=rep(2000,5))
  c <- data.frame("Country" = Country,"Pop" = c(1100,815,175,107,250),Year=rep(1990,5))
  Data <- bind_rows(a,b,c)

Legend Ordering Vector - This uses 2020 as the year to determine order.

Legend_Order <- Data %>% 
    filter(Year==max(Year)) %>% 
    arrange(desc(Pop)) %>% 
    select(Country) %>% 
    unlist() %>% 
    as.vector()

Then I create my plot and use Legend Order as breaks

Graph <- Data %>% 
    ggplot() + 
    geom_line(aes(x = Year, y = Pop, group = Country, color = Country), size = 1.2) + 
    scale_color_discrete(name = 'Country', breaks = Legend_Order)
Graph

But then when I pass this on to:

ggplotly(Graph)

For some reason plotly ignores the breaks argument and uses the original factor levels. If I set the factor levels beforehand, the color schemes changes (since the factors are in a different order).

How can I keep the color scheme from graph to graph, but change the legend order when using plotly?

Arik124
  • 21
  • 1

1 Answers1

0

Simply recode your Conutry var as factor with the levels set according to Legend_Order. Try this:

library(plotly)
library(dplyr)
Country  <-  c("CHN","IND","INS","PAK","USA")
a <- data.frame("Country" = Country,"Pop" = c(1400,1300,267,233,330),Year=rep(2020,5))
b <- data.frame("Country" = Country,"Pop" = c(1270,1000,215,152,280),Year=rep(2000,5))
c <- data.frame("Country" = Country,"Pop" = c(1100,815,175,107,250),Year=rep(1990,5))
Data <- bind_rows(a,b,c)

Legend_Order <- Data %>% 
  filter(Year==max(Year)) %>% 
  arrange(desc(Pop)) %>% 
  select(Country) %>% 
  unlist() %>% 
  as.vector()
Data$Country <- factor(Data$Country, levels = Legend_Order)

Graph <- Data %>% 
  ggplot() + 
  geom_line(aes(x = Year, y = Pop, group = Country, color = Country), size = 1.2)

ggplotly(Graph)

enter image description here

To "lock in" the color assignment you can make use of a named color vector like so (for short I only show the ggplots):

# Fix the color assignments using a named color vector which can be assigned via scale_color_manual
cols <- scales::hue_pal()(5) # Default ggplot2 colors
cols <- setNames(cols, Legend_Order) # Set names according to legend order

# Plot with unordered Countries but "ordered" color assignment
Data %>% 
  ggplot() + 
  geom_line(aes(x = Year, y = Pop, color = Country), size = 1.2) +
  scale_color_manual(values = cols)

# Plot with ordered factor
Data$Country <- factor(Data$Country, levels = Legend_Order)

Data %>% 
  ggplot() + 
  geom_line(aes(x = Year, y = Pop, color = Country), size = 1.2) +
  scale_color_manual(values = cols)

stefan
  • 90,330
  • 6
  • 25
  • 51
  • I tried that - the issue I run into is then if for some reason the order should change, the color assignments change. I want to lock in the color assignments from graph to graph (I have more variables) while still using the ordering trick. – Arik124 Aug 11 '20 at 22:46
  • @Arik124. I see. I just made an edit. Maybe this solves your problem – stefan Aug 11 '20 at 22:59
  • This was really helpful - I ended up doing something similar with the setNames function. I locked in the color assignemnts, and then wrote a function to handle th eplotting. This way I have multiple plots with different ordering but the color is consistent between graphs. Very useful. – Arik124 Sep 10 '20 at 19:14