Podemos criar um novo geom, geom_arrowbar
que podemos usar como qualquer outro geom; portanto, no seu caso, ele forneceria o gráfico desejado apenas fazendo:
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))
E contém 3 parâmetros, column_width
, head_width
e head_length
que lhe permitem alterar a forma da seta se você não faz como os padrões. Também podemos especificar a cor do preenchimento e outras estéticas, conforme necessário:
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))
O único problema é que temos que escrevê-lo primeiro!
Seguindo os exemplos da vinheta ggplot2 , podemos definir nossa geom_arrowbar
da mesma maneira que outros geoms são definidos, exceto que queremos poder passar nossos três parâmetros que controlam a forma da seta. Eles são adicionados à params
lista do layer
objeto resultante , que será usado para criar nossa camada de setas:
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, ...))
}
Agora, "tudo" que resta é definir o que GeomArrowBar
é. Esta é efetivamente uma ggproto
definição de classe. A parte mais importante é a draw_panel
função de membro, que pega cada linha do nosso quadro de dados e a converte em formas de seta. Após algumas matemáticas básicas para trabalhar a partir das coordenadas xey, bem como nossos vários parâmetros de forma, qual deve ser a forma da seta, ela produz uma grid::polygonGrob
para cada linha de nossos dados e a armazena em a gTree
. Isso forma o componente gráfico da camada.
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}
)
Esta implementação está longe de ser perfeita. Faltam algumas funcionalidades importantes, como limites de eixo padrão sensíveis e a capacidade de coord_flip
, e produzirá resultados não estéticos se as pontas das setas forem mais longas que a coluna inteira (embora você não queira usar esse gráfico nessa situação) . No entanto, sensivelmente, a seta estará apontando para a esquerda se você tiver um valor negativo. Uma implementação melhor também pode adicionar uma opção para pontas de setas vazias.
Em resumo, seriam necessários muitos ajustes para resolver esses (e outros) bugs e torná-los prontos para produção, mas é bom o suficiente para produzir alguns gráficos agradáveis sem muito esforço nesse meio tempo.
Criado em 2020-03-08 pelo pacote reprex (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))