Alpha美学显示了箭头的骨架,而不是普通的形状-如何预防? [英] Alpha aesthetic shows arrow's skeleton instead of plain shape - how to prevent it?
问题描述
我的目标是在条形图的末端建立一个带有箭头的条形图.我去定义了arrow
的geom_segment
.我想将一列映射到透明度上,但是Alpha美学在使用箭头对象时似乎无法正常工作.这是代码段:
I'm aiming at building a bar plot with arrows at the end of bars. I went for geom_segment
with arrow
defined. I want to map one column onto transparency, but the alpha aesthetic doesn't seem to work fine with arrow object. Here's the code snippet:
tibble(y = c(10, 20, 30), n = c(300, 100, 200), transparency = c(10, 2, 4)) %>%
ggplot() + geom_segment(aes(x = 0, xend = n, y = y, yend = y, alpha = transparency),
colour = 'red', size = 10, arrow = arrow(length = unit(1.5, 'cm'), type = 'closed')) +
scale_y_continuous(limits = c(5, 35))
可以很容易地观察到,alpha
的对象在使用较低的alpha
值时看起来效果不佳,显示出其骨架而不是普通的透明形状.有办法预防吗?
It can be easily observed that arrow
object doesn't look well with lower values of alpha
, showing its skeleton instead of plain, transparent shape. Is there a way to prevent it?
推荐答案
我们可以创建一个新的几何图形geom_arrowbar
,我们可以像使用其他任何几何图形一样使用它,因此在您的情况下,只需执行以下操作即可提供所需的图形:
We can create a new geom, geom_arrowbar
, that we can use like any other geom, so in your case it would give the desired plot by just doing:
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))
它包含3个参数,分别为column_width
,head_width
和head_length
,如果您不喜欢默认值,可以更改箭头的形状.我们还可以根据需要指定填充颜色和其他美观性:
And it contains 3 parameters, column_width
, head_width
and head_length
that allow you to change the shape of the arrow if you don't like the defaults. We can also specify the fill colour and other aesthetics as needed:
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))
唯一的麻烦是我们必须先写它!
The only snag being that we have to write it first!
Following the examples in the extending ggplot2 vignette, we can define our geom_arrowbar
in the same way that other geoms are defined, except we want to be able to pass in our 3 parameters that control the shape of the arrow. These are added to the params
list of the resultant layer
object, which will be used to create our arrows layer:
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, ...))
}
现在剩下的全部"就是定义GeomArrowBar
是什么.这实际上是ggproto
类定义.其中最重要的部分是draw_panel
成员函数,该函数采用数据帧的每一行并将其转换为箭头形状.在根据x和y坐标以及我们的各种形状参数进行了一些基本数学运算之后,箭头的形状应该是什么,它为数据的每一行生成一个grid::polygonGrob
并将其存储在
Now "all" that remains is to define what a GeomArrowBar
is. This is effectively a ggproto
class definition. The most important part of it is the draw_panel
member function, which takes each line of our dataframe and converts it into arrow shapes. After some basic maths to work out from the x and y co-ordinates as well as our various shape parameters what the shape of the arrow should be, it produces one grid::polygonGrob
for each line of our data and stores it in a gTree
. This forms the graphical component of the layer.
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}
)
此实现远非完美.它缺少一些重要的功能,例如合理的默认轴限制和coord_flip
的功能,并且如果箭头的长度比整列长,它将产生不美观的结果(尽管您可能不想使用这样的图情况).但是,如果值为负数,它将明智地使箭头指向左侧.更好的实现可能还会为空箭头添加一个选项.
This implementation is far from perfect. It is missing some important functionality, such as sensible default axis limits and the ability to coord_flip
, and it will produce unaesthetic results if the arrow heads are longer than the whole column (though you might not want to use such a plot in that situation anyway). It will, however, sensibly have the arrow pointing to the left if you have a negative value. A better implementation might also add an option for empty arrow heads.
简而言之,需要进行大量调整才能消除这些(和其他)错误并使其可以投入生产,但同时可以在无需付出过多努力的情况下生成一些不错的图表就足够了.
In short, it would need a lot of tweaks to iron out these (and other) bugs and make it production-ready, but it's good enough to produce some nice charts without too much effort in the meantime.
由 reprex软件包(v0.3.0)
Created on 2020-03-08 by the reprex package (v0.3.0)
这篇关于Alpha美学显示了箭头的骨架,而不是普通的形状-如何预防?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!