2

Can the following chart be generated using ggplot2:

enter image description here

There are two variables mapped onto the axises, one variable (Region) mapped onto the colour (using grouped bars) and one variable (Product) mapped onto some other aethetics (alpha, pattern, line style)

How would that be possible? An example using R is welcome.

Update

In my original question I did not think about facets. Of course with facets you are able to display four variables. The question should be reformulated as Display more than four variables using different aesthetics in a ggplot2 bar chart ...

Claude
  • 1,724
  • 3
  • 17
  • 46
  • 1
    A combination of stack and dodge is not easy with ggplot. I recommend to use facets for cat "Product". But here you can finde one solution: https://stackoverflow.com/questions/12715635/ggplot2-bar-plot-with-both-stack-and-dodge/66117382#66117382 It would be better to provide some data. – Roman Sep 24 '21 at 09:22

2 Answers2

3

Here is an approach by abusing facets to serve as an x-axis so you can both stack and "dodge" the data. You can look into the ggpattern package, but I'm not fluent in its use.

library(ggplot2)

df <- expand.grid(
  region = c("North", "East", "South", "West"),
  product = c("Red wine", 'White wine'),
  year = 2013:2015
)
set.seed(42)
df$value <- runif(nrow(df))

ggplot(df, aes(region, value)) +
  geom_col(aes(alpha = product, fill = region), width = 1) +
  # Expand x axis to control the width of 'dodging'
  scale_x_discrete(expand = c(0.5, 0), breaks = NULL, name = NULL) +
  scale_alpha_manual(values = c(0.6, 1)) +
  facet_grid(~ year, switch = "x") +
  # 0 spacing gives impression it is a single panel
  theme(panel.spacing.x = unit(0, "pt"))

Created on 2021-09-24 by the reprex package (v2.0.1)

EDIT: An alternative without using facets, but with use of a helper function to position everything on the x-axis:

helper <- function(center, offset, width = 0.6) {
  if (!is.numeric(center)) {
    center <- match(center, sort(unique(center)))
  }
  offset <- match(offset, sort(unique(offset)))
  offset <- scales::rescale(offset, to = c(-0.5, 0.5) * width)
  center + offset
}

ggplot(df, aes(helper(year, region), value)) +
  geom_col(aes(alpha = product, fill = region), width = 0.15) +
  scale_alpha_manual(values = c(0.6, 1)) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  theme(panel.spacing.x = unit(0, "pt"))

teunbrand
  • 33,645
  • 4
  • 37
  • 63
3

Here's a base version that gets most of the way there

set.seed(1)
d <- replicate(15, rpois(2, 10))
s <- replace(rep(0.1, 15), 1:2 * 5 + 1, 1)

op <- par(mar = c(5, 4, 2, 7), las = 1)
bp <- barplot(colSums(d), space = s, col = 2:6)
barplot(d, space = s, add = TRUE, density = c(0, 10), col = 'black', border = 'black')
abline(h = 0)
axis(1L, bp[1:3 * 5 - 2], 13:15 + 2000, lwd = 0)
title(xlab = 'Year', cex.lab = 1.5)

l <- list(
  list(
    title = 'Region', fill = 2:6,
    legend = c('North', 'South', 'East', 'West', 'Center')
  ),
  list(
    title = 'Product', density = c(20, 0),
    legend = c('Red wine', 'White wine')
  )
)
lg <- legend('topright', legend = '', bty = 'n', inset = c(-0.025, 0))
for (ii in seq_along(l)) {
  lg <- do.call('legend', c(
    list(x = lg$rect$left, y = lg$rect$top - lg$rect$h,
         xpd = NA, bty = 'n', title.adj = 0), l[[ii]]
  ))
}
par(op)

enter image description here

rawr
  • 20,481
  • 4
  • 44
  • 78