1

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 = '')))
stefan
  • 90,330
  • 6
  • 25
  • 51
Ed_Gravy
  • 1,841
  • 2
  • 11
  • 34
  • The second answer to [Reverse the legend order when using ggplotly](https://stackoverflow.com/questions/59611914/reverse-the-legend-order-when-using-ggplotly) solves the problem. However, it would have been helpful if the answer had some explanation as to what the `reverse_legend_labels` function is doing. – Ed_Gravy Aug 16 '22 at 13:51

1 Answers1

1

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


shafee
  • 15,566
  • 3
  • 19
  • 47