How to change facet labels?

RGgplot2SymbolsFacetPlotmath

R Problem Overview


I have used the following ggplot command:

ggplot(survey, aes(x = age)) + stat_bin(aes(n = nrow(h3), y = ..count.. / n), binwidth = 10)
  + scale_y_continuous(formatter = "percent", breaks = c(0, 0.1, 0.2))
  + facet_grid(hospital ~ .)
  + theme(panel.background = theme_blank())

to produce

alt text

I'd like to change the facet labels, however, to something shorter (like Hosp 1, Hosp 2...) because they are too long now and look cramped (increasing the height of the graph is not an option, it would take too much space in the document). I looked at the facet_grid help page but cannot figure out how.

R Solutions


Solution 1 - R

Here is a solution that avoids editing your data:

Say your plot is facetted by the group part of your dataframe, which has levels control, test1, test2, then create a list named by those values:

hospital_names <- list(
  'Hospital#1'="Some Hospital",
  'Hospital#2'="Another Hospital",
  'Hospital#3'="Hospital Number 3",
  'Hospital#4'="The Other Hospital"
)

Then create a 'labeller' function, and push it into your facet_grid call:

hospital_labeller <- function(variable,value){
  return(hospital_names[value])
}

ggplot(survey,aes(x=age)) + stat_bin(aes(n=nrow(h3),y=..count../n), binwidth=10)
 + facet_grid(hospital ~ ., labeller=hospital_labeller)
 ...

This uses the levels of the data frame to index the hospital_names list, returning the list values (the correct names).


Please note that this only works if you only have one faceting variable. If you have two facets, then your labeller function needs to return a different name vector for each facet. You can do this with something like :

plot_labeller <- function(variable,value){
  if (variable=='facet1') {
    return(facet1_names[value])
  } else {
    return(facet2_names[value])
  }
}

Where facet1_names and facet2_names are pre-defined lists of names indexed by the facet index names ('Hostpital#1', etc.).


Edit: The above method fails if you pass a variable/value combination that the labeller doesn't know. You can add a fail-safe for unknown variables like this:

plot_labeller <- function(variable,value){
  if (variable=='facet1') {
    return(facet1_names[value])
  } else if (variable=='facet2') {
    return(facet2_names[value])
  } else {
    return(as.character(value))
  }
}

Answer adapted from https://stackoverflow.com/questions/2893005/how-to-change-strip-text-labels-in-ggplot-with-facet-and-margin-true


edit: WARNING: if you're using this method to facet by a character column, you may be getting incorrect labels. See this bug report. fixed in recent versions of ggplot2.

Solution 2 - R

Here's another solution that's in the spirit of the one given by @naught101, but simpler and also does not throw a warning on the latest version of ggplot2.

Basically, you first create a named character vector

hospital_names <- c(
                    `Hospital#1` = "Some Hospital",
                    `Hospital#2` = "Another Hospital",
                    `Hospital#3` = "Hospital Number 3",
                    `Hospital#4` = "The Other Hospital"
                    )

And then you use it as a labeller, just by modifying the last line of the code given by @naught101 to

... + facet_grid(hospital ~ ., labeller = as_labeller(hospital_names))

Hope this helps.

Solution 3 - R

Change the underlying factor level names with something like:

# Using the Iris data
> i <- iris
> levels(i$Species)
[1] "setosa"     "versicolor" "virginica" 
> levels(i$Species) <- c("S", "Ve", "Vi")
> ggplot(i, aes(Petal.Length)) + stat_bin() + facet_grid(Species ~ .)

Solution 4 - R

Here's how I did it with facet_grid(yfacet~xfacet) using ggplot2, version 2.2.1:

facet_grid(
    yfacet~xfacet,
    labeller = labeller(
        yfacet = c(`0` = "an y label", `1` = "another y label"),
        xfacet = c(`10` = "an x label", `20` = "another x label")
    )
)

Note that this does not contain a call to as_labeller() -- something that I struggled with for a while.

This approach is inspired by the last example on the help page Coerce to labeller function.

Solution 5 - R

The EASIEST way to change WITHOUT modifying the underlying data is:

  1. Create an object using as_labeller(). If the column names begin with a number or contain spaces or special characters, don't forget to use back tick marks:
# Necessary to put RH% into the facet labels
hum_names <- as_labeller(
     c(`50` = "RH% 50", `60` = "RH% 60",`70` = "RH% 70", 
       `80` = "RH% 80",`90` = "RH% 90", `100` = "RH% 100"))
  1. Add to the ggplot:
    ggplot(dataframe, aes(x = Temperature.C, y = fit)) + 
        geom_line() + 
        facet_wrap(~Humidity.RH., nrow = 2, labeller = hum_names)

Solution 6 - R

If you have two facets hospital and room but want to rename just one, you can use:

facet_grid( hospital ~ room, labeller = labeller(hospital = as_labeller(hospital_names)))

For renaming two facets using the vector-based approach (as in naught101's answer), you can do:

facet_grid( hospital ~ room, labeller = labeller(hospital = as_labeller(hospital_names),
                                                 room = as_labeller(room_names)))

Solution 7 - R

Adding another solution similar to @domi's with parsing mathematical symbols, superscript, subscript, parenthesis/bracket, .etc.

library(tidyverse)
theme_set(theme_bw(base_size = 18))

### create separate name vectors
# run `demo(plotmath)` for more examples of mathematical annotation in R
am_names <- c(
  `0` = "delta^{15}*N-NO[3]^-{}",
  `1` = "sqrt(x,y)"
)

# use `scriptstyle` to reduce the size of the parentheses &
# `bgroup` to make adding `)` possible 
cyl_names <- c(
  `4` = 'scriptstyle(bgroup("", a, ")"))~T~-~5*"%"',
  `6` = 'scriptstyle(bgroup("", b, ")"))~T~+~10~degree*C',
  `8` = 'scriptstyle(bgroup("", c, ")"))~T~+~30*"%"'
)

ggplot(mtcars, aes(wt, mpg)) + 
  geom_jitter() +
  facet_grid(am ~ cyl,
             labeller = labeller(am  = as_labeller(am_names,  label_parsed),
                                 cyl = as_labeller(cyl_names, label_parsed))
             ) +
  geom_text(x = 4, y = 25, size = 4, nudge_y = 1,
            parse = TRUE, check_overlap = TRUE,
            label = as.character(expression(paste("Log"["10"], bgroup("(", frac("x", "y"), ")")))))

### OR create new variables then assign labels directly
# reverse facet orders just for fun
mtcars <- mtcars %>% 
  mutate(am2  = factor(am,  labels = am_names),
         cyl2 = factor(cyl, labels = rev(cyl_names), levels = rev(attr(cyl_names, "names")))
  )

ggplot(mtcars, aes(wt, mpg)) + 
  geom_jitter() +
  facet_grid(am2 ~ cyl2,
             labeller = label_parsed) +
  annotate("text", x = 4, y = 30, size = 5,
           parse = TRUE, 
           label = as.character(expression(paste("speed [", m * s^{-1}, "]"))))

Created on 2019-03-30 by the reprex package (v0.2.1.9000)

Solution 8 - R

Simple solution (from here):

p <- ggplot(mtcars, aes(disp, drat)) + geom_point()
# Example (old labels)
p + facet_wrap(~am)


to_string <- as_labeller(c(`0` = "Zero", `1` = "One"))
# Example (New labels)
p + facet_wrap(~am, labeller = to_string)

Solution 9 - R

This solution is very close to what @domi has, but is designed to shorten the name by fetching first 4 letters and last number.

library(ggplot2)

# simulate some data
xy <- data.frame(hospital = rep(paste("Hospital #", 1:3, sep = ""), each = 30),
                 value = rnorm(90))

shortener <- function(string) {
  abb <- substr(string, start = 1, stop = 4) # fetch only first 4 strings
  num <- gsub("^.*(\\d{1})$", "\\1", string) # using regular expression, fetch last number
  out <- paste(abb, num) # put everything together
  out
}

ggplot(xy, aes(x = value)) +
  theme_bw() +
  geom_histogram() +
  facet_grid(hospital ~ ., labeller = labeller(hospital = shortener))

enter image description here

Solution 10 - R

Note that this solution will not work nicely in case ggplot will show less factors than your variable actually contains (which could happen if you had been for example subsetting):

 library(ggplot2)
 labeli <- function(variable, value){
  names_li <- list("versicolor"="versi", "virginica"="virg")
  return(names_li[value])
 }

 dat <- subset(iris,Species!="setosa")
 ggplot(dat, aes(Petal.Length)) + stat_bin() + facet_grid(Species ~ ., labeller=labeli)

A simple solution (besides adding all unused factors in names_li, which can be tedious) is to drop the unused factors with droplevels(), either in the original dataset, or in the labbeler function, see:

labeli2 <- function(variable, value){
  value <- droplevels(value)
  names_li <- list("versicolor"="versi", "virginica"="virg")
  return(names_li[value])
}

dat <- subset(iris,Species!="setosa")
ggplot(dat, aes(Petal.Length)) + stat_bin() + facet_grid(Species ~ ., labeller=labeli2)

Solution 11 - R

Both facet_wrap and facet_grid also accept input from ifelse as an argument. So if the variable used for faceting is logical, the solution is very simple:

facet_wrap(~ifelse(variable, "Label if true", "Label if false"))

If the variable has more categories, the ifelse statement needs to be nested.

As a side effect, this also allows the creation of the groups to be faceted within the ggplot call.

Solution 12 - R

This is working for me.

Define a factor:

hospitals.factor<- factor( c("H0","H1","H2") )

and use, in ggplot():

facet_grid( hospitals.factor[hospital] ~ . )

Solution 13 - R

The labeller function defintion with variable, value as arguments would not work for me. Also if you want to use expression you need to use lapply and can not simply use arr[val], as the argument to the function is a data.frame.

This code did work:

libary(latex2exp)
library(ggplot2)
arr <- list('virginica'=TeX("x_1"), "versicolor"=TeX("x_2"), "setosa"=TeX("x_3"))
mylabel <- function(val) { return(lapply(val, function(x) arr[x])) }
ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width)) + geom_line() + facet_wrap(~Species, labeller=mylabel)

Solution 14 - R

Since I'm not yet allowed to comment on posts, I'm posting this separately as an addendum to Vince's answer and son520804's answer . Credit goes to them.

> Son520804:

> using Iris data:

> I assume:
>You have installed the dplyr package, which has the convenient mutate command, and >your dataset is named survey. >survey %>% mutate(Hosp1 = Hospital1, Hosp2 = Hospital2,........) >This command helps you to rename columns, yet all other columns are kept. >Then do the same facet_wrap, you are fine now.

Using the iris example of Vince and the partial code of son520804, I did this with the mutate function and achieved an easy solution without touching the original dataset. The trick is to create a stand-in name vector and use mutate() inside the pipe to correct the facet names temporarily:

i <- iris

levels(i$Species)
[1] "setosa"     "versicolor" "virginica"

new_names <- c(
  rep("Bristle-pointed iris", 50), 
  rep("Poison flag iris",50), 
  rep("Virginia iris", 50))

i %>% mutate(Species=new_names) %>% 
ggplot(aes(Petal.Length))+
    stat_bin()+
    facet_grid(Species ~ .)

In this example you can see the levels of i$Species is temporarily changed to corresponding common names contained in the new_names vector. The line containing

mutate(Species=new_names) %>%

can easily be removed to reveal the original naming.

Word of caution: This may easily introduce errors in names if the new_name vector is not correctly set up. It would probably be much cleaner to use a separate function to replace the variable strings. Keep in mind that the new_name vector may need to be repeated in different ways to match the order of your original dataset. Please double - and - triple check that this is correctly achieved.

Solution 15 - R

Just extending naught101's answer -- credit goes to him

plot_labeller <- function(variable,value, facetVar1='<name-of-1st-facetting-var>', var1NamesMapping=<pass-list-of-name-mappings-here>, facetVar2='', var2NamesMapping=list() )
{
  #print (variable)
  #print (value)
  if (variable==facetVar1) 
    {
      value <- as.character(value)
      return(var1NamesMapping[value])
    } 
  else if (variable==facetVar2) 
    {
      value <- as.character(value)
      return(var2NamesMapping[value])
    } 
  else 
    {
      return(as.character(value))
    }
}

What you have to do is create a list with name-to-name mapping

clusteringDistance_names <- list(
  '100'="100",
  '200'="200",
  '300'="300",
  '400'="400",
  '600'="500"
)

and redefine plot_labeller() with new default arguments:

plot_labeller <- function(variable,value, facetVar1='clusteringDistance', var1NamesMapping=clusteringDistance_names, facetVar2='', var1NamesMapping=list() )

And then:

ggplot() + 
  facet_grid(clusteringDistance ~ . , labeller=plot_labeller) 

Alternatively you can create a dedicated function for each of the label changes you want to have.

Solution 16 - R

I have another way to achieve the same goal without changing the underlying data:

ggplot(transform(survey, survey = factor(survey,
        labels = c("Hosp 1", "Hosp 2", "Hosp 3", "Hosp 4"))), aes(x = age)) +
  stat_bin(aes(n = nrow(h3),y=..count../n), binwidth = 10) +
  scale_y_continuous(formatter = "percent", breaks = c(0, 0.1, 0.2)) +
  facet_grid(hospital ~ .) +
  opts(panel.background = theme_blank())

What I did above is changing the labels of the factor in the original data frame, and that is the only difference compared with your original code.

Solution 17 - R

I think all other solutions are really helpful to do this, but there is yet another way.

I assume:

  • you have installed the dplyr package, which has the convenient mutate command, and

  • your dataset is named survey.

    survey %>% mutate(Hosp1 = Hospital1, Hosp2 = Hospital2,........)

This command helps you to rename columns, yet all other columns are kept.

Then do the same facet_wrap, you are fine now.

Solution 18 - R

I feel like I should add my answer to this because it took me quite long to make this work:

This answer is for you if:

  • you do not want to edit your original data
  • if you need expressions (bquote) in your labels and
  • if you want the flexibility of a separate labelling name-vector

I basically put the labels in a named vector so labels would not get confused or switched. The labeller expression could probably be simpler, but this at least works (improvements are very welcome). Note the ` (back quotes) to protect the facet-factor.

n <- 10
x <- seq(0, 300, length.out = n)

# I have my data in a "long" format
my_data <- data.frame(
  Type = as.factor(c(rep('dl/l', n), rep('alpha', n))),
  T = c(x, x),
  Value = c(x*0.1, sqrt(x))
)

# the label names as a named vector
type_names <- c(
  `nonsense` = "this is just here because it looks good",
  `dl/l` = Linear~Expansion~~Delta*L/L[Ref]~"="~"[%]", # bquote expression
  `alpha` = Linear~Expansion~Coefficient~~alpha~"="~"[1/K]"
  )


ggplot() + 
  geom_point(data = my_data, mapping = aes(T, Value)) + 
  facet_wrap(. ~ Type, scales="free_y", 
             labeller = label_bquote(.(as.expression(
               eval(parse(text = paste0('type_names', '$`', Type, '`')))
               )))) +
  labs(x="Temperature [K]", y="", colour = "") +
  theme(legend.position = 'none')

enter image description here

Solution 19 - R

Have you tried changing the specific levels of your Hospital vector?

levels(survey$hospital)[levels(survey$hospital) == "Hospital #1"] <- "Hosp 1"
levels(survey$hospital)[levels(survey$hospital) == "Hospital #2"] <- "Hosp 2"
levels(survey$hospital)[levels(survey$hospital) == "Hospital #3"] <- "Hosp 3"

Solution 20 - R

A one liner from mishabalyasin :

facet_grid(.~vs, labeller = purrr::partial(label_both, sep = " #"))

See it in action

library(reprex)
library(tidyverse)

mtcars %>% 
  ggplot(aes(x="", y=gear,fill=factor(gear), group=am)) +
  geom_bar(stat="identity", width=1) +
  coord_polar("y", start=0) +
  facet_grid(.~vs, labeller = purrr::partial(label_both, sep = " #"))

Created on 2021-07-09 by the reprex package (v2.0.0)

Solution 21 - R

My approach to this issue these days is to use dplyr::case_when to produce a labeler within the facet_grid or facet_wrap function. This is an extension of the solution proposed by @lillemets

ggplot(survey, aes(x = age)) + stat_bin(aes(n = nrow(h3), y = ..count.. / n), binwidth = 10)
  + scale_y_continuous(formatter = "percent", breaks = c(0, 0.1, 0.2))
  + facet_grid(case_when(hospital == "Hospital #1" ~ "Hosp1",
                         hospital == "Hospital #2" ~ "Hosp2") ~ .)
  + theme(panel.background = theme_blank())

What's nice is that if you have a second facet label to change you just use the same approach on the other side of the ~ within facet_grid

Solution 22 - R

After struggling for a while, what I found is that we can use fct_relevel() and fct_recode() from forcats in conjunction to change the order of the facets as well fix the facet labels. I am not sure if it's supported by design, but it works! Check out the plots below:

library(tidyverse)

before <- mpg %>%
  ggplot(aes(displ, hwy)) + 
  geom_point() +
  facet_wrap(~class)
before

after <- mpg %>%
  ggplot(aes(displ, hwy)) + 
  geom_point() + 
  facet_wrap(
    vars(
      # Change factor level name
      fct_recode(class, "motorbike" = "2seater") %>% 
        # Change factor level order
        fct_relevel("compact")
    )
  )
after

Created on 2020-02-16 by the reprex package (v0.3.0)

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
QuestionwishihadabetternameView Question on Stackoverflow
Solution 1 - Rnaught101View Answer on Stackoverflow
Solution 2 - RmbironView Answer on Stackoverflow
Solution 3 - RVinceView Answer on Stackoverflow
Solution 4 - RbovenderView Answer on Stackoverflow
Solution 5 - RLeinfloydView Answer on Stackoverflow
Solution 6 - RdomiView Answer on Stackoverflow
Solution 7 - RTungView Answer on Stackoverflow
Solution 8 - RNickView Answer on Stackoverflow
Solution 9 - RRoman LuštrikView Answer on Stackoverflow
Solution 10 - RMatifouView Answer on Stackoverflow
Solution 11 - RlillemetsView Answer on Stackoverflow
Solution 12 - RHernán CView Answer on Stackoverflow
Solution 13 - RreoxView Answer on Stackoverflow
Solution 14 - RAlexander KiellandView Answer on Stackoverflow
Solution 15 - Ruser4786271View Answer on Stackoverflow
Solution 16 - RytuView Answer on Stackoverflow
Solution 17 - Rson520804View Answer on Stackoverflow
Solution 18 - RdaniView Answer on Stackoverflow
Solution 19 - RphiliporlandoView Answer on Stackoverflow
Solution 20 - RSamuel SaariView Answer on Stackoverflow
Solution 21 - RJuan Pablo CarvalloView Answer on Stackoverflow
Solution 22 - RAshirwadView Answer on Stackoverflow