trevorld / ggpattern

ggplot geoms with pattern fills
https://trevorldavis.com/R/ggpattern/dev/
Other
359 stars 18 forks source link

[FEAT] Use geom_bar_pattern() with additional fill variable in different position #67

Closed RS-eco closed 2 years ago

RS-eco commented 2 years ago

I would like to make a bar plot, where I combine information from three variables. So far I can use geom_bar with fill to plot two variables with position = "dodge":

library(ggplot2); library(ggpattern)

ggplot(mpg, aes(x = class)) + 
  geom_bar(aes(fill = as.factor(cyl)), position="dodge")

Or I can use geom_bar_pattern() with position="stack":

ggplot(mpg, aes(x = class)) + 
  geom_bar_pattern(aes(pattern = drv), position="stack")

But how I can I combine these two into one figure, where fill is displayed with position="dodge" and pattern is displayed with position="stack"). Is this somehow possible?

trevorld commented 2 years ago

Couldn't you use geom_bar() with position "dodge" and then geom_bar_pattern() (with fill set to "transparent") with position "stack"?

RS-eco commented 2 years ago

Well, this does not really solve my problem, as the geom_bar_pattern then still does not consider the dodging by the 2nd variable:

library(ggplot2); library(ggpattern)

ggplot(mpg, aes(x = class)) + 
  geom_bar(aes(fill = as.factor(cyl)), position="dodge") + 
  geom_bar_pattern(aes(pattern = drv), fill="transparent")

trevorld commented 2 years ago

I don't think that geom_bar_pattern() can do what you wished it could do. It only has the same "position" options as geom_bar().

With a lot of manual work you could probably create your desired graph using geom_rect_pattern() to draw each desired class x cyl x drv rectangle within your desired "bars" and then use scale_x_continuous() to adjust the x axis labels.

Or maybe use something like {gggrid} and {gridpattern} to manually draw pattern grobs on top of an initial geom_bar().

RS-eco commented 2 years ago

Not super neat, but here the first work-around solution I came up with using geom_rect_pattern(), just for future reference.

library(ggplot2); library(ggpattern); library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

n_cyl <- mpg %>% group_by(class) %>% 
    summarise(n_cyl = n_distinct(cyl)) %>% ungroup()
n_count <- mpg %>% group_by(class, cyl, drv) %>% 
    summarise(count=n()) %>%
    mutate(count_start = cumsum(count)-count,
           count_end = cumsum(count)) %>% ungroup()
#> `summarise()` has grouped output by 'class', 'cyl'. You can override using the `.groups` argument.
cum_cyl <- mpg %>% select(class, cyl) %>% distinct() %>%
  mutate(cyl_no = 1) %>% group_by(class) %>% 
  arrange(class, cyl) %>% summarise(cum_cyl=cumsum(cyl_no)) %>% ungroup() %>% select(-class)
#> `summarise()` has grouped output by 'class'. You can override using the `.groups` argument.
cum_cyl <- mpg %>% distinct(cyl, class) %>% arrange(class, cyl) %>% bind_cols(cum_cyl)
dat <- mpg %>% select(class, cyl, drv) %>% left_join(n_count) %>% left_join(cum_cyl) %>% 
  left_join(n_cyl) %>% distinct() %>% arrange(class, cyl, drv) %>% 
  mutate(class = as.numeric(factor(class, labels=c(1:length(unique(mpg$class)))))) %>% 
  mutate(x_start = (class-0.5)+((cum_cyl-1)/n_cyl),
         x_end = (class-0.5)+(cum_cyl/n_cyl))
#> Joining, by = c("class", "cyl", "drv")
#> Joining, by = c("class", "cyl")
#> Joining, by = "class"

ggplot(data=mpg, aes(x =class)) + 
  geom_bar(aes(fill = as.factor(cyl)), 
           position="dodge", width=1) + 
  geom_rect_pattern(data=dat,
                    aes(xmin=x_start, xmax=x_end, ymin=count_start, ymax=count_end, pattern = drv),
                    fill = 'transparent', colour  = 'black',
                    pattern_density = 0.35, pattern_spacing=0.03)

trevorld commented 2 years ago
trevorld commented 2 years ago

Also note one can achieve a similar affect via facet_grid():

library(ggplot2)
library(ggpattern)

df <- transform(mpg, cyl = as.factor(cyl))

ggplot(df, aes(x = cyl)) +
    facet_grid(. ~ class) +
    geom_bar(aes(fill = cyl)) +
    geom_bar_pattern(aes(pattern = drv),
                     fill = "transparent", col = "black",
                     pattern_spacing = 0.2, pattern_density = 0.4,
                     pattern_key_scale_factor = 0.2) +
    scale_pattern_manual(values = c("none", "stripe", "circle"))

ggp_facet