Chris Oosthuizen
Chris Oosthuizen

Reputation: 99

ggplot reverse continuous axis without flipping geom_col

I need to produce a simple column bar plot with the y-axis reversed, but without simply flipping the entire plot with scale_y_reverse. The scoring measurement is reversed in my use case, that is, smaller values rank higher. I also need to add an average score reference line to the plot. For example:

library(tidyverse)

df <- data.frame(Score = c(1,3,3,4,6,5,7),
                 Entity = c("A","B","C","D","E","F","G"))

ggplot(df,aes(Entity,Score))+
  geom_col()+
  geom_hline(yintercept = mean(df$Score)) +
  scale_y_reverse()

which produces the following plot on the left. I need to write my code such that it produces the plot on the right.

enter image description here

I have tried converting the score to a factor variable, but then I'm unable to plot the geom_hline because the axis is no longer continuous but rather discrete. Furthermore, I suppose you can hard code it with geom_segment, but I need to find a better solution because I'm implementing the plot in a shiny application which requires dynamic entity selection (that is, it's not always the same entities).

Another idea could be to mutate the Score column such that df %>% mutate(ScoreRev=7-Score), which will produce the right contents of the plot, but then I will need to manually override the y-axis labels without changing the contents. If that makes sense.

EDIT:

After reading R ggplot change labels of axis without changing the scale I found another alternative.

library(tidyverse)

df <- data.frame(Score = c(1,3,3,4,6,5,7),
                 Entity = c("A","B","C","D","E","F","G"))

df <- df %>% mutate(ScoreRev = 7-Score)

ggplot(df,aes(Entity,ScoreRev))+
  geom_col()+
  geom_hline(yintercept = mean(df$ScoreRev))+
  scale_y_continuous(labels = function(x) label_parsed(paste(7-x)))

enter image description here

Upvotes: 0

Views: 392

Answers (1)

teunbrand
teunbrand

Reputation: 38063

This approach comes with the warning that it is very much off-label use of ggplot2, and involve some of its inner workings.

Because geom_col() at some point in the plot building process gets converted to a rectangle with xmin/xmax/ymin/ymax parameters, we can insert a value for one of these using the after_scale() function. Because under the hood the reversed scale just does -1 * x, we can pick a value of -7 to anchor the bars at 7.

It isn't an approved aesthetic for geom_col(), so you'll get a warning though.

library(tidyverse)

df <- data.frame(Score = c(1,3,3,4,6,5,7),
                 Entity = c("A","B","C","D","E","F","G"))

ggplot(df,aes(Entity,Score))+
  geom_col(aes(ymax = after_scale(-7)))+
  geom_hline(yintercept = mean(df$Score)) +
  scale_y_reverse()
#> Warning: Ignoring unknown aesthetics: ymax

Created on 2022-02-02 by the reprex package (v2.0.0)

Upvotes: 3

Related Questions