M. Beausoleil
M. Beausoleil

Reputation: 3557

How to draw circles (circular dots) from linetype in ggplot2

I have a plot with a linetype that are pill shaped. But I would like to draw them as circles. Is there a way to change the linetype in ggplot2 to get 1. circles, and 2. change the spacing between these circles (with a numeric parameter that would allow for more or less space between the circles)?

library(tidyverse)

x = factor(1:100)
y = 1
# set.seed(42)
df = data.frame(x, y)
add_lines = purrr::map(.x = 0:10, ~ df |>
                         mutate(y = y+.x+ sqrt(runif(1, min = 0, max = 20)), 
                                gr = paste0('line', .x))) |> 
  bind_rows() |> 
  mutate(x_nb = as.numeric(x))

ggplot(data = add_lines, 
       mapping = aes(x = x, 
                     y = sqrt(x_nb)*sin(y+pi*(x_nb/max(x_nb)))+cos(y)+sqrt(y)*-sin(sqrt(x_nb)), 
                     group = gr,
                     color = gr)) + 
  geom_path(size = 2, linetype = '11',lineend = "round") +
  scale_color_viridis_d() + 
  theme(panel.grid.major = element_line(linetype = "blank"),
    panel.grid.minor = element_line(linetype = "blank"),
    axis.title = element_text(size = 0),
    axis.text = element_text(size = 0), axis.text.x = element_text(size = 0),
    axis.text.y = element_text(size = 0),
    plot.title = element_text(size = 0),
    panel.background = element_rect(fill = "grey90"),
    plot.background = element_rect(colour = NA),
    axis.line = element_line(colour = NA),
    axis.ticks = element_line(colour = NA),
    legend.position = "none") +labs(x = NULL, y = NULL)

Upvotes: 0

Views: 41

Answers (1)

Allan Cameron
Allan Cameron

Reputation: 174458

One way to tackle this is simply to draw dots along your paths using geom_point. For this, we can write a helper function that takes your data frame and modifies it so that it gives equally-spaced points along each path using interpolation.

make_points <- function(data, x, y, groups = 1, spacing = 1) {
  
  xvals <- data[[deparse(substitute(x))]]
  yvals <- data[[deparse(substitute(y))]]
  ratio <- diff(range(yvals)) / diff(range(xvals))
  
  data %>%
    group_by({{groups}}) %>%
    mutate(dist = c(0, cumsum(sqrt(diff({{x}})^2 + 
                    (diff({{y}}) / ratio)^2)))) |>
    reframe(x = approx(dist, {{x}}, xout = seq(0, max(dist), spacing))$y,
            y = approx(dist, {{y}}, xout = seq(0, max(dist), spacing))$y)
}

Personally I would move the trig calculations of the y values outside of ggplot. This allows you to focus on the data and plotting of the data individually. Note your theme code can be shortened a lot using theme_void

library(tidyverse)

set.seed(42)

add_lines <- purrr::map(.x = 0:10, function(.x) {
  data.frame(x = 1:100, y = 1) |>
    mutate(y = y+.x+ sqrt(runif(1, min = 0, max = 20)), 
           gr = paste0('line', .x),
           y = sqrt(x)*sin(y+pi*(x/max(x)))+cos(y)+sqrt(y)*-sin(sqrt(x)))
  }) |>
  bind_rows()

ggplot(data = make_points(add_lines, x, y, gr, spacing = 2),
       mapping = aes(x = x, y = y, group = gr, color = gr)) + 
  geom_point(size = 2) +
  scale_color_viridis_d(guide = "none") + 
  theme_void() +
  theme(panel.background = element_rect(fill = "grey90"))

enter image description here

Just adjust spacing in the call to make_points to change the spacing and size inside geom_point to control the circle size. For example, changing spacing to 1.5 gives us

enter image description here

Upvotes: 1

Related Questions