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 ...

1
  • 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: stackoverflow.com/questions/12715635/… It would be better to provide some data. Commented Sep 24, 2021 at 9:22

2 Answers 2

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"))

Sign up to request clarification or add additional context in comments.

2 Comments

See the update to my question: I was not aware of facets at the moment I wrote the question. Do you see a way without using facets?
Please see the edited answer.
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

2 Comments

This is a great solution, however it's not ggplot2, isn't it?
i agree, it is a great improvement over ggplot!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.