How to Reorder a Legend in Ggplot2

How to reorder a legend in ggplot2?

I find the easiest way is to just reorder your data before plotting. By specifying the reorder(() inside aes(), you essentially making a ordered copy of it for the plotting parts, but it's tricky internally for ggplot to pass that around, e.g. to the legend making functions.

This should work just fine:

df$Label  <- with(df, reorder(Label, Percent))
ggplot(data=df, aes(x=Label, y=Percent, fill=Label)) + geom_bar()

I do assume your Percent column is numeric, not factor or character. This isn't clear from your question. In the future, if you post dput(df) the classes will be unambiguous, as well as allowing people to copy/paste your data into R.

ggplot2: Reorder items in a legend

You can change the order of the items in the legend in two principle ways:

  1. Refactor the column in your dataset and specify the levels. This should be the way you specified in the question, so long as you place it in the code correctly.

  2. Specify ordering via scale_fill_* functions, using the breaks= argument.

Here's how you can do this using a subset of the mpg built-in datset as an example. First, here's the standard plot:

library(ggplot2)
library(dplyr)

p <- mpg %>%
dplyr::filter(class %in% c('compact', 'pickup', 'minivan', 'suv')) %>%
ggplot(aes(x=factor(year), fill=class)) +
geom_bar() + coord_flip()
p

Sample Image

Change order via refactoring

The key here is to ensure you use factor(...) before your plot code. Results are going to be mixed if you're trying to pipe them together (i.e. %>%) or refactor right inside the plot code.

Note as well that our colors change compared to the original plot. This is due to ggplot assigning the color values to each legend key based on their position in levels(...). In other words, the first level in the factor gets the first color in the scale, second level gets the second color, etc...

d <- mpg %>% dplyr::filter(class %in% c('compact', 'pickup', 'minivan', 'suv'))

d$class <- factor(d$class, levels=c('compact', 'suv', 'pickup', 'minivan'))

p <-
d %>% ggplot(aes(x=factor(year), fill=class)) +
geom_bar() +
coord_flip()

Sample Image

Changing order of keys using scale function

The simplest solution is to probably use one of the scale_*_* functions to set the order of the keys in the legend. This will only change the order of the keys in the final plot vs. the original. The placement of the layers, ordering, and coloring of the geoms on in the panel of the plot area will remain the same. I believe this is what you're looking to do.

You want to access the breaks= argument of the scale_fill_discrete() function - not the limits= argument.

p + scale_fill_discrete(breaks=c('compact', 'suv', 'pickup', 'minivan'))

Sample Image

How to rearrange legend order in ggplot

The first thing you want to do here is put your data into Tidy Data format. Notice how you are specifying a geom_area() function for every fill color in your dataset and naming them manually? The philosophy behind grammar of graphics upon which ggplot2 was built is that you can use the mapping function aes() to let ggplot2 define the separation and how fill= is applied by the observations in your dataset. We'll do that here, then demonstrate a few other things that will get your plot working... as well as finally making your legend in the proper order.

Tidying the Data

To tidy the data, you need to gather the columns except for time into 2 new columns: one to specify the range (i.e. "0-63" or "100-200") which will be mapped to the fill aesthetic, and one to specify the value that will be mapped to the y aesthetic. As the data stands in df_original, you have the values spread across multiple columns (they should be in one column) and the names of those columns should really be gathered into a separate column. The result of doing this is called "gathering" or making your data "long".

Here, we'll use pivot_longer():

library(ggplot2)
library(dplyr)
library(tidyr)
library(scales)

df_original <- read.table(text="time less63 63_100 100_200 200_400
06:01 0 4 3 1
06:02 2 6 6 5
06:03 4 8 8 6
06:04 6 9 7 8
06:05 7 10 8 7
06:06 3 11 3 7
06:07 6 7 2 2
06:08 7 3 0 3", header=TRUE)

df <- df_original %>%
pivot_longer(
cols = -time, # take everything except time column
names_to = "ranges",
values_to = "val"
) %>%
mutate(ranges = factor(
ranges,
levels=c("less63", "X63_100", "X100_200", "X200_400"),
labels=c("0-63", "63-100", "100-200", "200-400")))

Then you want to change the df$range column to a factor. Here you can specify the order of the ranges using the levels= argument, and while we're at it you can specify how they look (renaming) by the labels= argument:

df <- df %>%
mutate(ranges = factor(
ranges,
levels=c("less63", "X63_100", "X100_200", "X200_400"),
labels=c("0-63", "63-100", "100-200", "200-400")))

The last step is converting your df$time column to a POSIXct object, which means we can reference it as a time object. You can look at the help for strptime() to see what the string for format= is referencing:

# OP states time is in hours:minutes, so reflected here
df$time <- as.POSIXct(df$time, format="%H:%M")

Plotting

Plotting is pretty simple now. Since we tidied up the data, we can just use one geom_area() call. Also, we reordered the factor levels for range, so the order of objects in the legend is set the way it should be and looks the way it should be thanks to the labels= argument.

One more thing: you need to reference scale_fill_manual() not scale_colour_manual(), since the aesthetic that is mapped in geom_area() is the fill:

ggplot(df, aes(x=time, y=val, fill=ranges)) +
geom_area(alpha=0.5) +
scale_x_time(labels = scales::time_format(format = "%H:%M")) +
scale_fill_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue"))

Sample Image

Update: Overlapping Plot

The OP indicated they were creating an overlapping plot. By default, the position of the layers mapped to fill in geom_are() is set to "stacked", which means the y values are stacked on top of one another. This makes it easy to view and see the way the areas change over the x axis. However, OP wanted to prepare a plot where the y values are... the value. This would be position="identity" and creates overlapping areas. You can see the direct effect here:

ggplot(df, aes(x=time, y=val, fill=ranges)) +
geom_area(alpha=0.2, position="identity") +
scale_x_time(labels = scales::time_format(format = "%M:%S")) +
scale_fill_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue"))

Sample Image

You can see that even cutting down the alpha= value, this is going to be hard to discern what's going on. If you want to see this information more clearly, I would recommend using horizontally-placed facets as a better viewing option:

ggplot(df, aes(x=time, y=val, fill=ranges)) +
geom_area(alpha=0.2, position="identity") +
scale_x_time(labels = scales::time_format(format = "%M:%S")) +
scale_fill_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue")) +
facet_grid(ranges ~ .)

Sample Image

Reorder legend on ggplot2

You need to make "sex" a factor, and organise it in the order you want.

Add this line before plotting:

mydata$sex <- factor(mydata$sex, levels = c("M", "F"))

Sample Image

How to reorder the legend in ggplot and pltly?

Your code seems to be missing some variables, so I could not get the same plot to show you, but your question seems to be best answered using an illustrative sample data frame. TL;DR - use breaks= to assign order of keys in a legend.

The answer to your question lies in understanding how to change aspects of the legend using scale_*_manual():

  • labels= use this to change the appearance (words) of each legend key.

  • values= necessary when you start setting any other arguments. If you supply a named vector or list, you can explicitly assign a color to each level of the underlying factor associated with the data. If you supply a list of colors, they will be assigned according to the order of the labels in the legend. Note, it's not assigned according to the levels of the factor.

  • breaks= use this argument to indicate the order in which legend keys appear.

Here's the example:

library(dplyr)
library(tidyr)
library(ggplot2)
df <- data.frame(x=1:100, Low=rnorm(100,5,1.2),Med=rnorm(100,10,2),High=rnorm(100,15,0.8))
df <- df %>% gather('Status','Values',-x)

p <- ggplot(df, aes(Status,Values)) + geom_boxplot(aes(fill=Status), alpha=0.5)
p + scale_fill_manual(values=c('red','blue','green'))

Sample Image

The order in which df$Status appears on the x axis is decided by the order of the levels= in factor(df$Status). It's not what you ask in your question, but it's good to remember. By default, it appears that this was decided alphabetically.

The legend entries are similarly ordered alphabetically, but this is because the order will default to the order of the levels in factor(df$Status) for a discrete value. The unnamed color vector for values= is therefore assigned based on the order of items in the legend.

Note what happens if you use labels= to try to get it back to "Low, Med, High":

p + scale_fill_manual(labels=c('Low','Med','High'), values=c('red','blue','green'))

Sample Image

Now you should see the danger in assigning labels= with a simple vector. The labels= argument simply renames each of the label of the respective levels... but the order doesn't change. If we wanted to rename the levels, a better approach would be to send labels= a named vector:

p + scale_fill_manual(
labels=c('Low'='Lowest','Med'='Medium','High'='Highest'),
values=c('red','blue','green'))

Sample Image

If you want to change the order of the items in the legend, you can do that with the breaks= argument. Here, I'll show you all arguments combined:

p + scale_fill_manual(
labels=c('Low'='Lowest','Med'='Medium','High'='Highest'),
values=c('red','blue','green'),
breaks=c('Low','Med','High'))

Sample Image

How to reorder the items in a legend?

ggplot will usually order your factor values according to the levels() of the factor. You are best of making sure that is the order you want otherwise you will be fighting with a lot of function in R, but you can manually change this by manipulating the color scale:

ggplot(d, aes(x = x, y = y)) + 
geom_point(size=7, aes(color = a)) +
scale_color_discrete(breaks=c("1","3","10"))

In R, how can I reorder the legend of a multi-group time series plot to reflect the end values?

I think what you are looking for is the scale_color_discrete() function.

Original:

iris %>% ggplot(aes(Sepal.Length, Sepal.Width, col = Species)) + geom_line() 

before

Reordered legend items

iris %>% 
ggplot(aes(Sepal.Length, Sepal.Width, col = Species)) +
geom_line() +
scale_color_discrete(
breaks = levels(fct_reorder2(iris$Species, iris$Sepal.Length, iris$Sepal.Width)) # supply your ordered vector here
)

after

You should always include a reproducible example with your question, so it easier to answer.

Ordering ggplot2 legend to agree with factor order of bars in geom_col when plotting data from tabyl

So it turns out the problem was this bit in the geom_col portion of the ggplot code: fill = str_wrap(Category,40). Somehow that fill argument didn't play well with scale_fill_discrete, which is why Jared's initial solution didn't work, but his updated answer gets us most of the way there.

So the solution steps were:

  1. Remove the str_wrap command from the geom_col fill argument.
  2. Add scale_fill_discrete(labels = ~ stringr::str_wrap(.x, width = 40)) to the end of the ggplot code.
  3. Add y = "Category" to the labs element in the ggplot (to override the yucky y axis title that would otherwise result from the reordering command).

Huge thanks to @jared_mamrot for helping me troubleshoot!

Also appropriate citation from another post that offered the solution: How to wrap legend text in ggplot?

library(tidyverse)
library(ggplot2)
library(forcats)
library(janitor)
#>
#> Attaching package: 'janitor'
#> The following objects are masked from 'package:stats':
#>
#> chisq.test, fisher.test

temp <- tribble(
~ Category,
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"CCCCC CCC CCC CC CCCCC CCC CCCCCCCCCC CCCC CCCCC CCCCCCCCC CCCCCCCCCCC CCCC CCC CCC C CCC",
"CCCCC CCC CCC CC CCCCC CCC CCCCCCCCCC CCCC CCCCC CCCCCCCCC CCCCCCCCCCC CCCC CCC CCC C CCC",
"CCCCC CCC CCC CC CCCCC CCC CCCCCCCCCC CCCC CCCCC CCCCCCCCC CCCCCCCCCCC CCCC CCC CCC C CCC",
"CCCCC CCC CCC CC CCCCC CCC CCCCCCCCCC CCCC CCCCC CCCCCCCCC CCCCCCCCCCC CCCC CCC CCC C CCC",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"EEEE",
"EEEE",
"EEEE",
"EEEE",
)

temp_n <- temp %>%
nrow()

temp_tabyl <-
temp %>%
tabyl(Category) %>%
mutate(Category = factor(Category,levels = c("DDDDD DD D DDD DDDD DDD DDDDDDD DDD DDDD DDDDDDD DDD DDD DDDD DDDDDDDDD DDDD DDDDD DDDDDDD",
"BBBB BBBB BBBBB B BBBBBB BBBBB BBBBBB BBBBB BBBBB B BBBB BBBBB BBBBBBB",
"AAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAAA AAAAAAAAAA",
"CCCCC CCC CCC CC CCCCC CCC CCCCCCCCCC CCCC CCCCC CCCCCCCCC CCCCCCCCCCC CCCC CCC CCC C CCC",
"EEEE"))) %>%
rename(Percent = percent) %>%
arrange(desc(Percent)) %>%
mutate(CI = sqrt(Percent*(1-Percent)/temp_n),
MOE = CI * 1.96,
ub = Percent + MOE,
lb = Percent - MOE)

temp_tabyl %>%
ggplot() +
geom_col(aes(y = reorder(Category,Percent),
x = Percent,
fill = Category),
colour = "black"
) +
geom_errorbar(
aes(
y = reorder(Category,Percent),
xmin = lb,
xmax = ub
),
width = 0.4,
colour = "orange",
alpha = 0.9,
size = 1.3
) +
labs(colour="Category",
y = "Category") +
geom_label(aes(y = Category,
x = Percent,
label = scales::percent(Percent)),nudge_x = .11) +
scale_x_continuous(labels = scales::percent,limits = c(0,1)) +
labs(title = "Plot Title",
caption = "Plot Caption.") +
theme_bw() +
theme(
text = element_text(family = 'Roboto'),
strip.text.x = element_text(size = 14,
face = 'bold'),
panel.grid.minor = element_blank(),
axis.title.y = element_text(size = 14),
plot.title = element_text(hjust = 0.5, size = 16),
plot.subtitle = element_text(hjust = 1),
plot.caption = element_text(hjust = 0),
axis.text.y=element_blank()
) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank()) +
theme(strip.text = element_text(colour = 'white'),
legend.spacing.y = unit(.5, 'cm')) +
guides(fill = guide_legend(as.factor('Category'),
byrow = TRUE)) +
scale_fill_discrete(labels = ~ stringr::str_wrap(.x, width = 40))

Sample Image

Created on 2022-06-20 by the reprex package (v2.0.1)

ggplot2: how to change the order of a legend

In your case, you want to set the breaks. The values and labels won't change the order. They should be given in the order of the breaks. The interaction() creates levels for you by putting a period between the two categories. You can check that with with(dat, levels(interaction(Status, Type))). Then you can five an order for this via breaks=. For example

scale_fill_manual("", 
breaks = c("Case.Age30", "Case.Age50",
"Control.Age30", "Control.Age50"),
values = c("#756bb1", "#2ca25f",
"#bcbddc", "#99d8c9"),
labels = c("Age 30 Case", "Age 50 Case",
"Age 30 Control", "Age 50 Control"))

Sample Image



Related Topics



Leave a reply



Submit