rscatterpieggpattern

Scatterpie plot with patterns


I'm using the R scatterpie package for plotting pie charts on an xy plane. Each one of the pies/groups also has a class assignment which I would also like to convey in the plot and was thinking of using patterns for that.

Here's the example data.frame:

library(ggplot2)
library(ggpattern)
library(scatterpie)
library(dplyr)
set.seed(1)
df <- data.frame(x = runif(50, 0, 100), y = runif(50, 0, 100), id = paste0("id",1:50),
                 g1 = as.integer(runif(50, 0, 100)), g2 = as.integer(runif(50, 0, 100)), g3 = as.integer(runif(50, 0, 100)),
                 class = sample(c("c1", "c2"), 50, replace = T)) %>%
  dplyr::mutate(size = 0.02*(g1 + g2 + g3))
df$class <- factor(df$class)

Here's the code I tried with the ggpattern package:

ggplot() + geom_scatterpie(data = df, aes(x = x, y = y, group = id, r = size),cols = paste0("g",1:3),color = "black", alpha = 0.8, size = 0.1) +
  scale_fill_manual(values=c("#FFC9F8", "#ee9e77", "#895A44")) +
  geom_col_pattern(aes(pattern_fill = class))+
  xlim(-10,110) + ylim(-10,110) + coord_equal() + theme_void()

Which gives the error:

Don't know how to automatically pick scale for object of type function. Defaulting to continuous.
Error in `f()`:
! Aesthetics must be valid data columns. Problematic aesthetic(s): pattern_fill = class. 
Did you mistype the name of a data column or forget to add after_stat()?

I tried playing around with the pattern part of the command but with no success.

Currently, the only way I am able to encode the class of each pie is with the linetype aesthetics argument:

ggplot() + geom_scatterpie(data = df, aes(x = x, y = y, group = id, r = size, linetype = class),cols = paste0("g",1:3), color = "black", size = 1) +
  scale_fill_manual(values=c("#FFC9F8", "#ee9e77", "#895A44")) +
  xlim(-10,110) + ylim(-10,110) + coord_equal() + theme_void()

enter image description here

But it's not ideal.

Any idea how to make this work or alternative ways to encode class?


Solution

  • I'm not convinced about the visualization, though it is possible via ggplot and ggpattern. I don't think you can use geom_scatterpie, which, as Axeman points out, doesn't really work the way other ggplot geoms do.

    If I had to do this, I would probably just make my own pie function and draw the result with geom_polygon_pattern.

    You could do that with the following helper function:

    make_pie <- function(x, y, size, groups, n, class, rownum) {
      angles <- c(0, 2*pi * cumsum(n)/sum(n))
      do.call("rbind", Map(function(a1, a2, g) {
        xvals <- c(0, sin(seq(a1, a2, len = 30)) * size, 0) + x
        yvals <- c(0, cos(seq(a1, a2, len = 30)) * size, 0) + y
        data.frame(x = xvals, y = yvals, group = g, class = class, rownum = rownum)
      }, head(angles, -1), tail(angles, -1), groups))
    }
    

    You can then plot using your data as follows:

    df %>%
      mutate(r = row_number()) %>%
      rowwise() %>%
      group_map(~ with(.x, make_pie(x, y, size, c("g1", "g2", "g3"),
                                    c(g1, g2, g3), class, r))) %>%
      bind_rows() %>%
      ggplot(aes(x, y, fill = group, group = interaction(group, rownum))) +
      geom_polygon_pattern(aes(pattern = class), pattern_fill = "black",
                           pattern_angle = 45, pattern_spacing = 0.01,
                           pattern_density = 0.1) +
      scale_fill_manual(values = c("#FFC9F8", "#ee9e77", "#895A44")) +
      guides(pattern = guide_legend(override.aes = list(fill = "white"))) +
      coord_equal() +
      theme_void()
    

    enter image description here

    You could map the alpha channel as Axeman suggests like this:

    df %>%
      mutate(r = row_number()) %>%
      rowwise() %>%
      group_map(~ with(.x, make_pie(x, y, size, c("g1", "g2", "g3"),
                                    c(g1, g2, g3), class, r))) %>%
      bind_rows() %>%
      ggplot(aes(x, y, fill = class, group = interaction(group, rownum),
                 alpha = group)) +
      geom_polygon() +
      scale_fill_manual(values = c("red4", "green4")) +
      scale_alpha_manual(values = c(0.3, 0.7, 1)) +
      coord_equal() +
      theme_void()
    

    enter image description here

    Or just declare each fill color individually:

    df %>%
      mutate(r = row_number()) %>%
      rowwise() %>%
      group_map(~ with(.x, make_pie(x, y, size, c("g1", "g2", "g3"),
                                    c(g1, g2, g3), class, r))) %>%
      bind_rows() %>%
      mutate(fill_class = paste0(group, " (class ", class, ")")) %>%
      mutate(fill_class = factor(fill_class, unique(fill_class))) %>%
      ggplot(aes(x, y, fill = fill_class, group = interaction(group, rownum))) +
      geom_polygon() +
      scale_fill_manual("Group (class)",
                        values = c("red3", "hotpink", "red", 
                                   "navy", "blue", "dodgerblue")) +
      coord_equal() +
      theme_void()
    

    enter image description here