Gradient fill in ggplot2
Asked Answered
V

2

7

Say if have the following plot.

library(ggplot2)
n <- 1169
df22 <- data.frame(x = 1:n, val = seq(0, 0.5, length.out = n), type = 1)

ggplot(df22, aes(x = x, y = val)) +
  geom_ribbon(aes(ymax = val, ymin = 0, fill = type, group = type))

Instead of the blue color i would like to have a Gradient fill (from blue to red - vertically. So starting with blue at the bottom and red on top with a Parameter to Control the smoothness of Color change).

I found the following resource: https://ggplot2.tidyverse.org/reference/scale_gradient.html

Unfortunately, it didnt work out for me as my data is not continous(?).

Vivie answered 20/11, 2018 at 16:13 Comment(1)
There's not an easy way to do this. scale_gradient is made for mapping data to different colors. You don't have data corresponding to the different colors. There are hacky work-arounds (possible dupes), e.g. How to produce a gradient fill within a polygon? and How to make a gradient color filled timeseries plot?Reorder
P
11

The following code will do it (but horizontally):

library(scales) # for muted
ggplot(df22, aes(x = x, y = val)) +
  geom_ribbon(aes(ymax = val, ymin = 0, group = type)) +
  geom_col(aes(fill = val)) +
  scale_fill_gradient2(position="bottom" , low = "blue", mid = muted("blue"), high = "red", 
                       midpoint = median(df22$val)) 

Ribbon-Horizontally

If you want to make it vertically, you may flip the coordinates using coord_flip() upside down.

ggplot(df22, aes(x = val, y = x)) +
  geom_ribbon(aes(ymax = val, ymin = 0)) +
  coord_flip() +
  geom_col(aes(fill = val)) +
  scale_fill_gradient2(position="bottom" , low = "blue", mid = muted("blue"), high = "red", 
                       midpoint = median(df22$val)) 

Ribbon-Vertically

Or, if you want it to be horizontal with a vertical gradient (as you requested), you might need to go around it by playing with your data and using the geom_segment() instead of geom_ribbon(), like the following:

vals <- lapply(df22$val, function(y) seq(0, y, by = 0.001))
y <- unlist(vals)
mid <- rep(df22$x, lengths(vals))
d2 <- data.frame(x = mid - 1, xend = mid + 1, y = y, yend = y)

ggplot(data = d2, aes(x = x, xend = xend, y = y, yend = yend, color = y)) +
  geom_segment(size = 1) +
  scale_color_gradient2(low = "blue", mid = muted("blue"), high = "red", midpoint = median(d2$y)) 

This will give you the following:

enter image description here

Hope you find it helpful.

Philender answered 20/11, 2018 at 16:53 Comment(2)
thank you so much for the great answer. I made an upvote already, as we are very Close. I would require "vertically. So starting with blue at the bottom and red on top", is that possible as well?Vivie
thank you for the Response. Unfortunately thats not possible. It will have to be vertically - blue on bottom, red on top.Vivie
H
2

Using ggplot2 >= 3.5.0 (and R >= 4.2.0) which adds support for gradient fills this can now more easily be achieved.

As a first step this requires to sepeify a gradient fill using e.g. grid::linearGradient where the direction of the gradient can be set via the x1, x2, y1 and y2 argument, e.g. in your case we have to deviate from the defaults by setting x2 = unit(0, "npc") to get a gradient in the vertical direction.

This gradient could then be applied to the geom by passing it to the fill= argument:

library(ggplot2)
library(grid)

grad_ungroup <- linearGradient(
  c("blue", "red"),
  x1 = unit(0, "npc"), y1 = unit(0, "npc"),
  x2 = unit(0, "npc"), y2 = unit(1, "npc")
)

ggplot(df22, aes(x = x)) +
  geom_ribbon(aes(ymax = val, ymin = 0),
    fill = grad_ungroup
  )

Hooknosed answered 24/2 at 10:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.