'How to combine two data frames into one graph where I will have two stacked bars side by side for each class?

I registered here specifically for this purpose.

Basically I have two data frames that have the exact same information but from two different years.

Here the head() of one of the data frames:

species dbh_cm height_m         f plot dbh_m           ba
1       1    0.7     1.34 7.1627066   16 0.007 3.848451e-05
2       3    1.9     1.95 2.0018036   16 0.019 2.835287e-04
3       3    4.0     3.05 0.9120516   16 0.040 1.256637e-03
4       1    3.5     2.27 1.0072122   16 0.035 9.621128e-04
5       3    0.6     1.52 6.9312671   16 0.006 2.827433e-05
6       3    4.2     2.70 0.9406631   16 0.042 1.385442e-03
        volume class     Sp
1 0.0003693754 (0,5] Spruce
2 0.0011067593 (0,5]  Larch
3 0.0034956596 (0,5]  Larch
4 0.0021997474 (0,5] Spruce
5 0.0002978850 (0,5]  Larch
6 0.0035187332 (0,5]  Larch

For plotting the graphs for each of these I used:

ggplot(data=trees_b, aes(x=class, fill = Sp)) + 
  geom_bar(stat = "count") +
  labs( x = "DBH classes [cm]", y = "Number of trees [n]", fill="Species") +
  scale_x_discrete(labels=c("(0,5]" = "2.5","(5,10]" = "7.5", "(10,15]" = "12.5",
                            "(15,20]" = "17.5", "(20,25]" = "22.5", "(25,30]" = "27.5",
                            "(30,35]" = "32.5", "(35,40]" = "37.5", "(40,45]" = "42.5",
                            "(45,50]" = "47.5", "(50,55]" = "52.5", "(55,60]" = "57.5",
                            "(60,65]" = "62.5", "(65,70]" = "67.5","(70,75]" = "72.5",
                            "(75,80]" = "77.5", "(80,85]" = "82.5")) +
  scale_fill_viridis(direction = -1, discrete = T) +
  theme(axis.text.x = element_text( size = 15),
        axis.text.y = element_text (size = 15),
        axis.title = element_text(size = 15),
        legend.text = element_text (size = 15),
        legend.title = element_text (size = 16, face = "bold"))

I know the code is not the cleanest but it worked out perfectly for what I needed and that is this:

enter image description here

Now I want to basically combine the two graphs into one for comparison purposes, is there a way to do that?



Solution 1:[1]

One approach to combine your graphs would be to use faceting. To this end I use dplyr::bind_rows to bind your datasets by row and which makes it easy to add an identifier column to the data which could then be used as faceting variable:

Note: I also added a simple function to compute the class means.

trees_b <- trees_a

trees <- list(a = trees_a, b = trees_b) |>
  dplyr::bind_rows(.id = "id")

library(ggplot2)
library(viridis)
#> Loading required package: viridisLite

class_mean <- function(x) {
  sapply(stringr::str_extract_all(x, "\\d+"), function(x) mean(as.numeric(x)))
}

ggplot(data = trees, aes(x = class, fill = Sp)) +
  geom_bar(stat = "count") +
  labs(x = "DBH classes [cm]", y = "Number of trees [n]", fill = "Species") +
  scale_x_discrete(labels = class_mean) +
  scale_fill_viridis(direction = -1, discrete = T) +
  theme(
    axis.text.x = element_text(size = 15),
    axis.text.y = element_text(size = 15),
    axis.title = element_text(size = 15),
    legend.text = element_text(size = 15),
    legend.title = element_text(size = 16, face = "bold")
  ) +
  facet_wrap(~id)

EDIT As you clarified in your comment you want a stacked and dodged bar chart. One approach to achieve that would be via the "facets that's don't look like facets" trick. The basic idea is to facet by the variable you mapped on x and instead map the faceting variable on x. Afterwards we use some styling via theme options to get rid of the faceting look. For more options have a look at ggplot2 - bar plot with both stack and dodge.

ggplot(data = trees, aes(x = id, fill = Sp)) +
  geom_bar(stat = "count") +
  labs(x = "DBH classes [cm]", y = "Number of trees [n]", fill = "Species") +
  scale_fill_viridis(direction = -1, discrete = T) +
  theme(
    axis.text.x = element_text(size = 15),
    axis.text.y = element_text(size = 15),
    axis.title = element_text(size = 15),
    legend.text = element_text(size = 15),
    legend.title = element_text(size = 16, face = "bold")
  ) +
  facet_wrap(~class, labeller = labeller(class = class_mean), strip.position = "bottom", nrow = 1) +
  theme(strip.placement = "outside", strip.background.x = element_blank(), panel.spacing.x = unit(0, "pt"))

enter image description here

DATA

trees_a <- structure(list(
  species = c(1L, 3L, 3L, 1L, 3L, 3L), dbh_cm = c(
    0.7,
    1.9, 4, 3.5, 0.6, 4.2
  ), height_m = c(
    1.34, 1.95, 3.05, 2.27,
    1.52, 2.7
  ), f = c(
    7.1627066, 2.0018036, 0.9120516, 1.0072122,
    6.9312671, 0.9406631
  ), plot = c(16L, 16L, 16L, 16L, 16L, 16L),
  dbh_m = c(0.007, 0.019, 0.04, 0.035, 0.006, 0.042), ba = c(
    3.848451e-05,
    0.0002835287, 0.001256637, 0.0009621128, 2.827433e-05, 0.001385442
  ), volume = c(
    0.0003693754, 0.0011067593, 0.0034956596, 0.0021997474,
    0.000297885, 0.0035187332
  ), class = c(
    "(0,5]", "(0,5]", "(0,5]",
    "(0,5]", "(0,5]", "(0,5]"
  ), Sp = c(
    "Spruce", "Larch", "Larch",
    "Spruce", "Larch", "Larch"
  )
), class = "data.frame", row.names = c(
  "1",
  "2", "3", "4", "5", "6"
))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1