Closed RS-eco closed 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"?
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")
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()
.
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)
Technically this seems like this should be implemented as a new {ggplot2}
"position" class:
Not necessarily useful just for {ggpattern}
(i.e. people seem to want to do this with unpatterned base geom_bar()
):
Five years ago {ggplot2}
maintainers said they had no (near-term) plans on implementing this particular "position" themselves: https://github.com/tidyverse/ggplot2/issues/2267
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"))
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":
Or I can use geom_bar_pattern() with 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?