I have data in a structure similar to the following structure:
data.frame(x = c("A", "B", "C", "D", "E"),
a = abs(rnorm(5)),
b = abs(rnorm(5)))
I want to display this in the following manner (forgive the poor paint job):
To do this, I have written the below code:
set.seed(20)
data.frame(x = c("A", "B", "C", "D", "E"),
a = abs(rnorm(5)),
b = abs(rnorm(5))) %>%
mutate(b = -b) %>%
gather("source", "amount", a, b) %>%
ggplot(aes(x = x,
y = amount,
fill = source)) +
geom_col() +
scale_y_continuous(labels = abs)
Which gives me the following:
How would I go about adding the gap along y = 0 and filling it with the x axis labels?
I can think of a couple of ways to approach this.
You can try to get tricky with facets and free scales, labeling only the top axis using the approach I saw here.
However, you'll see this leaves some awkward space below the plot since it that didn't seem easy to remove (but see here).
library(ggplot2)
library(tidyr)
library(dplyr)
set.seed(20)
dat = data.frame(x = c("A", "B", "C", "D", "E"),
a = abs(rnorm(5)),
b = abs(rnorm(5))) %>%
mutate(b = -b) %>%
gather("source", "amount", a, b) %>%
mutate(x1 = if_else(source == "a",
as.character(x),
paste0(as.character(x), 'no_display')))
# function to suppress labels
delete_no_display = function(v) {
if_else(stringr::str_detect(v, 'no_display'), '', v)
}
ggplot(dat, aes(x = x1,
y = amount,
fill = source)) +
geom_col() +
facet_wrap(~source, ncol = 1, scales = "free") +
scale_x_discrete(name = NULL, label = delete_no_display) +
scale_y_continuous(name = NULL,
labels = abs,
breaks = c(-3, -2, -1, 0, 1, 2, 3),
expand = c(0, 0)) +
theme(strip.background = element_blank(),
strip.text.x = element_blank(),
axis.ticks.x = element_blank())
Another option is to build the plots separately per group and then combine them. You can do this via functions from package cowplot. This package has five helpful vignettes available if you decide to get into the nitty gritty.
This package does have strong opinions on theme, although given the plot you are attempting to make this theme may be what you want so I left it as is.
First I make the two plots. I added a fill
legend to the first plot, but that can be removed as needed.
g1 = ggplot(subset(dat, source == "a"),
aes(x = x, y = amount, fill = source)) +
geom_col() +
scale_x_discrete(name = NULL) +
scale_y_continuous(name = NULL,
labels = abs,
limits = c(0, 3),
expand = expand_scale(mult = c(0, .1) ) ) +
scale_fill_manual(limits = c("a", "b"),
values = c("#F8766D", "#00BFC4")) +
theme(plot.margin = margin(0, 0, 0, 0),
axis.ticks.x = element_blank())
g2 = ggplot(subset(dat, source == "b"),
aes(x = x, y = amount, fill = source)) +
geom_col() +
scale_x_discrete(name = NULL) +
scale_y_continuous(name = NULL,
labels = abs,
limits = c(-3, 0),
expand = expand_scale(mult = c(.1, 0) ) ) +
scale_fill_manual(limits = c("a", "b"),
values = c("#F8766D", "#00BFC4"),
guide = "none") +
theme(axis.ticks.x = element_blank(),
axis.text.x = element_blank(),
plot.margin = margin(t = 2, unit = "mm"))
Then extract the legend and combine the plots (with no legends) using plot_grid()
.
library(cowplot)
legend_a = get_legend(g1)
combined = plot_grid(g1 + theme(legend.position = "none"),
g2,
ncol = 1, align = "v")
This combined
plot then looks like:
You can add the legend back on (see the shared legends vignette) and/or put on an overall y axis label as shown here if you want (although the legend spacing gets funky).
plot_grid(combined, legend_a,
rel_widths = c(2, .2),
scale = c(.93, 1)) +
draw_label("amount", x = 0, y = .5, angle = 90, vjust = 1)
The downside to the cowplot approach is that the bottom plot ends up slightly larger than the upper one. If I use align = "hv"
I'm back to having a little extra space along the bottom. It may be best to remove all axes and then manually insert the labels. I feel like there is info to be gleaned here, but I didn't get that far.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With