Painting Tails
By Carl Goodwin in R
April 26, 2022
If you’re a cat, go find the nearest open pot of paint. No need to read further.
But if you’re a data scientist looking to paint the tail of a density plot, what to do?
There are techniques for painting a region under a curve. But the experimental ggfx package offers an interesting alternative solution based on the blending modes familiar to users of Photoshop.
library(tidyverse)
library(scales)
library(ggfx)
library(patchwork)
library(wesanderson)
library(clock)
library(tidyquant)
theme_set(theme_bw())
(cols <- wes_palette("Royal1"))
The advantage here is that the tail-painting aesthetic needs no information about the shape of the curve; only the limits on the x-axis.
The left plot shows the raw components without blending. The right plot is only retaining the red where there is a layer below.
p0 <- tibble(outcome = rnorm(10000, 20, 2)) |>
ggplot(aes(outcome)) +
scale_y_continuous(labels = label_percent())
p1 <- p0 +
geom_density(adjust = 2, fill = cols[3]) +
annotate("rect",
xmin = 15, xmax = 18, ymin = -Inf, ymax = Inf,
fill = cols[2]
) +
labs(title = "Without Blending", y = "Density")
p2 <- p0 +
as_reference(geom_density(adjust = 2, fill = cols[3]), id = "density") +
with_blend(annotate("rect",
xmin = 15, xmax = 18, ymin = -Inf, ymax = Inf,
fill = cols[2]
), bg_layer = "density", blend_type = "atop") +
labs(title = "With Blending", y = NULL)
p1 + p2
Of course the red box could also be layered behind a density curve with alpha applied so it shows through. But if the preference is tail-only colouring, it’s a neat solution.
Blending is actually a handy solution for any awkward shape. The same technique is used here with a time series ribbon summarising the median, lower and upper quartiles of a set of closing stock prices.
Note this patch if having prob lems with tq_get
tickrs <- c("AAPL", "NFLX", "TSLA", "ADBE", "FB", "GOOG", "MSFT")
p0 <- tq_get(tickrs, get = "stock.prices", from = "2022-01-01") |>
group_by(date) |>
summarise(
close = quantile(close, c(0.25, 0.5, 0.75)),
quantile = c("lower", "median", "upper") |> factor()
) |>
ungroup() |>
pivot_wider(names_from = quantile, values_from = close) |>
ggplot(aes(date, median)) +
annotate("text",
x = as.Date("2022-03-16"), y = 100,
label = "Helpful\nAnnotation", colour = "black"
) +
scale_y_continuous(limits = c(0, NA)) +
labs(x = NULL)
p1 <- p0 +
geom_ribbon(aes(ymin = lower, ymax = upper), fill = cols[1]) +
geom_line(colour = cols[3]) +
annotate("rect",
xmin = as.Date("2022-03-01"), xmax = as.Date("2022-03-31"),
ymin = -Inf, ymax = Inf, fill = cols[4], colour = "black", linetype = "dashed"
) +
labs(title = "Without Blending", y = "Closing Price")
p2 <- p0 +
as_reference(geom_ribbon(aes(ymin = lower, ymax = upper), fill = cols[1]), id = "ribbon") +
with_blend(
annotate(
"rect",
xmin = as.Date("2022-03-01"), xmax = as.Date("2022-03-31"),
ymin = -Inf, ymax = Inf, fill = cols[4], colour = "black", linetype = "dashed"
),
bg_layer = "ribbon", blend_type = "atop"
) +
geom_line(colour = cols[3]) +
labs(title = "With Blending", y = NULL)
p1 + p2 +
plot_annotation(title = "Median Price Bounded by Upper & Lower Quartiles")
It would be interesting to hear about other uses of ggfx beyond the purely artistic.
- Posted:
- April 26, 2022
- Updated:
- October 7, 2022
- Length:
- 3 minute read, 506 words
- Categories:
- R
- Tags:
- special effects
- See Also:
- Bootstraps & Bandings