Możemy stworzyć nowy geom, z geom_arrowbar
którego będziemy mogli korzystać jak każdy inny geom, więc w twoim przypadku dałoby to pożądaną fabułę, po prostu wykonując:
tibble(y = c(10, 20, 30), n = c(300, 100, 200), transparency = c(10, 2, 4)) %>%
ggplot() +
geom_arrowbar(aes(x = n, y = y, alpha = transparency), fill = "red") +
scale_y_continuous(limits = c(5, 35)) +
scale_x_continuous(limits = c(0, 350))
I zawiera 3 parametry column_width
, head_width
a head_length
które pozwalają zmienić kształt strzałki, jeśli nie podoba ci się domyślne. W razie potrzeby możemy również określić kolor wypełnienia i inne elementy estetyczne:
tibble(y = c(10, 20, 30), n = c(300, 100, 200), transparency = c(10, 2, 4)) %>%
ggplot() +
geom_arrowbar(aes(x = n, y = y, alpha = transparency, fill = as.factor(n)),
column_width = 1.8, head_width = 1.8, colour = "black") +
scale_y_continuous(limits = c(5, 35)) +
scale_x_continuous(limits = c(0, 350))
Jedyną przeszkodą jest to, że najpierw musimy to napisać!
Postępując zgodnie z przykładami w rozszerzającej się winiecie ggplot2 , możemy zdefiniować naszą geom_arrowbar
w ten sam sposób, w jaki zdefiniowane są inne geomy, z wyjątkiem tego, że chcemy móc przekazać nasze 3 parametry kontrolujące kształt strzałki. Są one dodawane do params
listy wynikowego layer
obiektu, który zostanie użyty do utworzenia naszej warstwy strzał:
library(tidyverse)
geom_arrowbar <- function(mapping = NULL, data = NULL, stat = "identity",
position = "identity", na.rm = FALSE, show.legend = NA,
inherit.aes = TRUE, head_width = 1, column_width = 1,
head_length = 1, ...)
{
layer(geom = GeomArrowBar, mapping = mapping, data = data, stat = stat,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, head_width = head_width,
column_width = column_width, head_length = head_length, ...))
}
Teraz „wszystko”, co pozostaje, to zdefiniowanie, czym GeomArrowBar
jest a . Jest to faktycznie ggproto
definicja klasy. Najważniejszą jego częścią jest draw_panel
funkcja członka, która pobiera każdą linię naszej ramki danych i przekształca ją w kształty strzałek. Po kilku podstawowych obliczeniach matematycznych dotyczących współrzędnych xiy oraz różnych parametrów kształtu, jaki powinien być kształt strzałki, tworzy jeden grid::polygonGrob
dla każdej linii naszych danych i przechowuje go w gTree
. To tworzy komponent graficzny warstwy.
GeomArrowBar <- ggproto("GeomArrowBar", Geom,
required_aes = c("x", "y"),
default_aes = aes(colour = NA, fill = "grey20", size = 0.5, linetype = 1, alpha = 1),
extra_params = c("na.rm", "head_width", "column_width", "head_length"),
draw_key = draw_key_polygon,
draw_panel = function(data, panel_params, coord, head_width = 1,
column_width = 1, head_length = 1) {
hwidth <- head_width / 5
wid <- column_width / 10
len <- head_length / 10
data2 <- data
data2$x[1] <- data2$y[1] <- 0
zero <- coord$transform(data2, panel_params)$x[1]
coords <- coord$transform(data, panel_params)
make_arrow_y <- function(y, wid, hwidth) {
c(y - wid/2, y - wid/2, y - hwidth/2, y, y + hwidth/2, y + wid/2, y + wid/2)
}
make_arrow_x <- function(x, len){
if(x < zero) len <- -len
return(c(zero, x - len, x - len , x, x - len, x - len, zero))
}
my_tree <- grid::gTree()
for(i in seq(nrow(coords))){
my_tree <- grid::addGrob(my_tree, grid::polygonGrob(
make_arrow_x(coords$x[i], len),
make_arrow_y(coords$y[i], wid, hwidth),
default.units = "native",
gp = grid::gpar(
col = coords$colour[i],
fill = scales::alpha(coords$fill[i], coords$alpha[i]),
lwd = coords$size[i] * .pt,
lty = coords$linetype[i]))) }
my_tree}
)
Ta implementacja jest daleka od ideału. Brakuje niektórych ważnych funkcji, takich jak rozsądne domyślne limity osi i zdolność do coord_flip
, i przyniesie nieestetyczne wyniki, jeśli strzały są dłuższe niż cała kolumna (choć i tak nie chcesz używać takiego wykresu w tej sytuacji) . Jednak rozsądnie będzie mieć strzałkę skierowaną w lewo, jeśli masz wartość ujemną. Lepsza implementacja może również dodać opcję pustych strzałek.
Krótko mówiąc, wymagałoby to wielu poprawek, aby wyeliminować te (i inne) błędy i sprawić, że będzie gotowy do produkcji, ale w międzyczasie jest wystarczająco dobry, aby wygenerować fajne wykresy.
Utworzono 2020-03-08 przez pakiet reprezentx (v0.3.0)
tibble(y = c(10, 20, 30), n = c(300, 100, 200), transparency = c(10, 2, 4)) %>% ggplot() + geom_segment(aes(x = 0, xend = n-10, y = y, yend = y, alpha = transparency), colour = 'red', size = 10) + geom_segment(aes(x = n-0.1, xend = n, y = y, yend = y, alpha = transparency), colour = 'red', size = 1, arrow = arrow(length = unit(1.5, 'cm'), type = 'closed')) + scale_y_continuous(limits = c(5, 35))