Home > Enterprise >  Custom order of legend in ggplot2 so it doesn't match the order of the factor in the plot
Custom order of legend in ggplot2 so it doesn't match the order of the factor in the plot

Time:10-23

I am trying to make custom likert style plot. I like the plot but I want to change the order of the legend while keeping the order of the plot.

Data:

df <- structure(list(Benefit = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 
3L, 4L, 4L, 4L, 5L, 5L, 5L, 6L, 6L, 6L, 7L, 7L, 7L, 7L, 8L, 8L, 
8L, 9L, 9L, 9L, 9L, 10L, 10L, 10L), .Label = c("Medical: Importance", 
"Medical:Satisfaction", "Dental:  Importance", "Dental: Satisfaction", 
"Vision:  Importance", "Vision: Satisfaction", "401K:    Importance", 
"401K:   Satisfaction", "EAP:     Importance", "EAP:    Satisfaction"
), class = "factor"), value = structure(c(1L, 2L, 5L, 6L, 1L, 
2L, 4L, 5L, 6L, 9L, 1L, 2L, 4L, 5L, 6L, 9L, 1L, 2L, 3L, 4L, 5L, 
6L, 9L, 1L, 2L, 3L, 4L, 5L, 6L, 9L), .Label = c("Very Important", 
"Important", "Not at all Important", "Less Important", "Strongly Satisfied", 
"Satisfied", "Strongly Dissatisfied", "Dissatisified", "N/A"), class = "factor"), 
    Percent = c(-80.7, -19.3, -50, -50, -64.3, -33.9, 1.8, -43.6, 
    -50.9, 5.5, -52.7, -41.8, 5.5, -33.9, -51.8, 14.3, -75, -17.3, 
    5.8, 1.9, -50, -30.8, 19.2, -13.7, -39.2, 5.9, 41.2, -13.2, 
    -45.3, 41.5)), class = "data.frame", row.names = c(4L, 9L, 
24L, 29L, 2L, 7L, 12L, 22L, 27L, 42L, 5L, 10L, 15L, 25L, 30L, 
45L, 1L, 6L, 16L, 11L, 21L, 26L, 41L, 3L, 8L, 18L, 13L, 23L, 
28L, 43L))

Here is the code for ggplot2 to make the plot that I like:

col4 <- c("#81A88D","#ABDDDE","#B40F20","#F4B5BD","orange","#F3DF6C","gray")

p <- ggplot(df, aes(x=Benefit, y = Percent, fill = value, label=abs(Percent)))  
  geom_bar(stat="identity", width = .5, position = position_stack(reverse = TRUE))  
  geom_col(position = 'stack')  
  scale_x_discrete(limits = rev(levels(df$Benefit)))  
  geom_text(position = position_stack(vjust = 0.5), 
            angle = 45, color="black")  
  coord_flip()  
  scale_fill_manual(values = col4)  
  scale_y_continuous(breaks=(seq(-100,100,25)), labels=abs(seq(-100,100,by=25)), limits=c(-100,100))  
                theme_minimal()  
                    theme(
                axis.title.y = element_blank(),
                legend.position = c(0.85, 0.8),
                legend.title=element_text(size=14), 
                axis.text=element_text(size=12, face="bold"),
                    legend.text=element_text(size=12),               
                panel.background = element_rect(fill = "transparent",colour = NA),
                    plot.background = element_rect(fill = "transparent",colour = NA),
                    #panel.border=element_blank(),
                    panel.grid.major=element_blank(),
                    panel.grid.minor=element_blank()
                    ) 
   labs(fill="")   ylab("")   ylab("Percent")  
   annotate("text", x = 9.5, y = 50, label = "Importance")  
   annotate("text", x = 8.00, y = 50, label = "Satisfaction") 
p

Here's the plot:

enter image description here

The main thing is the order of the legend. For example, for EAP: Importance I have the plot going from, left to right, with "Very Important" -> "Important" -> "Less Important" -> "Not at all Important". But the legend goes Very Important" -> "Important" -> "Not at all Important" -> "Less Important".

I want the legend to match the plot. I've tried fooling around with guides and the various scale functions and can't get it to work. The data is ordered like the legend, but that's the only way I could get the plot to look like I wanted. I need a custom legend!

Thanks in advance for any help!

Edit to remove reference to "med" dataframe, which was an expanded version of df. This should run now.

CodePudding user response:

Unfortunately, I could not reproduce your figure fully as it seems that I'm missing your med data.

However, changing the levels in your data frame accordingly should do the trick. Just do the following before the ggplot() command:

levels(df$value) <- c("Very Important", "Important", "Less Important", 
"Not at all Important", "Strongly Satisfied",    
"Satisfied", "Strongly Dissatisfied", "Dissatisified", "N/A")

Edit

Being able to reproduce your example, I came up with the following, a bit hacky, solution.

p <- ggplot(df, aes(x=Benefit, y = Percent, fill = value, label=abs(Percent)))  
  geom_bar(stat="identity", width = .5, position = position_stack(reverse = TRUE))  
  geom_col(position = 'stack')  
  scale_x_discrete(limits = rev(levels(df$Benefit)))  
  geom_text(position = position_stack(vjust = 0.5), 
            angle = 45, color="black")  
  coord_flip()  
  scale_fill_manual(labels = c("Very Important", "Important", "Less Important", 
                               "Not at all Important", "Strongly Satisfied",    
                               "Satisfied", "N/A"),values = col4)  
  scale_y_continuous(breaks=(seq(-100,100,25)), labels=abs(seq(-100,100,by=25)), limits=c(-100,100))  
  theme_minimal()  
  theme(
    axis.title.y = element_blank(),
    legend.position = c(0.85, 0.8),
    legend.title=element_text(size=14), 
    axis.text=element_text(size=12, face="bold"),
    legend.text=element_text(size=12),               
    panel.background = element_rect(fill = "transparent",colour = NA),
    plot.background = element_rect(fill = "transparent",colour = NA),
    #panel.border=element_blank(),
    panel.grid.major=element_blank(),
    panel.grid.minor=element_blank()
  ) 
  labs(fill="")   ylab("")   ylab("Percent")  
  annotate("text", x = 9.5, y = 50, label = "Importance")  
  annotate("text", x = 8.00, y = 50, label = "Satisfaction")   
guides(fill = guide_legend(override.aes = list(fill = c("#81A88D","#ABDDDE","#F4B5BD","#B40F20","orange","#F3DF6C","gray")) ) )
p

enter image description here

  • Related