问题:我正在使用testthat
软件包来测试ggplot2
数字。我找不到图例名称的位置(即name
的{{1}}参数)。 scale_fill_continuous()
保存在哪里? (有关具体示例,请参见文章末尾的可复制示例)。
我的搜索者:我已经搜索过SO,但是其他带有name
和[testthat]
标签的问题没有帮助(例如this one和{{ 3}})。我也浏览了this one,但找不到答案。
可复制的示例:我正在寻找[ggplot]
的位置,以便可以测试并确保其正确。
expression("Legend name"^2)
答案 0 :(得分:2)
假设您的ggplot对象命名为p
,并且已在比例尺中指定了name
参数,那么它将在p$scales$scales[[i]]$name
中找到(其中i
对应于秤的顺序)。
以下是关于如何找到它的漫长旅程。不必回答这个问题,但是下次您想在ggplot中查找内容时,它可能会对您有所帮助。
起点:通常,将ggplot对象转换为grob对象很有用,因为后者允许我们执行各种我们不容易在ggplot中入侵的事情(例如,绘制一个绘制区域边缘的几何图形而不会被切除,请使用不同的颜色为不同的构面条上色,为每个构面手动设定构面宽度,将图例作为自定义注释添加到另一幅地图上,等等。
ggplot2软件包具有函数ggplotGrob
,该函数执行转换。这意味着,如果我们逐步研究这些步骤,则应该能够找到一个在ggplot对象中找到比例标题的步骤,以便将其转换为某种textGrob。
反过来,这意味着我们将采用以下单行代码,并逐层向下浏览,直到我们了解幕后情况:
ggplotGrob(my_plot)
第1层:ggplotGrob
本身只是两个函数ggplot_build
和ggplot_gtable
的包装。
> ggplotGrob
function (x)
{
ggplot_gtable(ggplot_build(x))
}
来自?ggplot_build
:
ggplot_build
取得绘图对象,并执行所有必要的步骤 产生可以渲染的对象。该功能输出两个 件:数据帧列表(每层一个)和一个面板 对象,其中包含有关轴限制,中断等的所有信息。
来自?ggplot_gtable
:
此函数将构建显示绘图所需的所有杂点,并且 将它们存储在称为
gtable()
的特殊数据结构中。这个 您可以通过编程操作来处理对象 (例如)将图例框设为2厘米宽,或将多个图块组合成一个 单一显示,可保留各图的宽高比。
第2层:ggplot_build
和ggplot_gtable
都只是在输入到控制台后才返回通用的UseMethod("<function name>"
,并且实际的相关函数不会从中导出ggplot2软件包。不过,您仍然可以在GitHub(link)上找到它们,或者仍然可以使用三元冒号:::
来访问它们。
> 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
的作用。
第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)
中抽出,但我们必须更深入地研究以了解哪种方式。
第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|%
是软件包中未导出的函数。它们采用两个值,如果已定义/未放弃则返回第一个值,否则返回第二个。)
Annnnnnnnnnd我们突然间从寻找传奇人物的地方太少而变成了太多。按优先顺序排列:
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
基本相同,因此您可以在上面带有g.build$plot
的列表。
旁注:如果ggplot对象存在某种身份危机,并且包含为同一比例定义的多个图例标题,则这是优先级相同的列表。下图:
my_plot
摘要:既然我们已经从兔子洞中爬出来了,我们知道根据您为图例定义标题的位置,可以将其存储在相应的位置在您的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
)