问题:--我正在使用testthat
包测试ggplot2
图形。我找不到图例名的位置(即name
参数用于scale_fill_continuous()
)。name
保存在哪里?(具体示例见文章末尾的可重复示例)。
我的搜索者:我已经搜索过了,但是其他带有[testthat]
和[ggplot]
标签的问题没有帮助(例如这一个和这一个)。我也浏览了单元测试,找不到答案。
可重复的例子:,我正在寻找expression("Legend name"^2)
的位置,这样我就可以测试并确保它是正确的。
library(ggplot2)
library(testthat)
# Create example data and plots
df <- data.frame(
x = c(1, 2, 3, 1, 4, 5, 6, 4),
y = c(1, 2, 1, 1, 1, 2, 1, 1),
z = rep(1:2, each = 4),
group = rep(letters[1:2], each = 4))
my_plot <-
ggplot(df, aes(x = x, y = y, group = group, fill = z )) +
geom_polygon() +
scale_fill_continuous(name = expression("Legend name"^2),
low = "skyblue", high = "orange")
my_wrong_plot <-
ggplot(df, aes(x = x, y = y, group = group, fill = z)) +
geom_polygon() +
scale_fill_continuous(name = expression("Wrong name"^2),
low = "skyblue", high = "orange")
# Example tests that work
test_that("plot is drawn correctly", {
expect_identical(
deparse(my_plot$mapping$group),
deparse(my_wrong_plot$mapping$group),
info = 'The `group` aesthetic is incorrect.'
)
expect_identical(
deparse(my_plot$mapping$fill),
deparse(my_wrong_plot$mapping$fill),
info = 'The `fill` aesthetic is incorrect.'
)
expect_identical(
class(my_plot$layers[[1]]$geom)[1],
class(my_wrong_plot$layers[[1]]$geom)[1],
info = 'There is no polygon layer.'
)
expect_identical(
layer_data(my_plot),
layer_data(my_wrong_plot),
info = "The `scale_fill_continuous()` data is incorrect."
)
})
发布于 2019-01-19 05:00:39
简短回答
假设您的ggplot对象名为p
,并且您已经在缩放中指定了name
参数,那么它将在p$scales$scales[[i]]$name
中找到( i
对应于标度的顺序)。
长答案
下面是一个关于我是如何找到它的长篇大论。没有必要回答这个问题,但它可能会帮助你下一次,当你想要寻找的东西,在全球合作伙伴关系。
起始点:通常情况下,将ggplot对象转换为grob对象很有用,因为grob对象允许我们在ggplot中做一些我们很难破解的事情(例如,在不被切断的情况下,在绘图区域的边缘绘制一个geom,用不同的颜色为不同的面条着色,为每个方面手工绘制面宽,将绘图添加到另一个地图作为自定义注释,等等)。
ggplot2包有一个函数ggplotGrob
,它执行转换。这意味着,如果我们检查沿途的步骤,我们应该能够找到一个步骤,该步骤可以在ggplot对象中找到缩放标题,以便将其转换为某种类型的textGrob。
这反过来意味着我们将接受以下一行代码&向下连续的层,直到我们找出引擎盖下面发生了什么:
ggplotGrob(my_plot)
Layer 1:ggplotGrob
本身只是两个函数( ggplot_build
和ggplot_gtable
)的包装器。
> ggplotGrob
function (x)
{
ggplot_gtable(ggplot_build(x))
}
来自?ggplot_build
ggplot_build
接受绘图对象,并执行生成可以呈现的对象所需的所有步骤。该函数输出两部分:一个数据帧列表(每个层一个)和一个面板对象,其中包含关于轴限值、断点等的所有信息。
来自?ggplot_gtable
此函数构建显示绘图所需的所有grobs,并将它们存储在称为
gtable()
的特殊数据结构中。如果您想要编程操作,这个对象是可以接受的(例如)使图例框2厘米宽,或将多个情节组合成一个单一的显示,保持纵横比的情节。
Layer 2:当输入控制台时,ggplot_build
和ggplot_gtable
只返回一个通用的UseMethod("<function name>"
,而实际的函数没有从ggplot2包中导出。尽管如此,您还是可以在GitHub (链接)上找到它们,也可以使用三冒号:::
访问它们。
> ggplot2:::ggplot_build.ggplot
function (plot)
{
plot <- plot_clone(plot)
# ... omitted for space
layout <- create_layout(plot$facet, plot$coordinates)
data <- layout$setup(layer_data, plot$data, plot$plot_env)
# ... omitted for space
structure(list(data = data, layout = layout, plot = plot),
class = "ggplot_built")
}
> ggplot2:::ggplot_gtable.ggplot_built
function (data)
{
plot <- data$plot
layout <- data$layout
data <- data$data
theme <- plot_theme(plot)
# ... omitted for space
position <- theme$legend.position %||% "right"
# ... omitted for space
legend_box <- if (position != "none") {
build_guides(plot$scales, plot$layers, plot$mapping,
position, theme, plot$guides, plot$labels)
}
# ... omitted for space
}
我们看到ggplot2:::ggplot_gtable.ggplot_built
中有一个代码块,它似乎创建了一个图例框:
legend_box <- if (position != "none") {
build_guides(plot$scales, plot$layers, plot$mapping,
position, theme, plot$guides, plot$labels)
}
让我们来测试一下是否真的是这样:
g.build <- ggplot_build(my_plot)
legend.box <- ggplot2:::build_guides(
g.build$plot$scales,
g.build$plot$layers,
g.build$plot$mapping,
"right",
ggplot2:::plot_theme(g.build$plot),
g.build$plot$guides,
g.build$plot$labels)
grid::grid.draw(legend.box)
的确如此。让我们放大看看ggplot2:::build_guides
做了什么。
Layer 3:在ggplot2:::build_guides
中,在处理图例框的位置和对齐的代码行之后,指南定义(gdefs
)由名为guides_train
的函数生成
> ggplot2:::build_guides
function (scales, layers, default_mapping, position, theme, guides,
labels)
{
# ... omitted for space
gdefs <- guides_train(scales = scales, theme = theme, guides = guides,
labels = labels)
# .. omitted for space
}
和以前一样,我们可以为每个参数插入适当的值&检查这些指导定义说了什么:
gdefs <- ggplot2:::guides_train(
scales = g.build$plot$scales,
theme = ggplot2:::plot_theme(g.build$plot),
guides = g.build$plot$guides,
labels = g.build$plot$labels
)
> gdefs
[[1]]
$title
expression("Legend name"^2)
$title.position
NULL
#... omitted for space
是的,这就是我们所期望的规模名称:expression("Legend name"^2)
。ggplot2:::guides_train
(或其内部的一些函数)已经将其从g.build$plot$<something>
/ ggplot2:::plot_theme(g.build$plot)
中提取出来,但我们必须深入挖掘,看看哪一个&如何实现。
Layer 4:在ggplot2:::guides_train
中,我们找到一行代码,从几个可能的位置之一获取图例标题:
> guides_train
function (scales, theme, guides, labels)
{
gdefs <- list()
for (scale in scales$scales) {
for (output in scale$aesthetics) {
guide <- guides[[output]] %||% scale$guide
# ... omitted for space
guide$title <- scale$make_title(guide$title %|W|%
scale$name %|W|% labels[[output]])
# ... omitted for space
}
}
gdefs
}
(ggplot2:::%||%
和ggplot2:::%|W|%
是包中未导出的函数。它们接受两个值,如果定义了/不放弃第一个值,则返回第二个值。)
我们突然从找传奇头衔的地方太少变成了有太多的地方。在这里,按优先次序排列:
g.build$plot$guides[["fill"]]
,而g.build$plot$guides[["fill"]]$title
的值不是waiver()
:g.build$plot$guides[["fill"]]$title
;g.build$plot$scales$scales[[1]]$guide$title
的值不是waiver()
:g.build$plot$scales$scales[[1]]$guide$title
;g.build$plot$scales$scales[[1]]$name
的值不是waiver()
:g.build$plot$scales$scales[[1]]$name
;g.build$plot$labels[["fill"]]
.我们还通过查看ggplot2:::ggplot_build.ggplot
背后的代码了解到,g.build$plot
与最初输入的my_plot
本质上是相同的,因此您可以用my_plot
替换列表中的每个g.build$plot
实例。
Side备注:如果您的ggplot对象存在某种身份危机,并且包含为相同比例定义的多个图例标题,则使用的优先级列表是相同的。说明如下:
base.plot <- ggplot(df,
aes(x = x, y = y, group = group, fill = z )) +
geom_polygon()
cowplot::plot_grid(
# plot 1: title defined in guides() overrides titles defined in `scale_...`
base.plot + ggtitle("1") +
scale_fill_continuous(
name = "scale",
low = "skyblue", high = "orange",
guide = guide_colorbar(title = "guide in scale")) +
guides(fill = guide_colorbar(title = "guide")),
# plot 2: title defined in scale_...'s guide overrides scale_...'s name
base.plot + ggtitle("2") +
scale_fill_continuous(
name = "scale",
low = "skyblue", high = "orange",
guide = guide_colorbar(title = "guide in scale")),
# plot 3: title defined in `scale_...'s name
base.plot + ggtitle("3") +
scale_fill_continuous(
name = "scale",
low = "skyblue", high = "orange"),
# plot 4: with no title defined anywhere, defaults to variable name
base.plot + ggtitle("4") +
scale_fill_continuous(
low = "skyblue", high = "orange"),
nrow = 2
)
汇总:现在我们已经爬回兔子洞了,我们知道,根据您为图例定义标题的位置,您可以找到它存储在您的ggplot对象中的相应位置。但是,这个标题在情节中是否是可见的,取决于您是否也定义了另一个具有更高优先级的标题.
sample.plot <- ggplot(df,
aes(x = x, y = y, group = group, fill = z )) +
geom_polygon() +
scale_fill_continuous(
name = "title3",
guide = guide_colorbar(title = "title2")) +
guides(fill = guide_colorbar(title = "title1"))
> sample.plot$guides[["fill"]]$title
[1] "title1"
> sample.plot$scales$scales[[1]]$guide$title
[1] "title2"
> sample.plot$scales$scales[[1]]$name
[1] "title3"
> sample.plot$labels[["fill"]]
[1] "z"
https://stackoverflow.com/questions/54240034
复制