接收面板比例/布局信息的 Facet labeller 功能
Facet labeller function that receives panel scale / layout information
我正在创建一个多面图,其中一个面板比另一个面板宽,并且都有相当长的标签。我正在尝试找出一种方法来根据面板的宽度按比例包装标签,这样如果文本太长而无法整齐地放入其条带中,则文本 仅 被包装。我想象类似于 ggfittext
的东西,但用于标签文本。我知道绘图的某些部分是相对的并且取决于屏幕/canvas 大小,所以我更希望能够说最小的面板应该在给定点处中断,而其他面板应该中断比例。
我想出了一个手动的、动态设置断点的强力方法,但它需要绘制两次图并且不能很好地缩放。
没有标签调整的样本数据和绘图:
library(purrr)
library(ggplot2)
df <- tibble::tribble(
~field, ~group, ~value,
"Computer science, mathematics, biology, engineering", "A", 65,
"Computer science, mathematics, biology, engineering", "B", 55,
"English, social sciences, history, visual and performing arts", "A", 30,
"English, social sciences, history, visual and performing arts", "B", 25
)
p <- ggplot(df, aes(x = group, y = value)) +
geom_col() +
coord_flip()
p_facet <- p + facet_grid(cols = vars(field), scales = "free_x", space = "free")
p_facet
显然,我可以使用带有手动设置断点的字符串包装函数,但这将应用于每个标签,而不管其面板的大小。因此,虽然 40 个字符对于较小的面板来说是一个很好的宽度,但对于较宽的面板来说就没有必要了;我宁愿让更广泛的情节的标签保持完整。
p + facet_grid(cols = vars(field), scales = "free_x", space = "free",
labeller = labeller(.cols = label_wrap_gen(40)))
我深入研究了绘图的参数,发现这个范围列表对应于每个面板的限制。对于其中的每一个,diff
是范围的宽度。将 40 个字符作为较小面板的断点,我可以计算字符数与范围(以及面板宽度)之间的比率,并使用它来为任何其他面板找到成比例的断点。 Labeller 函数只获取标签字符串作为参数,所以我实际上不能将任何这些值传递给标签函数本身——我只是将它们保存在工作环境中。
ranges <- ggplot_build(p_facet)$layout$panel_params %>%
map(pluck, "x", "continuous_range")
ranges
#> [[1]]
#> [1] -3.25 68.25
#>
#> [[2]]
#> [1] -1.5 31.5
widths <- map_dbl(ranges, diff)
ratio <- 40 / min(widths)
# 40 / base_width = x1 / width1
p + facet_grid(cols = vars(field), scales = "free_x", space = "free",
labeller = labeller(.cols = function(labels) {
brkpts <- map_dbl(widths, ~round(. * ratio))
map2_chr(labels, brkpts, stringr::str_wrap)
}))
这得到了我想要的输出,但它很麻烦,感觉它打破了标签功能的预期逻辑。是否有其他方法可以调用 labeller
或 facet_grid
中的其他内容来查找有关面板范围或大小的布局信息,而无需先保存绘图,然后使用计算的参数进行馈送回到另一个 facet_grid
电话?
我对这个问题有部分答案,希望这能激发比我聪明的人一些东西,从而得出更完整的答案。
建议的解决方案是对条形文本使用 ggtext::element_textbox()
,它可以根据可用宽度换行文本。然而,我们又遇到了一个不同的问题,即无法自动确定环绕文本的高度。
library(purrr)
library(ggplot2)
library(ggtext)
library(patchwork)
df <- tibble::tribble(
~field, ~group, ~value,
"Computer science, mathematics, biology, engineering", "A", 65,
"Computer science, mathematics, biology, engineering", "B", 55,
"English, social sciences, history, visual and performing arts", "A", 30,
"English, social sciences, history, visual and performing arts", "B", 25
)
p <- ggplot(df, aes(x = group, y = value)) +
geom_col() +
coord_flip() +
facet_grid(cols = vars(field), scales = "free_x", space = "free") +
theme(
strip.text.x = element_textbox(
# relative fontsize = 0.8 for default strips
height = unit(3 * 0.8, "lines"),
width = unit(1, "npc"),
margin = margin(4.4, 4.4, 4.4, 4.4),
halign = 0.5, valign = 0.5
)
)
p
只是为了展示包装适应不同的宽度,但我们必须调整高度以获得更好的绘图。
p + p & theme(strip.text.x.top = element_textbox(height = unit(5, "lines")))
由 reprex package (v1.0.0)
于 2021-07-08 创建
我正在创建一个多面图,其中一个面板比另一个面板宽,并且都有相当长的标签。我正在尝试找出一种方法来根据面板的宽度按比例包装标签,这样如果文本太长而无法整齐地放入其条带中,则文本 仅 被包装。我想象类似于 ggfittext
的东西,但用于标签文本。我知道绘图的某些部分是相对的并且取决于屏幕/canvas 大小,所以我更希望能够说最小的面板应该在给定点处中断,而其他面板应该中断比例。
我想出了一个手动的、动态设置断点的强力方法,但它需要绘制两次图并且不能很好地缩放。
没有标签调整的样本数据和绘图:
library(purrr)
library(ggplot2)
df <- tibble::tribble(
~field, ~group, ~value,
"Computer science, mathematics, biology, engineering", "A", 65,
"Computer science, mathematics, biology, engineering", "B", 55,
"English, social sciences, history, visual and performing arts", "A", 30,
"English, social sciences, history, visual and performing arts", "B", 25
)
p <- ggplot(df, aes(x = group, y = value)) +
geom_col() +
coord_flip()
p_facet <- p + facet_grid(cols = vars(field), scales = "free_x", space = "free")
p_facet
显然,我可以使用带有手动设置断点的字符串包装函数,但这将应用于每个标签,而不管其面板的大小。因此,虽然 40 个字符对于较小的面板来说是一个很好的宽度,但对于较宽的面板来说就没有必要了;我宁愿让更广泛的情节的标签保持完整。
p + facet_grid(cols = vars(field), scales = "free_x", space = "free",
labeller = labeller(.cols = label_wrap_gen(40)))
我深入研究了绘图的参数,发现这个范围列表对应于每个面板的限制。对于其中的每一个,diff
是范围的宽度。将 40 个字符作为较小面板的断点,我可以计算字符数与范围(以及面板宽度)之间的比率,并使用它来为任何其他面板找到成比例的断点。 Labeller 函数只获取标签字符串作为参数,所以我实际上不能将任何这些值传递给标签函数本身——我只是将它们保存在工作环境中。
ranges <- ggplot_build(p_facet)$layout$panel_params %>%
map(pluck, "x", "continuous_range")
ranges
#> [[1]]
#> [1] -3.25 68.25
#>
#> [[2]]
#> [1] -1.5 31.5
widths <- map_dbl(ranges, diff)
ratio <- 40 / min(widths)
# 40 / base_width = x1 / width1
p + facet_grid(cols = vars(field), scales = "free_x", space = "free",
labeller = labeller(.cols = function(labels) {
brkpts <- map_dbl(widths, ~round(. * ratio))
map2_chr(labels, brkpts, stringr::str_wrap)
}))
这得到了我想要的输出,但它很麻烦,感觉它打破了标签功能的预期逻辑。是否有其他方法可以调用 labeller
或 facet_grid
中的其他内容来查找有关面板范围或大小的布局信息,而无需先保存绘图,然后使用计算的参数进行馈送回到另一个 facet_grid
电话?
我对这个问题有部分答案,希望这能激发比我聪明的人一些东西,从而得出更完整的答案。
建议的解决方案是对条形文本使用 ggtext::element_textbox()
,它可以根据可用宽度换行文本。然而,我们又遇到了一个不同的问题,即无法自动确定环绕文本的高度。
library(purrr)
library(ggplot2)
library(ggtext)
library(patchwork)
df <- tibble::tribble(
~field, ~group, ~value,
"Computer science, mathematics, biology, engineering", "A", 65,
"Computer science, mathematics, biology, engineering", "B", 55,
"English, social sciences, history, visual and performing arts", "A", 30,
"English, social sciences, history, visual and performing arts", "B", 25
)
p <- ggplot(df, aes(x = group, y = value)) +
geom_col() +
coord_flip() +
facet_grid(cols = vars(field), scales = "free_x", space = "free") +
theme(
strip.text.x = element_textbox(
# relative fontsize = 0.8 for default strips
height = unit(3 * 0.8, "lines"),
width = unit(1, "npc"),
margin = margin(4.4, 4.4, 4.4, 4.4),
halign = 0.5, valign = 0.5
)
)
p
只是为了展示包装适应不同的宽度,但我们必须调整高度以获得更好的绘图。
p + p & theme(strip.text.x.top = element_textbox(height = unit(5, "lines")))
由 reprex package (v1.0.0)
于 2021-07-08 创建