Creating a Grouped Bar Plot in R

Creating a grouped barplot with two Y axes in R

How about this:

dat <- data.frame(Week = c(1, 2, 3, 4, 5, 6, 7, 8), 
SPH = c(2.676, 2.660, 4.175, 2.134, 3.742, 1.395, 4.739, 2.756),
CPH = c(75.35, 29.58, 20.51, 80.43, 97.94, 85.39, 168.61, 142.19))

library(tidyr)
library(dplyr)

## Find minimum and maximum values for each variable
tmp <- dat %>%
summarise(across(c("SPH", "CPH"), ~list(min = min(.x), max=max(.x)))) %>%
unnest(everything())

## make mapping from CPH to SPH
m <- lm(SPH ~ CPH, data=tmp)
## make mapping from SPH to CPH
m_inv <- lm(CPH ~ SPH, data=tmp)

## transform CPH so it's on the same scale as SPH.
## to do this, you need to use the coefficients from model m above
dat <- dat %>%
mutate(CPH = coef(m)[1] + coef(m)[2]*CPH) %>%
## pivot the data so all plotting values are in a single column
pivot_longer(c("SPH", "CPH"),
names_to="var", values_to="vals") %>%
mutate(var = factor(var, levels=c("SPH", "CPH"),
labels=c("Sightings", "Clicks")))

ggplot(dat, aes(x=as.factor(Week), y=vals, fill=var)) +
geom_bar(position="dodge", stat="identity") +
## use model m_inv from above to identify the transformation from the tick values of SPH
## to the appropriate tick values of CPH
scale_y_continuous(sec.axis=sec_axis(trans = ~coef(m_inv)[1] + coef(m_inv)[2]*.x, name="Clicks/hour")) +
labs(y="Sightings/hour", x="Week", fill="") +
theme_bw() +
theme(legend.position="top")

Sample Image



Update - start both axes at zero

To start both axes at zero, you need to change have the values that are used in the linear map both start at zero. Here's an updated full example that does that:

dat <- data.frame(Week = c(1, 2, 3, 4, 5, 6, 7, 8), 
SPH = c(2.676, 2.660, 4.175, 2.134, 3.742, 1.395, 4.739, 2.756),
CPH = c(75.35, 29.58, 20.51, 80.43, 97.94, 85.39, 168.61, 142.19))

library(tidyr)
library(dplyr)

## Find minimum and maximum values for each variable
tmp <- dat %>%
summarise(across(c("SPH", "CPH"), ~list(min = min(.x), max=max(.x)))) %>%
unnest(everything())

## set lower bound of each to zero
tmp$SPH[1] <- 0
tmp$CPH[1] <- 0

## make mapping from CPH to SPH
m <- lm(SPH ~ CPH, data=tmp)
## make mapping from SPH to CPH
m_inv <- lm(CPH ~ SPH, data=tmp)

## transform CPH so it's on the same scale as SPH.
## to do this, you need to use the coefficients from model m above
dat <- dat %>%
mutate(CPH = coef(m)[1] + coef(m)[2]*CPH) %>%
## pivot the data so all plotting values are in a single column
pivot_longer(c("SPH", "CPH"),
names_to="var", values_to="vals") %>%
mutate(var = factor(var, levels=c("SPH", "CPH"),
labels=c("Sightings", "Clicks")))

ggplot(dat, aes(x=as.factor(Week), y=vals, fill=var)) +
geom_bar(position="dodge", stat="identity") +
## use model m_inv from above to identify the transformation from the tick values of SPH
## to the appropriate tick values of CPH
scale_y_continuous(sec.axis=sec_axis(trans = ~coef(m_inv)[1] + coef(m_inv)[2]*.x, name="Clicks/hour")) +
labs(y="Sightings/hour", x="Week", fill="") +
theme_bw() +
theme(legend.position="top")

Sample Image

Plotting a bar chart with multiple groups

Styling always involves a bit of fiddling and trial (and sometimes error (;). But generally you could probably get quite close to your desired result like so:

library(ggplot2)

ggplot(example, aes(categorical_var, n)) +
geom_bar(position="dodge",stat="identity") +
# Add some more space between groups
scale_x_discrete(expand = expansion(add = .9)) +
# Make axis start at zero
scale_y_continuous(expand = expansion(mult = c(0, .05))) +
# Put facet label to bottom
facet_wrap(~treatment, strip.position = "bottom") +
theme_minimal() +
# Styling via various theme options
theme(panel.spacing.x = unit(0, "pt"),
strip.placement = "outside",
strip.background.x = element_blank(),
axis.line.x = element_line(size = .1),
panel.grid.major.y = element_line(linetype = "dotted"),
panel.grid.major.x = element_blank(),
panel.grid.minor = element_blank())

Sample Image

Make a grouped barplot from count value in ggplot?

Was able to answer my question thanks to @Jon Spring, closing the aes sooner made the difference!

bike_rides %>%  
group_by(member_casual, month_of_use) %>%
summarize(Count = n()) %>%
ggplot(aes(x=month_of_use, y=Count, fill=member_casual)) +
geom_bar(stat='identity', position= "dodge")

New Graph

Practice makes perfect!

Create grouped barplot in R with ordered factor AND individual labels for each bar

We may use

library(gender)
library(dplyr)
library(ggplot2)
gender(as.character(pizza$person)) %>%
select(person = name, gender) %>%
left_join(pizza) %>%
arrange(gender != 'male') %>%
mutate(across(c(person, gender),
~ factor(., levels = unique(.)))) %>%
ggplot(aes(x = person, y = pizzas_eaten, fill = person)) +
geom_bar(stat = 'identity', position = 'dodge') +
facet_wrap(~ gender, scales = 'free_x') +
theme_bw()

-output

Sample Image

Creating a mirrored, grouped barplot?

Try this:

long.dat$value[long.dat$variable == "focal"] <-  -long.dat$value[long.dat$variable == "focal"]

library(ggplot2)
gg <- ggplot(long.dat, aes(interaction(volatile, L1), value)) +
geom_bar(aes(fill = variable), color = "black", stat = "identity") +
scale_y_continuous(labels = abs) +
scale_fill_manual(values = c(control = "#00000000", focal = "blue")) +
coord_flip()
gg

ggplot2 bar plot, grouped and filled by 'variable', and 'focal' data to the left and 'control' data to the right of center

I suspect that the order on the left axis (originally x, but flipped to the left with coord_flip) will be relevant to you. If the current isn't what you need and using interaction(L1, volatile) instead does not give you the correct order, then you will need to combine them intelligently before ggplot(..), convert to a factor, and control the levels= so that they are in the order (and string-formatting) you need.

Most other aspects can be controlled via + theme(...), such as legend.position="top". I don't know what the asterisks in your demo image might be, but they can likely be added with geom_point (making sure to negate those that should be on the left).

For instance, if you have a $star variable that indicates there should be a star on each particular row,

set.seed(42)
long.dat$star <- sample(c(TRUE,FALSE), prob=c(0.2,0.8), size=nrow(long.dat), replace=TRUE)
head(long.dat)
# volatile variable value L1 star
# 1 hexenal3 focal -26 conc1 TRUE
# 2 trans2hexenal focal -27 conc1 TRUE
# 3 trans2hexenol focal -28 conc1 FALSE
# 4 ethyl2hexanol focal -28 conc1 TRUE
# 5 phenethylalcohol focal -31 conc1 FALSE
# 6 methylsalicylate focal -31 conc1 FALSE

then you can add it with a single geom_point call (and adding the legend move):

gg +
geom_point(aes(y=value + 2*sign(value)), data = ~ subset(., star), pch = 8) +
theme(legend.position = "top")

same ggplot with stars and relocated legend



Related Topics



Leave a reply



Submit