Skip to content

Instantly share code, notes, and snippets.

@andrewheiss
Created October 25, 2020 16:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewheiss/669e326befd40e40422b6aa1239a4c74 to your computer and use it in GitHub Desktop.
Save andrewheiss/669e326befd40e40422b6aa1239a4c74 to your computer and use it in GitHub Desktop.
library(tidyverse)

example_df <- tribble(
  ~Year, ~Category, ~Name,
  2015,  "A",       "1",
  2015,  "A",       "3",
  2015,  "A",       "5",
  2015,  "C",       "2",
  2015,  "C",       "4",
  2016,  "A",       "1",
  2016,  "A",       "4",
  2016,  "B",       "2",
  2016,  "B",       "3",
  2016,  "B",       "5",
  2017,  "B",       "1",
  2017,  "B",       "2",
  2017,  "B",       "3",
  2017,  "B",       "4",
  2017,  "C",       "5",
  2018,  "B",       "1",
  2018,  "B",       "2",
  2018,  "B",       "3",
  2018,  "B",       "4",
  2018,  "B",       "5"
) %>% 
  mutate(Year = factor(Year))
ggplot(example_df, aes(x = Year, y = fct_rev(Category), color = Name)) +
  geom_point(size = 3) +
  labs(title = "No jittering; overplotting") +
  theme(legend.position = "bottom")

ggplot(example_df, aes(x = Year, y = fct_rev(Category), color = Name)) +
  geom_point(size = 3, position = position_jitter()) +
  labs(title = "Jittering; positioning not consistent") +
  theme(legend.position = "bottom")

Created on 2020-10-25 by the reprex package (v0.3.0)

@gkaramanis
Copy link

I would do it this way:

library(tidyverse)

example_df <- tribble(
  ~Year, ~Category, ~Name,
  2015,  "A",       "1",
  2015,  "A",       "3",
  2015,  "A",       "5",
  2015,  "C",       "2",
  2015,  "C",       "4",
  2016,  "A",       "1",
  2016,  "A",       "4",
  2016,  "B",       "2",
  2016,  "B",       "3",
  2016,  "B",       "5",
  2017,  "B",       "1",
  2017,  "B",       "2",
  2017,  "B",       "3",
  2017,  "B",       "4",
  2017,  "C",       "5",
  2018,  "B",       "1",
  2018,  "B",       "2",
  2018,  "B",       "3",
  2018,  "B",       "4",
  2018,  "B",       "5"
) %>% 
  mutate(
    x = Year + 0.1 * (as.numeric(Name) - 1) %/% 2,
    y = 0.1 * as.numeric(Name) %% 2,
    Year = factor(Year)
    )

ggplot(example_df, aes(x = x, y = as.numeric(fct_rev(Category)) + y, color = Name)) +
  geom_point(size = 3) +
  labs(title = "No jittering; overplotting") +
  scale_y_continuous(breaks = 3:1, labels = c("A", "B", "C")) +
  theme(legend.position = "bottom")

Rplot

@TimTeaFan
Copy link

TimTeaFan commented Oct 25, 2020

I had a similar problem recently and used a customized jitter function that aligns the dots in equal spaces along a circle. It can be easily applied, if the underlying data is numeric. In your case the implementation is not straight forward but still possible:

# custom jitterize function with credit to:
# https://stackoverflow.com/a/40279144/9349302
jitterize <- function(xcol, ycol, r, .name = "_jitter", adjx = 1, adjy = 1, out = NULL, n = NULL){
  
  en_xcol <- rlang::enquo(xcol)
  en_ycol <- rlang::enquo(ycol)
  
  xcol_nm <- paste0(rlang::as_name(en_xcol), .name)
  ycol_nm <- paste0(rlang::as_name(en_ycol), .name)
  
  if (is.null(n))
    n <- dplyr::n()
  if (is.null(out))
    out <- dplyr::row_number(xcol)
  
  if (unique(n) == 1) {
    return(tibble::tibble(!! xcol_nm := xcol,
                          !! ycol_nm := ycol))
    
  } else if (unique(n) == 2) {
    tibble::tibble(!! xcol_nm := xcol,
                   !! ycol_nm := ycol + (c(-r, r) * adjy))
  } else {
    
    polypoints <- seq(0, 2 * pi, length.out = n + 1)
    polypoints <- polypoints[-length(polypoints)]
    circx <- r * sin(polypoints)
    circy <- r * cos(polypoints)
    tibble::tibble(!! xcol_nm := xcol + (circx * adjx),
                   !! ycol_nm := ycol + (circy * adjy))
    
  }
}

library(tidyverse)

example_df <- tribble(
  ~Year, ~Category, ~Name,
  2015,  "A",       "1",
  2015,  "A",       "3",
  2015,  "A",       "5",
  2015,  "C",       "2",
  2015,  "C",       "4",
  2016,  "A",       "1",
  2016,  "A",       "4",
  2016,  "B",       "2",
  2016,  "B",       "3",
  2016,  "B",       "5",
  2017,  "B",       "1",
  2017,  "B",       "2",
  2017,  "B",       "3",
  2017,  "B",       "4",
  2017,  "C",       "5",
  2018,  "B",       "1",
  2018,  "B",       "2",
  2018,  "B",       "3",
  2018,  "B",       "4",
  2018,  "B",       "5"
) %>% 
  mutate(Year = factor(Year))

example_df %>% 
  mutate(Category_int = recode_vec[example_df$Category],
         Year_int = as.integer(Year)) %>% 
  full_join(., expand(., Name, Category_int, Year_int)) %>% 
  group_by(Category_int, Year_int) %>% 
  arrange(Name) %>% 
  mutate(jitterize(Year_int, Category_int, r = 0.225)) %>% 
  filter(!is.na(Year)) %>% 
  ggplot() +
  geom_point(aes(x = Year_int,
                 y = Category_int),
             size = 35,
             color = "aliceblue") +
  geom_point(aes(x = Year_int_jitter,
                 y = Category_int_jitter,
                 color = Name),
             size = 3) +
  scale_x_continuous(expand = c(0,0),
                     limits = c(0.5, 4.5),
                     minor_breaks = seq(0.5, 4.5, 1),
                     breaks = seq(1, 4, 1),
                     labels = levels(example_df$Year)) + 
  scale_y_continuous(trans = "reverse", 
                     expand = c(0,0),
                     limits = c(3.5, 0.5),
                     minor_breaks = seq(3.5, 0.5, -1),
                     breaks = seq(3, 1, -1),
                     labels = LETTERS[1:3]) + 
  labs(title = "Jittering in circles with fixed positions",
       x = "Year",
       y = "Company") + 
  theme_bw() + 
  theme(panel.grid.major = element_blank(),
        legend.position = "bottom")

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment