masher
masher

Reputation: 4096

Fill ggplot2 histogram by two criteria

I have some data (x) I'd like to plot as a histogram. There are two sample types (s), and each sample was collected in two different ways (r).

I would like to plot them as a stacked histogram filled by both s and r -- the best way to do it would be to facet them as per my my last example, but I am space-limited.

I can plot the data filled by s using a single geom_histogram. I can plot two geom_histograms to plot the different r. I don't know how to make the different geoms stack though. I haven't figured out how to fill them, but ggnewscale is probably an option.

I'd like to choose fills such as (for example) s == red, blue, and r == light, dark of the respective s colour.

Any suggestions?

library(tidyverse)
library(tibble)

x <-           c(0,0,0,0,0,1,1,1,1,-1,-1,-1,-1,2,2,2,-2,-2,-2,3,3,-3,-3,4,-4, 8,8,8,8,8,9,9,9,9,9,7,7,7,10,10,10,6,6,6,11,11,5,5,12,4)
r <- as.factor(c(1,2,2,2,1,1,2,2,2, 1, 1, 2, 2,2,2,2, 1, 1, 2,2,2, 2, 2,2, 2, 1,1,2,2,2,1,1,1,2,1,1,2,2, 1, 2, 2,2,2,2, 2, 1,1,2, 1,1))
s <- c(rep.int("a", 25), rep.int("b", 25))

figsd <- tibble(x,r,s)
  
figsd %>% 
ggplot(aes(x=x)) +
  geom_histogram(aes(fill = s), binwidth = 1, position = "stack") +
  coord_cartesian(ylim = c(0,5))



figsd %>% 
  ggplot(aes(x=x)) +
  geom_histogram(data = . %>% filter(r == 1), aes(fill = s), binwidth = 1,alpha = 0.5, position = "stack") +
  geom_histogram(data = . %>% filter(r != 1), aes(fill = s), binwidth = 1,alpha = 0.5, position = "stack") +
  coord_cartesian(ylim = c(0,5))



figsd %>% 
  ggplot(aes(x=x)) +
  geom_histogram(aes(fill = r), binwidth = 1,position = "stack") +
  coord_cartesian(ylim = c(0,5)) +
  facet_grid(s ~ .)

Created on 2020-11-02 by the reprex package (v0.3.0)

Upvotes: 0

Views: 992

Answers (2)

masher
masher

Reputation: 4096

Taken from the comment of @alistaire.

Redid ordering and colouring to make it a little better.

library(tidyverse)
library(tibble)

x <-           c(0,0,0,0,0,1,1,1,1,-1,-1,-1,-1,2,2,2,-2,-2,-2,3,3,-3,-3,4,-4, 8,8,8,8,8,9,9,9,9,9,7,7,7,10,10,10,6,6,6,11,11,5,5,12,4)
r <- as.factor(c(1,2,2,2,1,1,2,2,2, 1, 1, 2, 2,2,2,2, 1, 1, 2,2,2, 2, 2,2, 2, 1,1,2,2,2,1,1,1,2,1,1,2,2, 1, 2, 2,2,2,2, 2, 1,1,2, 1,1))
s <- c(rep.int("a", 25), rep.int("b", 25))

figsd <- tibble(x,r,s)

figsd %>%  mutate(r = factor(r, levels=c(2, 1))) %>% 
ggplot(aes(x=x)) +
  geom_histogram(aes(fill = interaction(r,s)), binwidth = 1, position = "stack")+ 
  scale_fill_manual(breaks = c("1.a","2.a","1.b","2.b"),
                    values = c('1.a' = 'lightblue', '1.b' = 'pink', '2.a' = 'darkblue', '2.b' = 'darkred'),
                    labels = c("1.a","2.a","1.b","2.b")
                   ) +
  coord_cartesian(ylim = c(0,5))

Created on 2020-11-02 by the reprex package (v0.3.0)

Upvotes: 1

alistaire
alistaire

Reputation: 43334

You can create an interaction between the two groupings and pass that to the fill aesthetic:

library(ggplot2)

figsd <- data.frame(
    x = c(0,0,0,0,0,1,1,1,1,-1,-1,-1,-1,2,2,2,-2,-2,-2,3,3,-3,-3,4,-4, 8,8,8,8,8,9,9,9,9,9,7,7,7,10,10,10,6,6,6,11,11,5,5,12,4),
    r = as.factor(c(1,2,2,2,1,1,2,2,2, 1, 1, 2, 2,2,2,2, 1, 1, 2,2,2, 2, 2,2, 2, 1,1,2,2,2,1,1,1,2,1,1,2,2, 1, 2, 2,2,2,2, 2, 1,1,2, 1,1)),
    s = c(rep.int("a", 25), rep.int("b", 25))
)


ggplot(figsd, aes(x, fill = interaction(r, s))) +
    geom_histogram(binwidth = 1) + 
    scale_fill_manual(values = c(
        '1.a' = 'lightblue', 
        '1.b' = 'darkblue', 
        '2.a' = 'pink', 
        '2.b' = 'darkred'
    )) + 
    labs(fill = 'group')

histogram of interaction

Levels here don't inherently mean anything, so you'll likely want to set fill colors deliberately to show relationships.

Also note that interaction() doesn't scale well, for whatever reason—if you're dealing with 100k rows, paste() will be considerably faster even though interaction() is more semantically correct in terms of what you're trying to display.

Upvotes: 3

Related Questions