R - ggplot2 'dodge' geom_step () to overlay geom_bar ()
Calculations using ggplot2 geom_bar(stat="identity")
are an efficient method for visualizing samples. I would like to use this method to display my observable counts and compare them to the expected counts, which I would like to use geom_step
to overlay a storyline layer on top of the panel.
However, when I do this, I am faced with the problem that by default the bubbles have their positions, but geom_step
not. For example, using both continuous and discrete dependent variables:
library(tidyverse)
test <- data_frame(a = 1:10, b = runif(10, 1, 10))
test_plot <- ggplot(test, aes(a, b)) +
geom_bar(stat="identity") +
geom_step(color = 'red')
test2 <- data_frame(a = letters[1:10], b = runif(10, 1, 10))
test2_plot <- ggplot(test2, aes(a, b, group = 1)) +
geom_bar(stat="identity") +
geom_step(color = 'red'))
gridExtra::grid.arrange(test_plot, test2_plot, ncol = 2)
As you can see, the two layers are offset, which is undesirable.
Reading the docs I can see it geom_path
has a parameter position =
, however trying something like geom_step(color = 'red', position = position_dodge(width = 0.5))
it doesn't do what I want, rather it shrinks the bars and stairstep line towards the center. Another option is to set up the data directly like this geom_step(aes(a-0.5, b), color = 'red')
, which gives an almost acceptable result for data with continuous dependent variables. You can also calculate the line of the aging line as a function and plot it with stat_function()
.
However, these approaches do not apply to data with discrete dependent variables, and my actual data has discrete dependent variables, so I need another answer.
Also, as you move, the start bar will not span the last bar as shown in the above image. Is there a simple elegant way to extend it to cover the last bar?
If geom_step()
- the wrong approach, and what I am trying to get can be achieved in another way, I am interested in that too.
source to share
I think the most efficient way to solve this problem is to define custom geometry like this:
library(tidyverse)
geom_step_extend <- function(data, extend = 1, nudge = -0.5,
...) {
# Function for computing the last segment data
get_step_extend_data <- function(data, extend = 1, nudge = -0.5) {
data_out <- as.data.frame(data[order(data[[1]]), ])
n <- nrow(data)
max_x_y <- data_out[n, 2]
if (is.numeric(data_out[[1]])) {
max_x <- data_out[n, 1] + nudge
} else {
max_x <- n + nudge
}
data.frame(x = max_x,
y = max_x_y,
xend = max_x + extend,
yend = max_x_y)
}
# The resulting geom
list(
geom_step(position = position_nudge(x = nudge), ...),
geom_segment(
data = get_step_extend_data(data, extend = extend, nudge = nudge),
mapping = aes(x = x, y = y,
xend = xend, yend = yend),
...
)
)
}
set.seed(111)
test <- data_frame(a = 1:10, b = runif(10, 1, 10))
test2 <- data_frame(a = letters[1:10], b = runif(10, 1, 10))
test_plot <- ggplot(test, aes(a, b, group = 1)) +
geom_bar(stat = "identity") +
geom_step_extend(data = test, colour = "red")
test2_plot <- ggplot(test2, aes(a, b, group = 1)) +
geom_bar(stat = "identity") +
geom_step_extend(data = test2, colour = "red")
gridExtra::grid.arrange(test_plot, test2_plot, ncol = 2)
Basically, this solution has three parts:
- Move to the left with a
position_nudge
stepped curve at the desired value (in this case -0.5); - Calculate the missing (what's on the right) segment data using the function
get_step_extend_data
. His behavior is inspiredggplot2:::stairstep
, which is the main functiongeom_step
; - Compose
geom_step
withgeom_segment
in separate geometry withlist
.
source to share
Here's a rather crude solution, but should work in this case.
Create an alternate dataframe that expanded each row to extend the x-axis by -0.5 and 0.5:
test2 <- data.frame(a = lapply(1:nrow(test), function(x) c(test[x,"a"]-.5, test[x,"a"], test[x, "a"]+0.5)) %>% unlist,
b = lapply(1:nrow(test), function(x) rep(test[x,"b"], 3)) %>% unlist)
Draw a path with the geom_line argument:
ggplot(test, aes(a,b)) + geom_bar(stat="identity", alpha=.7) + geom_line(data=test2, colour="red")
It will look neater if you set the geom_bar width to 1:
ggplot(test, aes(a,b)) + geom_bar(width=1, stat="identity", alpha=.7) + geom_line(data=test2, colour="red")
source to share