Home > database >  R ggplot2 geom_bar change/flip order of legend items
R ggplot2 geom_bar change/flip order of legend items

Time:08-17

Based on the data and code below, how can I change of the legend items such that the legend item Males is under the Male portion of the pyramid plot and vice versa?

I tried:

# Change the order of values to reflect them in the plot
  pop_hisp_df$Type = factor(pop_hisp_df$Type, levels = rev(pop_hisp_df$Type))

Error in `levels<-`(`*tmp*`, value = as.character(levels)) : 
  factor level [2] is duplicated

Current Output:

enter image description here

Data (pop_hisp_df):

structure(list(age_group = c("<  5 years", "5 - 14", "15  -  24", 
"25  -  34", "35  -  44", "45  -  54", "55  -  64", "65  -  74", 
"75  -  84", "85  ", "<  5 years", "5 - 14", "15  -  24", "25  -  34", 
"35  -  44", "45  -  54", "55  -  64", "65  -  74", "75  -  84", 
"85  "), Type = c("Males", "Males", "Males", "Males", "Males", 
"Males", "Males", "Males", "Males", "Males", "Females", "Females", 
"Females", "Females", "Females", "Females", "Females", "Females", 
"Females", "Females"), Value = c(-6, -13, -13, -15, -17, -15, 
-11, -6, -3, -1, 6, 12, 12, 14, 16, 15, 12, 7, 4, 2)), row.names = c(NA, 
-20L), class = c("tbl_df", "tbl", "data.frame"))

Code:

library(tidyverse)
library(plotly)

# Plot
    gg_pop_hisp = ggplot(pop_hisp_df, aes( x = forcats::as_factor(age_group), y = Value, fill = Type))  
      geom_bar(data = subset(pop_hisp_df, Type == "Females"), stat = "identity")  
      geom_bar(data = subset(pop_hisp_df, Type == "Males"), stat = "identity")  
      #geom_text(aes(label = paste0(abs(Value), "%")))  
      scale_y_continuous(limits=c(-20,20),
                         breaks=c(-15,-10,0,10,15),
                         labels=paste0(c(15,10,0,10,15),"%"))            # CHANGE
      scale_fill_manual(name = "", values = c("Females"="#FC921F", "Males"="#149ECE"), labels = c("Females", "Males"))  
      ggtitle("HISPANIC POPULATION BY GENDER AND AGE GROUP")  
      labs(x = "AGE GROUPS", y = "PERCENTAGE POPULATION", fill = "Gender")  
      theme_minimal()  
      theme(legend.position="bottom")  
      coord_flip()  
    
    # Interactive
    ggplotly(gg_pop_hisp) %>% 
      layout(
        legend = list(
          orientation = 'h', x = 0.3, y = -0.3, 
          title = list(text = '')))

CodePudding user response:

Solution for ggplotly Only

In this case, simply swapping the calls to geom_bar (i.e create bars for Males at first and then for Females) creates the desired output, which is a lot simpler.


gg_pop_hisp = ggplot(pop_hisp_df, aes( x = forcats::as_factor(age_group), y = Value, fill = Type))  
  geom_bar(data = subset(pop_hisp_df, Type == "Males"), stat = "identity")  
  geom_bar(data = subset(pop_hisp_df, Type == "Females"), stat = "identity")  
  #geom_text(aes(label = paste0(abs(Value), "%")))  
  scale_y_continuous(limits=c(-20,20),
                     breaks=c(-15,-10,0,10,15),
                     labels=paste0(c(15,10,0,10,15),"%"))   
  scale_fill_manual(name = "", values = c("Females"="#FC921F", "Males"="#149ECE"), labels = c("Females", "Males"))  
  ggtitle("HISPANIC POPULATION BY GENDER AND AGE GROUP")  
  labs(x = "AGE GROUPS", y = "PERCENTAGE POPULATION", fill = "Gender")  
  theme_minimal()  
  theme(legend.position="bottom")  
  coord_flip()

gg_pop_hisp

# Interactive
ggplotly(gg_pop_hisp) %>% 
  layout(
    legend = list(
      orientation = 'h', x = 0.3, y = -0.3, 
      title = list(text = '')))

ggplotly Output

ggplotly_output

Its works because, the bar for Males are created at first, so the legend is and similarly thereafter for Females and therefore, ggplotly renders the legend in this order.


Solution for both ggplot2 and ggplotly

Note that, in the intermediate ggplot plot gg_pop_hisp the issue of misplaced legend still remains. To solve this case both for ggplot and ggplotly output,

Create factor variable labels with levels as Levels: Males Females and use that scale_fill_manual to specify the labels of the scale and also change the order of values.


labels = forcats::as_factor(c("Males", "Females"))

gg_pop_hisp = ggplot(pop_hisp_df, aes( x = forcats::as_factor(age_group), y = Value, fill = Type))  
  geom_bar(data = subset(pop_hisp_df, Type == "Males"), stat = "identity")  
  geom_bar(data = subset(pop_hisp_df, Type == "Females"), stat = "identity")  
  #geom_text(aes(label = paste0(abs(Value), "%")))  
  scale_y_continuous(limits=c(-20,20),
                     breaks=c(-15,-10,0,10,15),
                     labels=paste0(c(15,10,0,10,15),"%"))   
  scale_fill_manual(name = "", values = c("Males"="#149ECE", "Females"="#FC921F"), # awapped the order here
                    labels = labels)  
  ggtitle("HISPANIC POPULATION BY GENDER AND AGE GROUP")  
  labs(x = "AGE GROUPS", y = "PERCENTAGE POPULATION", fill = "Gender")  
  theme_minimal()  
  theme(legend.position="bottom")  
  coord_flip()

ggplot2 output

gg_pop_hisp

ggplot_output


and the ggplotly output in this case,

ggplotly(gg_pop_hisp) %>% 
  layout(
    legend = list(
      orientation = 'h', x = 0.3, y = -0.3, 
      title = list(text = '')))

ggplotly_output


  • Related