rggplot2axis

Duplicating a discrete x-axis in ggplot2 using scale_x_continuous


I need to display the labels of the x-axis both above and below my ggplot, aligned with the corresponding data points. I have already created a ggplot with the necessary data and aesthetics, but I am struggling to duplicated the x-axis because it is a categorical variable.

Since with scale_x_discrete is not possible to duplicate an axis, I tried to use scale_x_continuous(), splitting the categorical variable with breaks and defining a character vector as labels. 'plot' is my ggplot with data and aesthetics. The variable 'birds$species_name' is a factor containing the names of bird species, ordered by increasing percentage values.

library(ggplot2)
library(data.table)

birds <- data.frame(species_name= c("eagle", "eagle", "robin", "vulture", "bee-eater"),
                 overall.percentage = c(12, 33, 19, 20, 15))

# reorder the species according to their lowest percentage value
x <- setDT(birds)[,.(.(sort(overall.percentage))), species_name]
birds[
  setorder(setDT(transpose(x[[2]]))[,species_name := x[[1]]])[,order := .I],
  on = "species_name", order := order]

plot<-
    ggplot(birds, aes(x=reorder(species_name, order), overall.percentage)) +
    geom_point(position = position_jitter(w = 0.25, h = 0))
    
plot + scale_x_continuous(breaks = 1:length(unique(birds$species_name)),
                     labels = as.character(unique(birds$species_name)),
                     sec.axis = dup_axis())

As suggested by a previous question online, I also tried this code.

plot + scale_x_continuous(breaks = 1:length(unique(birds$species_name)),
                          labels = as.character(unique(birds$species_name)),
                          sec_axis(~.,
                                   breaks = 1:length(unique(birds$species_name)),
                                   labels = as.character(unique(birds$species_name))))

I would have expected the code to successfully duplicate the x-axis in my plot, but always returns me the error "Discrete value supplied to continuous scale". May it be a problem connected to the ordering action?

Any suggestion would be highly appreciated, thanks in advance!


Solution

  • The idea is that you transform your discrete axis in to a numeric one, supply the labels yourself and then you cna easily duplicated the axis:

    library(ggplot2)
    
    ggplot(iris, aes(x = as.integer(Species), y = Sepal.Width, color = Species)) +
      geom_point(position = position_jitter(height = 0)) + 
      scale_x_continuous("Species",
                         breaks = seq_len(nlevels(iris$Species)),
                         labels = levels(iris$Species),
                         sec.axis = dup_axis()
                         ) +
      theme(panel.grid.minor = element_blank())
    

    Iris Species versus Speal.Width with duplicated axes


    Based on your data you could do the following:

    library(data.table)
    
    ## first modify the data such that species_name is 
    ## a. a factor
    ## b. has the factor levels not in alphabetical order but sorted
    ##    according to overall.percentage
    setDT(birds)
    birds[, species_name := reorder(factor(species_name), 
                                    overall.percentage, min)]
    
    ## then do your plot but use the integer positions and add labels manually
    ggplot(birds, aes(x = as.integer(species_name), y = overall.percentage)) +
      geom_point(position = position_jitter(w = 0.25, h = 0)) + 
      scale_x_continuous(breaks = birds[, seq_len(nlevels(species_name))],
                         labels = birds[, levels(species_name)],
                         sec.axis = dup_axis()) +
      theme(panel.grid.minor = element_blank())
    

    Scatterplot based on the provided data with 2 axes