Fixing the order of facets in ggplot

RGgplot2SequenceFacet

R Problem Overview


Data:

df <- data.frame(
    type   = c("T", "F", "P", "T", "F", "P", "T", "F", "P", "T", "F", "P"), 
    size   = c("50%", "50%", "50%", "100%", "100%", "100%", "150%", "150%", "150%", "200%", "200%", "200%"),
    amount = c(48.4, 48.1, 46.8, 25.9, 26, 24.9, 21.1, 21.4, 20.1, 20.8, 21.5, 16.5)
)

I need to plot a bargraph of the above data using ggplot (x-axis -> type, y-axis -> amount, group by size). When I used the following code, I am not getting the variable type and as well as size in the order shown in the data. Please see the figure. I have used the following code for that.

 ggplot(df, aes(type, amount , fill=type, group=type, shape=type, facets=size)) + 
  geom_col(width=0.5, position = position_dodge(width=0.6)) + 
  facet_grid(.~size) + 
  theme_bw() + 
  scale_fill_manual(values = c("darkblue","steelblue1","steelblue4"), 
                    labels = c("T", "F", "P"))

enter image description here .

For fixing the order issue, I have used a factor method for the variable "type" using the following. Please see the figure also.

temp$new = factor(temp$type, levels=c("T","F","P"), labels=c("T","F","P")) 

enter image description here

However, now I don't know how to fix the order for the variable size. It should be 50%, 100%. 150%, and 200%.

R Solutions


Solution 1 - R

Make your size a factor in your dataframe by:

temp$size_f = factor(temp$size, levels=c('50%','100%','150%','200%'))

Then change the facet_grid(.~size) to facet_grid(.~size_f)

Then plot: enter image description here

The graphs are now in the correct order.

Solution 2 - R

There are a couple of good solutions here.

Similar to the answer from Harpal, but within the facet, so doesn't require any change to underlying data or pre-plotting manipulation:

# Change this code:
facet_grid(.~size) + 
# To this code:
facet_grid(~factor(size, levels=c('50%','100%','150%','200%')))

This is flexible, and can be implemented for any variable as you change what element is faceted, no underlying change in the data required.

Solution 3 - R

Even less manipulating: facet_grid(~fct_relevel(size,'50%','100%','150%','200%'))

Solution 4 - R

Here's a solution that keeps things within a dplyr pipe chain. You sort the data in advance, and then using mutate_at to convert to a factor. I've modified the data slightly to show how this solution can be applied generally, given data that can be sensibly sorted:

# the data
temp <- data.frame(type=rep(c("T", "F", "P"), 4),
                    size=rep(c("50%", "100%", "200%", "150%"), each=3), # cannot sort this
                    size_num = rep(c(.5, 1, 2, 1.5), each=3), # can sort this
                    amount=c(48.4, 48.1, 46.8, 
                             25.9, 26.0, 24.9,
                             20.8, 21.5, 16.5,
                             21.1, 21.4, 20.1))

temp %>% 
  arrange(size_num) %>% # sort
  mutate_at(vars(size), funs(factor(., levels=unique(.)))) %>% # convert to factor

  ggplot() + 
  geom_bar(aes(x = type, y=amount, fill=type), 
           position="dodge", stat="identity") + 
  facet_grid(~ size)

You can apply this solution to arrange the bars within facets, too, though you can only choose a single, preferred order:

    temp %>% 
  arrange(size_num) %>%
  mutate_at(vars(size), funs(factor(., levels=unique(.)))) %>%
  arrange(desc(amount)) %>%
  mutate_at(vars(type), funs(factor(., levels=unique(.)))) %>%
  ggplot() + 
  geom_bar(aes(x = type, y=amount, fill=type), 
           position="dodge", stat="identity") + 
  facet_grid(~ size)
  

  ggplot() + 
  geom_bar(aes(x = type, y=amount, fill=type), 
           position="dodge", stat="identity") + 
  facet_grid(~ size)

Solution 5 - R

Similar to answer from glenn_in_boston, but without hard coding in the levels.

# Change this code:
facet_grid(.~size) + 
# To this code:
facet_grid(~factor(size, levels=unique(df$size)))

Works because size is arranged from the smallest to the largest in the dataframe.

If size was already a factor and you just want to flip the order when plotting, this is an option:

# Updating dataframe so size is a factor ordered smallest to largest
df <- data.frame(
  type   = c("T", "F", "P", "T", "F", "P", "T", "F", "P", "T", "F", "P"), 
  size   = factor(c("50%", "50%", "50%", "100%", "100%", "100%", "150%", "150%", "150%", "200%", "200%", "200%"), levels=c("50%", "100%","150%","200%"), ordered = TRUE),
  amount = c(48.4, 48.1, 46.8, 25.9, 26, 24.9, 21.1, 21.4, 20.1, 20.8, 21.5, 16.5)
)

# Now plotting with facets in the reverse order
ggplot(df, aes(type, amount , fill=type, group=type, shape=type, facets=size)) + 
  geom_col(width=0.5, position = position_dodge(width=0.6)) + 
  facet_grid(~factor(size, levels=rev(unique(df$size)))) +  #this part updated
  theme_bw() + 
  scale_fill_manual(values = c("darkblue","steelblue1","steelblue4"), 
                    labels = c("T", "F", "P"))

Solution 6 - R

Numerical ordering of charter facet labels can be achieved without parsing the labels or modifying the original data and without manually defining the order using stringr::str_sort( , numeric = TRUE):

... +

facet_grid(. ~ factor( , stringr::str_sort(unique( ), numeric = TRUE))) +

...


Full example:

library(ggplot2)

df <- data.frame(
  type   = c("T", "F", "P", "T", "F", "P", "T", "F", "P", "T", "F", "P"), 
  size   = c("50%", "50%", "50%", "100%", "100%", "100%", "150%", "150%", "150%", "200%", "200%", "200%"),
  amount = c(48.4, 48.1, 46.8, 25.9, 26, 24.9, 21.1, 21.4, 20.1, 20.8, 21.5, 16.5)
)

ggplot(df, aes(type, amount , fill=type, group=type, shape=type, facets=size)) + 
  geom_col(width=0.5, position = position_dodge(width=0.6)) + 
  facet_grid(.~factor(size, stringr::str_sort(unique(size), numeric = TRUE))) + 
  theme_bw() + 
  scale_fill_manual(values = c("darkblue","steelblue1","steelblue4"), 
                    labels = c("T", "F", "P"))

Created on 2022-03-11 by the reprex package (v2.0.1)

Solution 7 - R

Often, like in this case, the desire to specify the order of facets roots in them representing some ordinal data. In such a case it would often be better to first properly clean the data, i.e. to parse numeric values from character columns. In this case the can easily done with df$size <- as.numeric(sub("%", "", df$size))/100. One can then use a labelled function to control the facet labels, e.g. facet_grid(.~size, labeller = function(x) lapply(x, scales::label_percent()))

library(ggplot2)

df <- data.frame(
  type   = c("T", "F", "P", "T", "F", "P", "T", "F", "P", "T", "F", "P"), 
  size   = c("50%", "50%", "50%", "100%", "100%", "100%", "150%", "150%", "150%", "200%", "200%", "200%"),
  amount = c(48.4, 48.1, 46.8, 25.9, 26, 24.9, 21.1, 21.4, 20.1, 20.8, 21.5, 16.5)
)

df$size <- as.numeric(sub("%", "", df$size))/100

ggplot(df, aes(type, amount , fill=type, group=type, shape=type, facets=size)) + 
  geom_col(width=0.5, position = position_dodge(width=0.6)) + 
  facet_grid(.~size, labeller = function(x) lapply(x, scales::label_percent())) + 
  theme_bw() + 
  scale_fill_manual(values = c("darkblue","steelblue1","steelblue4"), 
                    labels = c("T", "F", "P"))

Created on 2022-03-11 by the reprex package (v2.0.1)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsamarasaView Question on Stackoverflow
Solution 1 - RHarpalView Answer on Stackoverflow
Solution 2 - Rglenn_in_bostonView Answer on Stackoverflow
Solution 3 - RibmView Answer on Stackoverflow
Solution 4 - Rtef2128View Answer on Stackoverflow
Solution 5 - RjcmbView Answer on Stackoverflow
Solution 6 - Rjan-glxView Answer on Stackoverflow
Solution 7 - Rjan-glxView Answer on Stackoverflow