Search code examples
rplotlymaps

Order legend in a R Plotly Bubblemap following factor order


I'm working on a Bubble map where I generated two columns, one for a color id (column Color) and one for a text refering to the id (column Class). This is a classification of my individuals (Color always belongs to Class). Class is a factor following a certain order that I made with :

COME1039$Class <- as.factor(COME1039$Class, levels = c('moins de 100 000 F.CFP',
                                                       'entre 100 000 et 5 millions F.CFP',
                                                       'entre 5 millions et 1 milliard F.CFP',
                                                       'entre 1 milliard et 20 milliards F.CFP',
                                                       'plus de 20 milliards F.CFP'))

This is my code

g <- list(
  scope = 'world',
  visible = F,
  showland = TRUE,
  landcolor = toRGB("#EAECEE"),
  showcountries = T,
  countrycolor = toRGB("#D6DBDF"),
  showocean = T,
  oceancolor = toRGB("#808B96")
)

COM.g1 <- plot_geo(data = COME1039,
                   sizes = c(1, 700))
COM.g1 <- COM.g1 %>% add_markers(
  x = ~LONGITUDE,
  y = ~LATITUDE,
  name = ~Class,
  size = ~`Poids Imports`,
  color = ~Color,
  colors=c(ispfPalette[c(1,2,3,7,6)]),
  text=sprintf("<b>%s</b> <br>Poids imports: %s tonnes<br>Valeur imports: %s millions de F.CFP",
               COME1039$NomISO,
               formatC(COME1039$`Poids Imports`/1000,
                       small.interval = ",",
                       digits = 1,
                       big.mark = " ",
                       decimal.mark = ",",
                       format = "f"),
               formatC(COME1039$`Valeur Imports`/1000000,
                       small.interval = ",",
                       digits = 1,
                       big.mark = " ",
                       decimal.mark = ",",
                       format = "f")),
  hovertemplate = "%{text}<extra></extra>"
)
COM.g1 <- COM.g1%>% layout(geo=g)
COM.g1 <- COM.g1%>% layout(dragmode=F)
COM.g1 <- COM.g1 %>% layout(showlegend=T)
COM.g1 <- COM.g1 %>% layout(legend = list(title=list(text='Valeurs des importations<br>'),
                                          orientation = "h",
                                          itemsizing='constant',
                                          x=0,
                                          y=0)) %>% hide_colorbar()
COM.g1

Unfortunately my data are too big to be added here, but this is the output I get :

enter image description here

As you can see, the order of the legend is not the one of the factor levels. How to get it ? If data are mandatory to help you to give me a hint, I will try to limit their size.

Many thanks !


Solution

  • Plotly is going to alphabetize your legend and you have to 'make' it listen. The order of the traces in your plot is the order in which the items appear in your legend. So if you rearrange the traces in the object, you'll rearrange the legend.

    I don't have your data, so I used some data from rnaturalearth.

    First I created a plot, using plot_geo. Then I used plotly_build() to make sure I had the trace order in the Plotly object. I used lapply to investigate the current order of the traces. Then I created a new order, rearranged the traces, and plotted it again.

    The initial plot and build.

    library(tidyverse)
    library(plotly)
    library(rnaturalearth)
    
    canada <- ne_states(country = "Canada", returnclass = "SF")
    x = plot_geo(canada, sizes = c(1, 700)) %>% 
      add_markers(x = ~longitude, y = ~latitude,
                  name = ~name, color = ~name)
    x <- plotly_build(x)            # capture all elements of the object
    

    Now for the investigation; this is more so you can see how this all comes together.

    # what order are they in?
    y = vector()
    invisible(
      lapply(1:length(x$x$data), 
             function(i) {
               z <- x$x$data[[i]]$name
               message(i, " ", z)
             })
    )
    # 1 Alberta
    # 2 British Columbia
    # 3 Manitoba
    # 4 New Brunswick
    # 5 Newfoundland and Labrador
    # 6 Northwest Territories
    # 7 Nova Scotia
    # 8 Nunavut
    # 9 Ontario
    # 10 Prince Edward Island
    # 11 Québec
    # 12 Saskatchewan
    # 13 Yukon
    

    In your question, you show that you made the legend element a factor. That's what I've done as well with this data.

    can2 = canada %>% 
      mutate(name = ordered(name,
                            levels = c("Manitoba", "New Brunswick", 
                                       "Newfoundland and Labrador", 
                                       "Northwest Territories", 
                                       "Alberta", "British Columbia", 
                                       "Nova Scotia", "Nunavut", 
                                       "Ontario", "Prince Edward Island", 
                                       "Québec", "Saskatchewan", "Yukon")))
    

    I used the data to reorder the traces in my Plotly object. This creates a vector. It starts with the levels and their row number or order (1:13). Then I alphabetized the data by the levels (so it matches the current order in the Plotly object).

    The output of this set of function calls is a vector of numbers (i.e., 5, 6, 1, etc.). Since I have 13 names, I have 1:13. You could always make it dynamic, as well 1:length(levels(can2$name)).

    # capture order
    df1 = data.frame(who = levels(can2$name), ord = 1:13) %>% 
      arrange(who) %>% select(ord) %>% unlist()
    

    Now all that's left is to rearrange the object traces and visualize it.

    x$x$data = x$x$data[order(c(df1))] # reorder the traces
    x      # visualize
    

    Originally:

    enter image description here

    With reordered traces:

    enter image description here