rggplot2dplyr

Sort on ggplot2 in R with facets from worst to best values


I have a data frame in R called df:

set.seed(123)  # Setting seed for reproducibility
levels <- c('a', 'b', 'c', 'd', 'e')
my_var <- sample(levels, 50, replace = TRUE)
levels2 <- c('a', 'b', 'c', 'd', 'e','f','h')
my_var2 <- sample(levels2, 50, replace = TRUE)
delta <- rnorm(50, mean = 0, sd = 1) 

df = tibble(my_var,my_var2,delta);df
# A tibble: 50 × 3
   my_var my_var2   delta
   <chr>  <chr>     <dbl>
 1 c      h        0.304 
 2 c      a        0.448 
 3 b      h        0.0530
 4 b      b        0.922 
 5 c      d        2.05  
 6 e      h       -0.491 
 7 d      d       -2.31  
 8 a      f        1.01  
 9 b      f       -0.709 
10 c      c       -0.688 
# ℹ 40 more rows

I want to plot them with facets like the pcture that I attach but the values of delta column to be sort from worst to best. (i.e. from smaller to bigger) but starting the worst from the bottom and left of each facet and increase to best (bigger) at top right.

How can I achieve this?


df = tibble(my_var,my_var2,delta);df
df%>%
  mutate(BandRange = factor(my_var2,levels=levels2)) %>%
  ggplot(aes(x = delta, y = (my_var2), color = "black")) +
  geom_point(size = 3) +
  facet_grid(my_var ~., scales="free") +
  labs(y="my_var2") +
  geom_vline(xintercept=0) +
  theme_bw() +
  theme(legend.position = "none",                              # Remove the legend
        axis.text.x  = element_text(angle = 0  , hjust = 1),   # Rotate x-axis labels
        strip.text.y = element_text(size  = 8  , angle = 0, vjust = 0.5),
        axis.text.y  = element_text(size  = 7),
        strip.text   = element_text(size  = 14),               # Increase facet label size
        axis.title   = element_text(size  = 14),               # Increase axis title size
        axis.text    = element_text(size  = 10))+              # Increase axis text size
  theme(strip.background = element_rect(color="black", size=1.5, linetype="solid"))+
  labs(title = "",x = "")

For simplicity I changed the delta value to be sequence from 1 to 50. I just want to be plotted the delta inside each facet from lower value to the higher value regardless of the my_var2 levels.

levels <- c('a', 'b', 'c', 'd', 'e')
my_var <- sample(levels, 50, replace = TRUE)
levels2 <- c('a', 'b', 'c', 'd', 'e','f','h')
my_var2 <- sample(levels2, 50, replace = TRUE)
delta <- seq(1,50,1);delta 
df = tibble(my_var,my_var2,delta)

enter image description here


Solution

  • Okay, I think I've understood what you are trying to do, and worked out a way of doing it. It's slightly hacky, but I think it needs to be, because you're trying to order your y axis differently for each facet, but without using alphabetical order or an ordered factor (because the orders of a factor would be the same for the whole column).

    You need to create a new variable for ordering, then edit the variable labels to make it look right:

    ## Libraries -------------------------------------
    library(ggplot2)
    library(dplyr)
    library(forcats)
    library(stringr)
    
    ## init data --------------------------------------
    set.seed(123)  # Setting seed for reproducibility
    levels <- c('a', 'b', 'c', 'd', 'e')
    my_var <- sample(levels, 50, replace = TRUE)
    levels2 <- c('a', 'b', 'c', 'd', 'e','f','h')
    my_var2 <- sample(levels2, 50, replace = TRUE)
    delta <- rnorm(50, mean = 0, sd = 1) 
    
    df <- tibble(my_var,my_var2,delta)
    

    Group the data by the facetting variable, then arrange by the 'arranging' variable (i.e. delta). Then you can prefix the new labels with letters from a-z, so that alphabetical ordering will preserve your desired order:

    ## Create proxy variable for ordering --------------
    df <- 
      group_by(df, my_var) |> 
      arrange(delta, .by_group = TRUE) |> 
      mutate(labels = paste0(letters[1:n()], "/", my_var2))
    
    head(df)
    #> # A tibble: 6 × 4
    #> # Groups:   my_var [1]
    #>   my_var my_var2    delta labels
    #>   <chr>  <chr>      <dbl> <chr> 
    #> 1 a      a       -0.600   a/a   
    #> 2 a      f       -0.575   b/f   
    #> 3 a      f       -0.348   c/f   
    #> 4 a      e       -0.0556  d/e   
    #> 5 a      f        0.00576 e/f   
    #> 6 a      h        0.181   f/h
    

    To make it look right, you need to pass a function to the label argument of scale_y_discrete(). This function will extract the 'real' label from each of your new labels, so that the alphabetical ordering part is not shown:

    ## Extract label from proxy variable --------------
    
    ggplot(df, aes(x = delta, y = labels)) +
      geom_point(colour = "red") +
    
      # "free_y" lets the y axis move around between facets
      facet_wrap(~my_var, dir = "v", ncol = 1, scales = "free_y") +
    
      # Remove the ordering component of each label:
      scale_y_discrete(labels = \(x) str_extract(x, "(?<=/)."))
    

    Voila:

    Plot as requested

    Created on 2024-11-07 with reprex v2.1.1