Search code examples
rggplot2dot-plotposition-dodge

dodging dots with geom_dotplot() if number of dots exceeds n for an x value


This is my dataframe:

DF <- structure(list(pub_year = c(2022, 2023, 2022, 2022, 2021, 2023, 2019, 
                                  2022, 2022, 2022, 2022, 2023, 2019, 2020, 
                                  2020, 2021, 2021, 2019, 2021, 2023), 
                     mega = c(1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 
                              0, 1, 1, 1, 1, 0, 1)), 
                 row.names = c(NA, -20L), 
                 class = c("tbl_df", "tbl", "data.frame"))

Creating a dotplot, I get something like this:

This is the code:

library(tidyverse)
library(ggExtra)

yHeight <- 10
binwidth <- 0.5
dotsize <- 0.5

DF %>%
  ggplot(aes(x = reorder(pub_year, -pub_year), fill = factor(mega))) +
  geom_dotplot(method = "histodot", dotsize = dotsize,
               binwidth = binwidth, stackgroups = T,
               binpositions = "all",
               stackdir = "up", stackratio = 1) +
  scale_fill_manual(values = c("#8FBEE5", "#DEE7EC"),
                    name = "TEST",
                    labels = c("≤ 40", "> 40")) +
  scale_y_continuous(limits=c(0, 1), expand = c(0, 0), breaks = seq(0, 1, 1/yHeight), labels = seq(0, yHeight)) +
  theme_minimal() +
  theme(axis.ticks = element_blank(),
        axis.title = element_blank(),
        #axis.text.y = element_blank(),
        axis.line = element_blank(),
        legend.position = "right",
        panel.grid.minor = element_blank()) +
  coord_fixed(ratio=binwidth*dotsize*yHeight) +
  ggExtra::removeGridX()

My question:

I want to have just 5 dots per value -- for example, there are 7 dots in the value "2022" now, but I want to have them dodged such that one "column" has 5 dots and another 2, something like below graph. How can I achieve that?


Solution

  • library(tidyverse)
    library(ggExtra)
    
    #yHeight <- 10
    binwidth <- 0.5
    dotsize <- 0.5
    max_dots <- 5
    
    DF %>% 
      mutate(y = 1:n(), .by = pub_year,
             y = if_else(y > max_dots , y - max_dots, y) / 2) %>% 
      ggplot(aes(x = reorder(pub_year, -pub_year), y = y, fill = factor(mega))) +
      geom_dotplot(binaxis="y", 
                   binwidth = binwidth, stackgroups = T,
                   binpositions = "all",
                   stackdir = "centerwhole", stackratio = 1)+
      scale_fill_manual(values = c("#8FBEE5", "#DEE7EC"),
                        name = "TEST",
                        labels = c("≤ 40", "> 40"))+
      scale_y_continuous(limits=c(0, max_dots / 2 + dotsize), expand = c(0, 0),
                         breaks = seq(dotsize, max_dots/2, dotsize), 
                         labels = seq(dotsize, max_dots/2, dotsize)) +
      theme_minimal() +
      theme(axis.ticks = element_blank(),
            axis.title = element_blank(),
            axis.text.y = element_blank(),
            axis.line = element_blank(),
            legend.position = "right",
            panel.grid.minor = element_blank()) +
      coord_fixed(ratio=binwidth*dotsize*max_dots/2) +
      ggExtra::removeGridX()
    

    Created on 2023-11-16 with reprex v2.0.2