r - 阿尔法美学显示箭头的骨架而不是简单的形状 - 如何防止它?
问题描述
我的目标是在条形图的末端建立一个带有箭头的条形图。我去了geom_segment
定义arrow
。我想将一列映射到透明度上,但 alpha 美学似乎不适用于箭头对象。这是代码片段:
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))
可以很容易地观察到arrow
对象在 的值较低时看起来不太好alpha
,显示出它的骨架而不是简单、透明的形状。有没有办法防止它?
解决方案
我们可以创建一个新的几何图形,geom_arrowbar
我们可以像使用任何其他几何图形一样使用它,因此在您的情况下,只需执行以下操作即可提供所需的图:
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
我们还可以根据需要指定填充颜色和其他美学:
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))
唯一的障碍是我们必须先写它!
按照扩展 ggplot2 vignette中的示例,我们可以像定义geom_arrowbar
其他几何图形一样定义我们的几何图形,除了我们希望能够传入控制箭头形状的 3 个参数。这些被添加到params
结果对象的列表中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, ...))
}
现在剩下的“所有”就是定义 aGeomArrowBar
是什么。这实际上是一个ggproto
类定义。其中最重要的部分是draw_panel
成员函数,它获取数据帧的每一行并将其转换为箭头形状。在从 x 和 y 坐标以及我们的各种形状参数中计算出箭头的形状应该是什么基本数学之后,它grid::polygonGrob
为我们数据的每一行生成一个并将其存储在gTree
. 这形成了图层的图形组件。
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
,如果箭头比整个列长,它会产生不美观的结果(尽管你可能不想在那种情况下使用这样的图) . 然而,如果你有一个负值,它会明智地让箭头指向左边。更好的实现还可以为空箭头添加一个选项。
简而言之,需要进行大量调整才能消除这些(和其他)错误并使其可以投入生产,但同时无需太多努力即可生成一些漂亮的图表就足够了。
由reprex 包(v0.3.0)于 2020 年 3 月 8 日创建
推荐阅读
- javascript - 如何替换 JSON 对象 kyes
- c++ - 对于 C++ 中的泛型类型,默认构造函数是否表示零
- java - ChoiceBox 在被点击 3 次后才会填充
- angular - 如何在 Angular 2 中将事件从深层嵌套的子级传递给父级?
- networking - 地址聚合连续块,为什么有些地址无法使用?
- primefaces - 如何在 selectOneMenu 中使用 LazyDataModel 列表
- r - 提取r中字符串中存在的天数
- python - 在 R 和 Python 中计算标准差
- azure - 如何取消 IoT Hub 中正在运行的计划作业?
- html - 如何在 Ionic 4 中每 10 秒后移动 Ion-Slides?