Краткий ответ
Если ваш объект ggplot назван p
, и вы указали аргумент name
в своей шкале, он будет найден в p$scales$scales[[i]]$name
(где i
соответствует порядку шкалы).
Длинный ответ
Ниже приводится длинный рассказ о том, как я его нашел. Не обязательно отвечать на вопрос, но он может помочь вам в следующий раз, когда вы захотите найти что-то в ggplot.
Начальная точка : Часто полезно преобразовать объект ggplot в объект grob, так как последний позволяет нам делать все виды вещей, которые мы не можем легко взломать в ggplot (например, построить geom на краю области графика без обрезки закрасьте разные полосы фасетов разными цветами, вручную добавьте ширину фасета для каждого фасета, добавьте график на другую карту в качестве пользовательской аннотации и т. д.).
В пакете 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
в приведенном выше списке на 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
)
Резюме : Теперь, когда мы вылезли обратно из кроличьей норы, мы знаем, что в зависимости от того, где вы определили заголовок для вашей легенды, вы можете найти его в соответствующем месте в пределах ваш объект 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"