1

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!

3
  • Can you provide some data using dput()? Commented Apr 23, 2024 at 9:25
  • This might be helpful - essentially you have to pass x a transformed numeric variable (e.g. as.numeric(as.factor(species_name))) and the breaks and labels arguments in your axes should change it back to labelled. If that's what your plot is already doing then it would be helpful to see your data and the start of the code to be able to reproduce your problem. Commented Apr 23, 2024 at 9:32
  • 1
    Does this answer your question? Duplicating (and modifying) discrete axis in ggplot2 Commented Apr 23, 2024 at 9:37

2 Answers 2

0

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

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you @thothal for your help! I tried as you suggested, but unfortunately it returns me the same error "Discrete value supplied to continuous scale". Do you have any ideas about why?
As long as you are not sharing how you create your plot it will be difficult to help you. My assumption is that your aes(x= is something categorical, but difficult to guess.
I posted how I've created the original plot, but I also tried applying 'as.integer()' to the x value in the aesthetics and changing the parameters inside 'scale_x_continuous()' as you suggested, returning the same error. Maybe there is a conflict with the ordering function?
Your code is a bit messed up. I suggest that you do your data manipulation outside the plotting and then a as.integer shoudl work.
0

Starting with ggplot2 4.0.0, discrete scales can be duplicated. Therefore, the following code now works:

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
]

ggplot(birds, aes(x = reorder(species_name, order), overall.percentage)) +
  geom_point(position = position_jitter(width = 0.25, height = 0)) +
  scale_x_discrete(sec.axis = dup_axis())

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.