user1701545
user1701545

Reputation: 6200

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?

Upvotes: 2

Views: 351

Answers (1)

Allan Cameron
Allan Cameron

Reputation: 173813

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

Upvotes: 4

Related Questions