I would like to fill a boxplot with 2 colors instead of 1 in ggplot(), using geom_boxplot().
For example, there would be a first color between 0% and 80% of the boxplot width, and a second color between 80% and 100% of the boxplot width.
I could not find other example of such plots with geom_boxplot so I am not sure this is feasible.
I would like to avoid using geom_rect() or annotate().
Reproducible example
##### Initiating objects
### Colors
colFill <- c("#4a7ff9", "#4aecf9")
colShader <- c("#4964a3", "#499ba2")
### Dataset
set.seed(1)
df <- data.frame(Values=rnorm(100, 100, 10),
Group=rep(c("Blue", "Cyan"), each=50))
##### Display plot
ggplot(df, aes(y=Values, x=Group, fill=Group)) +
geom_boxplot(lwd=3, width=0.5) +
scale_fill_manual(values=colFill) +
theme_classic() +
theme(legend.position="none")

My aim

Thank you
Here's a way to do it using only geom_boxplot. We use three layers: the first is main fill color only, with no lines. The second is a thin boxplot filled with the shading color, with no lines or outliers, nudged to the right. The third is a line-only boxplot. This avoids the need for using annotations or grob hacking.
ggplot(df, aes(y = Values, x = Group, fill = Group)) +
geom_boxplot(lwd = 0, width = 0.5) +
geom_boxplot(position = position_nudge(0.2), width = 0.1,
lwd = 0, aes(fill = paste(Group, 2)), outlier.shape = NA) +
geom_boxplot(lwd = 3, width = 0.5, fill = NA) +
scale_fill_manual(values = c(colFill, colShader)[c(1, 3, 2, 4)],
guide = 'none') +
theme_classic()

This is a chance to play around with the patterns and gradients in ggplot 3.5.0. Here are two approaches:
grid::rectGrob()Create a function that returns a list of two rectGrob objects of different colors, which will act as the pattern:
library(grid)
create_pattern <- function(color1, color2) {
gList(
rectGrob(
x = 1, y = 1, width = 1, height = 2, hjust = 1,
gp = gpar(fill = color1, col = NA)
),
rectGrob(
x = 1, y = 1, width = 0.25, height = 2, hjust = 1,
gp = gpar(fill = color2, col = NA)
)
) |>
gTree(children = _) |>
pattern()
}
Then we can create the patterns and draw the plot:
patterns <- list(
create_pattern(colFill[1], colShader[1]),
create_pattern(colFill[2], colShader[2])
)
# or if you have more levels
# patterns <- Map(\(x, y) create_pattern(x, y), colFill, colShader, USE.NAMES = FALSE)
ggplot(df, aes(y = Values, x = Group, fill = Group)) +
geom_boxplot(lwd = 3, width = 0.5) +
scale_fill_manual(values = patterns) +
theme_classic() +
theme(legend.position = "none")

grid::linearGradient()A more elegant solution using linearGradient(), suggested by Allan Cameron. Again we can create a function to produce the gradient:
binary_gradient <- function(color1, color2, ratio = c(3, 1)) {
linearGradient(
c(color1, color1, color2, color2),
stops = c(c(0, ratio[1] / sum(ratio), ratio[1] / sum(ratio), 1)),
y1 = 1
)
}
patterns <- Map(
binary_gradient,
colFill,
colShader,
USE.NAMES = FALSE
)
ggplot(df, aes(y = Values, x = Group, fill = Group)) +
geom_boxplot(lwd = 3, width = 0.5) +
scale_fill_manual(values = patterns) +
theme_classic() +
theme(legend.position = "none")
# ^^ produces the same plot as above
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