Nadine M.
Nadine M.

Reputation: 107

Barplot ggplot with different background color per bar couple

Can someone help me generating a barplot with all the displayed properties and additionally with a different background color for each couple of bar?
SE are the error bars.

Mat <- matrix(c(1.97, 0.61, 0.06, 0.06, 0.61, 0.51, 0.03, 0.25, 2.25, 1.36, 0.15, 0.17, 1.19, 1.41, 0.04, 0.25),ncol=4,byrow=TRUE)

rownames(Mat) <- c("Cognitive Strategies","Motivational Strategies","SE Cognitive Strategies","SE Motivational Strategies")

colnames(Mat) <- c("No Problems","Motivational Problems","Knowledge Problems","Both Problems")

Mat <- as.data.frame(Mat)


barplot(as.matrix(Mat[1:2, 1:4]), main=NULL, ylab = "Number of \n motivational and cognitive Strateiges (CI 95%)", cex.lab = 1.5, cex.main = 1.4, beside=TRUE, col=c("darkblue","red"))

I do not know how to change the background color for every pair of bars and especially to put this in the same code with the error bars and the legend.

enter image description here

Upvotes: 1

Views: 4820

Answers (2)

Paweł Chabros
Paweł Chabros

Reputation: 2399

Nadine,
first of all ggplot likes data in long format. You can transform your data like following:

library(tidyverse)
library(wrapr)

Mat_long <-
  Mat %>%
  as_tibble() %>%
  mutate(
    Group = c('No Problems','Motivational Problems','Knowledge Problems','Both Problems'),
    xpos = row_number()
  ) %>%
  unite('Cognitive Strategies', c('Cognitive Strategies', 'SE Cognitive Strategies')) %>%
  unite('Motivational Strategies', c('Motivational Strategies', 'SE Motivational Strategies')) %>%
  gather(Type, val, `Motivational Strategies`:`Cognitive Strategies`) %>%
  separate(val, c('val', 'SE'), sep = '_') %>%
  mutate_at(4:5, as.numeric)

Mat_long looks like below:

 A tibble: 8 x 5
  Group                  xpos Type                      val    SE
  <chr>                 <int> <chr>                   <dbl> <dbl>
1 No Problems               1 Motivational Strategies  0.61  0.06
2 Motivational Problems     2 Motivational Strategies  0.51  0.25
3 Knowledge Problems        3 Motivational Strategies  1.36  0.17
4 Both Problems             4 Motivational Strategies  1.41  0.25
5 No Problems               1 Cognitive Strategies     1.97  0.06
6 Motivational Problems     2 Cognitive Strategies     0.61  0.03
7 Knowledge Problems        3 Cognitive Strategies     2.25  0.15
8 Both Problems             4 Cognitive Strategies     1.19  0.04

Now you can do a beautifull plot like this:

enter image description here

with this code:

Mat_long %.>%
  ggplot(
    data = .,
    aes(
      x = xpos,
      y = val,
      fill = Type
  )) +
  geom_rect(aes(
      xmin = xpos - .5,
      xmax = xpos + .5,
      ymin = -Inf,
      ymax = Inf,
      fill = Group
    ),
    alpha = .2
  ) +
  geom_col(
    position = position_dodge(.5),
    width = .5
  ) +
  geom_errorbar(aes(
      ymin = val - SE,
      ymax = val + SE
    ),
    position = position_dodge(.5),
    width = .2
  ) +
  geom_text(aes(
      y = val + SE + .1,
      label = val %>% str_replace('\\.', ',')
    ),
    position = position_dodge(.5)
  ) +
  scale_fill_manual(
    values = c(
      '#fb929e',  # Both Problems
      '#235784',  # Cognitive Strategies
      '#ffdfdf',  # Knowledge Problems
      '#fff6f6',  # Motivational Problems
      '#7a5d7e',  # Motivational Strategies
      '#aedefc'   # No Problems
    ),
    breaks = c(
      'Cognitive Strategies',
      'Motivational Strategies'
    )
  ) +
  scale_x_continuous(
    breaks = .$xpos %>% unique(),
    labels = .$Group %>% unique(),
    expand = c(0, 0)
  ) +
  scale_y_continuous(
    labels = function(x) str_replace(x, '\\.', ','),
    limits = c(0, 2.6),
    expand = c(0, 0)
  ) +
  ylab('Number of Motivational and Cognitive Strategies\n(CI 95%)') +
  theme_light() +
  theme(
    legend.position = 'top',
    legend.title = element_blank(),
    legend.spacing.x = unit(1, 'mm'),
    axis.title.x = element_blank()
  )

But answer to your question is... You can do different background colors for each group with geom_rect. The problem is geom_rect needs numeric values. So you have to add number to each group (xpos in my example) and later on change labels of x-axis with scale_x_continous. You can pass down Mat_long dataframe with wrapr's %.>% pipe, to use it later in scale_x_continous by calling a dot .:

  scale_x_continuous(
    breaks = .$xpos %>% unique(),
    labels = .$Group %>% unique(),
    ...
  )

Upvotes: 5

Wolfgang Arnold
Wolfgang Arnold

Reputation: 1252

No full answer, but a few hints:

For using ggplot, data must be in right shape (this might help: https://r4ds.had.co.nz/tidy-data.html). You can achieve this with gather() from tidyverse library:

library (tidyverse)

plot_df <- gather(Mat, "Strategies", "Number")
plot_df$Problems <- factor(rep(rownames(Mat), 4), levels = rownames(Mat))

I'm using a factor for the "Problems" in order to being able to provide xmin and xmax values for the geom_rect, which is used for drawing background to groups of bars:

ggplot(plot_df, aes(x = as.numeric(Problems), y = Number, fill = Strategies)) +
  geom_rect(aes(xmin = as.numeric(Problems) - 0.4, xmax = as.numeric(Problems) + 0.4, ymin = 0, ymax = max(Number),  fill = Problems), alpha = 0.2) +
  geom_bar(stat = "identity", position = "dodge2") +
  scale_x_continuous(labels = levels(plot_df$Problems), breaks = seq(1, length(unique(as.character(plot_df$Problems))))) +
  xlab("Problems")

Don't look at the colours.... For adding errorbars see e.g. https://ggplot2.tidyverse.org/reference/geom_linerange.html

Upvotes: 2

Related Questions