Apply Geom Layer Conditionally - Separate Points & Lines

I have a data set similar to the one below where I have a lot of data for certain groups and then only single observations for other groups. I would like my single observations to show up as points but the other groups with multiple observations to show up as lines (no points). My code is below:

library(tidyverse)

dat <- tibble(x = runif(10, 0, 5),
              y = runif(10, 0, 20),
              group = c(rep("Group1", 4),
                        rep("Group2", 4),
                        "Single Point 1",
                        "Single Point 2")
              )

dat %>% 
  ggplot(aes(x = x, y = y, color = group)) + 
  geom_point() +
  geom_line()

Created on 2019-04-02 by the reprex package (v0.2.1)

If I understand what you're after this is part way there:

  ggplot() + 
  geom_line(data = filter(dat, grepl("Group",group)),
            mapping = aes(x = x, y = y, color = group)) +
    geom_point(data = filter(dat, !grepl("Group",group)),
              mapping = aes(x = x, y = y, color = group))

Doesn't touch the legend though.

1 Like

Thanks for the reply, I was really trying to avoid having to use 2 separate datasets to create this effect primarily cause of the issues it causes with the legend. Also my use case is slightly more complicated than using something like grepl() but I appreciate the response :D.

For this specific example, I think this should do the job:

library(tidyverse)

dat <- tibble(x = runif(10, 0, 5),
              y = runif(10, 0, 20),
              group = c(rep("Group1", 4),
                        rep("Group2", 4),
                        "Single Point 1",
                        "Single Point 2")
              )

dat %>%
 ggplot(aes(x = x, y = y, colour = group, shape = group, linetype = group)) +
 geom_line()+
 geom_point()+
 scale_shape_manual(values = c(NA, NA, 19, 19))+
 scale_linetype_manual(values = c(1, 1, 0, 0))
#> Warning: Removed 8 rows containing missing values (geom_point).

Created on 2019-04-03 by the reprex package (v0.2.1)

Ron.

3 Likes

If you cross-post a question on a different site (which is fine), just make sure you give a link to it here.

Here's the link to the same question on Stack Overflow:

1 Like

Below is a function using tidy evaluation that's intended to work on any data frame to plot singleton groups as points and non-singletons as lines only.

library(tidyverse)

plot_func = function(data, x, y, group.var, sort.singletons=TRUE, 
                     shape=16, linetype="solid") {
  
  # Quote arguments
  x=enquo(x)
  y=enquo(y)
  group.var = enquo(group.var)
  
  # Mark singleton groups
  data = data %>% 
    group_by(!!group.var) %>% 
    mutate(single = n()==1) %>% 
    ungroup
  
  # If sort.singletons (the default), place singleton groups after non-singletons in legend
  if(sort.singletons) {
    data = data %>% 
      arrange(single, !!group.var) %>% 
      mutate(!!group.var := factor(!!group.var, levels=unique(!!group.var)))
  }
  
  # Generate a named logical vector marking singleton groups
  #  Use this for generating the appropriate legend keys
  single.groups = data %>% 
    group_by(!!group.var) %>% 
    slice(1) %>% 
    select(!!group.var, single) %>% 
    deframe()
  
  # Generate legend key symbols based on single.groups vector we just created
  shapes = single.groups %>% ifelse(., shape, NA_integer_) 
  lines = single.groups %>% ifelse(., "blank", linetype)
  
  
  data %>% 
    ggplot(aes(x = !!x, y = !!y, color = !!group.var)) + 
    geom_line(linetype=linetype) +
    geom_point(aes(shape=single)) +
    scale_shape_manual(values=c(`FALSE`=NA, `TRUE`=shape)) +
    guides(shape=FALSE, 
           colour=guide_legend(override.aes=list(shape=shapes,
                                                 linetype=lines)))
}

Now, run the function on various data frames:

plot_func(dat, x, y, group)

Rplot20

plot_func(iris, Petal.Width, Petal.Length, Species)

plot_func(iris %>% slice(c(1:50, 51, 101:150)), 
          Petal.Width, Petal.Length, Species)

Rplot22

plot_func(iris %>% slice(c(1:50, 51, 101:150)), 
          Petal.Width, Petal.Length, Species, sort.singletons=FALSE)

Rplot21

plot_func(mtcars %>% mutate(carb=factor(carb)), mpg, hp, carb, 
          shape=17, linetype="22")

Rplot19

3 Likes

Wow this is legit! I love the reproducibility. It plays directly into my real-world case for this problem.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.