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:
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.
Specify ordering via
scale_fill_*
functions, using thebreaks=
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
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()
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'))
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"))
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"))
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 ~ .)
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"))
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'))
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'))
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'))
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'))
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:
- Remove the
str_wrap
command from thegeom_col
fill
argument. - Add
scale_fill_discrete(labels = ~ stringr::str_wrap(.x, width = 40))
to the end of theggplot
code. - 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))
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"))
Related Topics
Command Lines Error in Rstudio Console
Splitting a Data.Frame by a Variable
Merge Dataframes of Different Sizes
Reshape from Long to Wide and Create Columns with Binary Value
Convert from Billion to Million and Vice Versa
Expand Spacing Between Tick Marks on X Axis
What's the Difference Between Lapply and Do.Call
Select Rows of a Matrix That Meet a Condition
Why Is Message() a Better Choice Than Print() in R for Writing a Package
Colour Points in a Plot Differently Depending on a Vector of Values
Add an Index (Numeric Id) Column to Large Data Frame
Spread With Data.Frame/Tibble With Duplicate Identifiers
How to Create Two Independent Drill Down Plot Using Highcharter
Dplyr: Lead() and Lag() Wrong When Used with Group_By()
Pretty Ticks for Log Normal Scale Using Ggplot2 (Dynamic Not Manual)
How Subset a Data Frame by a Factor and Repeat a Plot for Each Subset
Ggmap Error: Geomrasterann Was Built with an Incompatible Version of Ggproto