1

How do I display all legends in R ggplotly? As of now it only displays the legends for matching data.

Here is what I have done so far:

library(ggplot2)
library(plotly)

schoolname <- c("Xavier", "Dakota")
value <- c(34,50)
df <- data.frame(schoolname,value)

ggplot(df, aes(x=schoolname, y=value, 
               fill = cut(value, breaks =c(0,34,69,100), 
                          labels = c("Failed -     <65%", 
                                     "Pass - 65%", 
                                     "Excellent - 80%")))) +
  geom_bar(stat = "identity",position="dodge", width=0.5, color = "#333333") +
  geom_text(aes(x=schoolname, y=value + 4.1, 
                label = paste0(value), tooltip = NULL), 
            inherit.aes = F, color='black', 
            position = position_dodge2(2), size=4, vjust=1.5) + 
  xlab("School Name") +
  ylab("Result (%)") +
  theme(plot.title = element_text(size = 18, hjust=0, vjust=0)) +
  scale_y_continuous(limits = c(0,100))+
  scale_fill_manual(values = c("Failed - <65%" = "#ea9999", 
                               "Pass - 65%" = "#ffc8aa", 
                               "Excellent- 80%" = "#d4edbc")) + 
  theme(legend.position = "top") +
  guides(fill = guide_legend(title = "Result", override.aes = list(size = 4))) -> p

2 Answers 2

1

We can add a dummy row (schoolname == NA) with "Excellent" rating, then that level is shown in the legend without getting plotted. Moreover, you had a typo where your levels in cut() did not match the ones in scale_fill_...(). Also note that your breaks in cut() do not match what you are showing in your legend.

library(dplyr)
library(ggplot2)
library(plotly)

df %>% 
  add_row(value = 100) %>% ## adding the dummy row
ggplot(aes(x=schoolname, y=value, fill = cut(value, breaks =c(0,34,69,100), 
                                             labels = c("Failed - <65%", 
                                                        "Pass - 65%", 
                                                        "Excellent - 80%")))) +
  geom_bar(stat = "identity", position="dodge", width=0.5, color = "#333333") +
  geom_text(aes(x=schoolname, y=value + 4.1, 
                label = paste0(value), tooltip = NULL), 
            inherit.aes = F, color='black', 
            position = position_dodge2(2), size=4, vjust=1.5) + 
  xlab("School Name") +
  ylab("Result (%)") +
  theme(plot.title = element_text(size = 18, hjust=0, vjust=0)) +
  scale_y_continuous(limits = c(0,100)) +
  scale_x_discrete(limits = df$schoolname) +
  scale_fill_manual(values = c("Failed - <65%" = "#ea9999", 
                               "Pass - 65%" = "#ffc8aa", 
                               "Excellent - 80%" = "#d4edbc"), 
                    drop = FALSE) + 
  theme(legend.position = "top") +
  guides(fill = guide_legend(title = "Result", override.aes = list(size = 4))) -> p

ggplotly(p)

Created on 2024-07-14 with reprex v2.0.2

Initially I thought of closing this as a duplicate of Show entire legend even when a category is not displayed in the graph, but this needs a different approach since the data is not presently available for every level.

Sign up to request clarification or add additional context in comments.

2 Comments

Besides the typos the main issue is related to the conversion to a plotly object and hence is more likely a duplicate of Prevent ggplotly to drop unused factor levels from legend which also adds a dummy row. (:
@stefan I didn't find that dupe-target, and it is a bit different from here, but close enough to close this I guess. If you do so, I won't contest :)
0

If I make the labels in your cut() function exactly match the labels in scale_fill_manual() and I add drop = FALSE to scale_fill_manual(), I do see all the labels in the legend. Make sure the number of spaces in each label is exactly the same between the two sets. For example, check "Failed - <65%" in your code. There is also some spurious text in your scale_fill_manual(). It starts with "[enter image description here]".

The unpopulated level is shown with a light gray color in the legend. I suppose that is intentional on the part of the ggplot2 designers.

p <- ggplot(df, aes(x=schoolname, y=value, 
                    fill = cut(value, breaks =c(0,34,69,100), 
                               labels = c("Failed - <65%", "Pass - 65%", "Excellent - 80%"))))+
  geom_bar(stat = "identity",position="dodge", width=0.5, color = "#333333") +
  geom_text(aes(x=schoolname, y=value + 4.1, label = paste0(value), tooltip = NULL), inherit.aes = F,   color='black', position=position_dodge2(2), size=4, vjust=1.5)+ 
  xlab("School Name") +
  ylab("Result (%)") +
  theme(plot.title = element_text(size = 18, hjust=0, vjust=0)) +
  scale_y_continuous(limits = c(0,100))+
  scale_fill_manual(values = c("Failed - <65%" = "#ea9999", 
                               "Pass - 65%" = "#ffc8aa", 
                               "Excellent - 80%" = "#d4edbc"), drop = FALSE) + 
  theme(legend.position = "top")+
  guides(fill = guide_legend(title = "Result", override.aes = list(size = 4)))

print(p)

2 Comments

It's shown as grey because it's NA! Yes, that's intentional, but not what OP is seeking.
Besides drop=FALSE you also have to add show.legend=TRUE in ggplot2 >= 3.5.0 to show a legend key for unused levels. See GGplot Legend Showing Blank Fill for Missing Factor Level. However, while this works for ggplot2 the key still gets dropped when converting to a plotly object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.