ggplot2: Change factor order in legend
By reordering the levels in your factor, you can change the order of the legend labels. Run:
df$var2 <- factor(df$var2, levels=c("levelB", "levelC", "levelA"))
Then rerun the ggplot code, and levelB should now be at the top of the legend and green, levelC second and red, and levelA third and black.
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)
Custom order of legend in ggplot2 so it doesn't match the order of the factor in the plot
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
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 ~ .)
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'))
legend ordering and factor ordering in ggplot2
Answering your two questions:
- Add the argument
reverse = T
to your last line, changing it to:guides(fill = guide_legend(title = "Witnesses heard", reverse = T))
- Since you already suggested
forcats
, you can usefct_relevel
:temp2 <- temp2 %>% mutate(kt = fct_relevel(kt, "Berne"))
. Just specifying"Berne"
will move it to the front. You could as well specify all levels, likefct_relevel(kt, "Berne", "Basel", "Geneva", "Zurich")
, but this isn't necessary.
Full example
temp2 <- gather(temp, ' ', val, -kt) %>%
mutate(kt = fct_relevel(kt, "Berne"))
# the following is just for convenience when plotting twice
layers <- list(
geom_col(position = 'fill', color='darkgrey'),
geom_text(aes(label = val), position = position_fill(vjust = 0.5), size=3),
scale_y_continuous(labels = scales::percent_format()),
theme_bw(),
labs(x='Jurisdiction', y='Percentage'),
coord_flip(),
scale_fill_grey(start = 0.9, end = .5),
guides(fill = guide_legend(title = "Witnesses heard", reverse = T))
)
ggplot(temp2, aes(kt, val, fill = ` `)) +
layers
In addition, you could use fct_rev
to reverse the placement on the y-axis:
ggplot(temp2, aes(fct_rev(kt), val, fill = ` `)) +
layers
Change ggplot2 legend order without changing the manually specified aesthetics
You were attempting to override aesthetics for alpha in two places (ie guides()
and scale_alpha...()
), and ggplot was choosing to just interpret one of them. I suggest including your shape override with your legend order override, like this:
library(ggplot2)
ggplot(DF1, aes(x = X, y = Y, color = Fruit)) +
geom_point() +
geom_line(data = DF2, aes(x = X, y = Y, linetype = Cat), inherit.aes = F) +
labs(color = NULL, linetype = NULL) +
geom_point(data = Empty, aes(x = X, y = Y, alpha = Type), inherit.aes = F) +
geom_line(data = Empty, aes(x = X, y = Y, alpha = Type), inherit.aes = F) +
scale_alpha_manual(name = "Type of item", values = c(1, 1), breaks = c("Fruit", "Cat")) +
guides(alpha = guide_legend(order = 1,
override.aes=list(linetype = c("blank", "solid"),
shape = c(16,NA))),
linetype = guide_legend(order = 2),
color = guide_legend(order = 3)) +
theme_bw()
data:
DF1 <- data.frame(X = 1:10,
Y = c(1:10*0.5, 1:10*0.25),
Fruit = rep(c("mango", "kiwi"), each = 10))
DF2 <- data.frame(X = 1:10,
Y = c(1:10*2, 1:10*4),
Cat = rep(c("tabby", "calico"), each = 10))
Empty <- data.frame(X = mean(DF1$X),
Y = as.numeric(NA),
Type = c("Cat", "Fruit"))
Related Topics
Problems with Dplyr and Posixlt Data
Resetting Cumsum If Value Goes to Negative in R
Renaming Multiple Columns with Dplyr Rename(Across(
Compute All Pairwise Differences Within a Vector in R
How to Rename Element's List Indexed by a Loop in R
Reduce Space Between Grid.Arrange Plots
Error While Using Install_Github | Devtools | Timeout Issue
Replace Nas in One Variable with Values from Another Variable
Dual Y Axis (Second Axis) Use in Ggplot2
Warning: Replacing Previous Import 'Head' When Loading 'Utils' in R
Applying Some Functions to Multiple Objects
How to Split Column into Two in R Using Separate
Shiny: Unwanted Space Added by Plotoutput() And/Or Renderplot()
R: Scatter Plot Matrix Using Ggplot2 with Themes That Vary by Facet Panel