动态调整 ggplot2 图例在 Quarto 文档类型中的大小

6
我正在使用R生成一个包含图形和表格的Quarto文档。该文档应呈现为HTML和PDF文件。大部分工作都很顺利。然而,当生成图形时,一些图例被切断了边缘。

Legend does not fit in the figure

我已经找到了解决方案,可以调整图例的大小,使所有的图例都适合于图形,使用以下方法:
theme(legend.text = element_text(size = 6),
      legend.title = element_text(size = 11))

这在HTML文档中呈现出一个漂亮的图形:

The legend fits in the HTML document

然而,当我尝试呈现PDF文档时,图像看起来像这样:

The problem is still not solved for PDF documents

当然,我可以找到有关图形高度的解决方案,但我在创建可重现示例时没有包含该代码。但是,正如可以看到的那样,图例仍然被切割。 这里是Quarto文档的可重现示例:
---
title: "Reproducible Example"
format:
  html:
    toc: true
  pdf:
    toc: true
---

This is a reproducible example to present my problem.

```{r}
library(tidyverse)
library(cowplot)
library(ggnewscale)
library(ggtext)
```

## Create data

```{r}
species_df <- tibble(fish_species = factor(x = c("Rainbow trout<br />(<i>Oncorhynchus mykiss</i>)", "Brown trout<br />(<i>Salmo trutta fario</i>)", "Whitefish<br />(<i>Coregonus sp.</i>)", "Grayling<br />(<i>Thymallus sp.</i>)", "Other salmonid<br />(other <i>Salmonidae</i>)", "Common perch<br />(<i>Perca fluviatilis</i>)", "Pikeperch<br />(<i>Sander lucioperca</i>)", "Other percid<br />(other <i>Percidae</i>)", "Koi<br />(<i>Cyprinus carpio</i>)", "Other carp<br />(other <i>Cyprinidae</i>)", "Freshwater ornamental fish<br />(diverse species)", "Saltwater ornamental fish<br />(diverse species)", "Crayfish<br />(<i>Crustacea</i>)", "Missing data"),
                                           levels = c("Rainbow trout<br />(<i>Oncorhynchus mykiss</i>)", "Brown trout<br />(<i>Salmo trutta fario</i>)", "Whitefish<br />(<i>Coregonus sp.</i>)", "Grayling<br />(<i>Thymallus sp.</i>)", "Other salmonid<br />(other <i>Salmonidae</i>)", "Common perch<br />(<i>Perca fluviatilis</i>)", "Pikeperch<br />(<i>Sander lucioperca</i>)", "Other percid<br />(other <i>Percidae</i>)", "Koi<br />(<i>Cyprinus carpio</i>)", "Other carp<br />(other <i>Cyprinidae</i>)", "Freshwater ornamental fish<br />(diverse species)", "Saltwater ornamental fish<br />(diverse species)", "Crayfish<br />(<i>Crustacea</i>)", "Missing data"))) |> 
  mutate(family = factor(x = case_when(fish_species %in% c("Rainbow trout<br />(<i>Oncorhynchus mykiss</i>)", "Brown trout<br />(<i>Salmo trutta fario</i>)", "Whitefish<br />(<i>Coregonus sp.</i>)", "Grayling<br />(<i>Thymallus sp.</i>)", "Other salmonid<br />(other <i>Salmonidae</i>)") ~ "Salmonid<br />(<i>Salmonidae</i>)",
                                       fish_species %in% c("Common perch<br />(<i>Perca fluviatilis</i>)", "Pikeperch<br />(<i>Sander lucioperca</i>)", "Other percid<br />(other <i>Percidae</i>)") ~ "Percid<br />(<i>Percidae</i>)",
                                       fish_species %in% c("Koi<br />(<i>Cyprinus carpio</i>)", "Other carp<br />(other <i>Cyprinidae</i>)") ~ "Cyprinid<br />(<i>Cyprinidae</i>)",
                                       fish_species %in% c("Freshwater ornamental fish<br />(diverse species)", "Saltwater ornamental fish<br />(diverse species)") ~ "Ornamental fish",
                                       fish_species %in% c("Crayfish<br />(<i>Crustacea</i>)") ~ "Crayfish<br />(<i>Crustacea</i>)",
                                       TRUE ~ "Other"),
                         levels = c("Salmonid<br />(<i>Salmonidae</i>)", "Percid<br />(<i>Percidae</i>)", "Cyprinid<br />(<i>Cyprinidae</i>)", "Ornamental fish", "Crayfish<br />(<i>Crustacea</i>)", "Other")),
         family_sober = factor(x = word(string = family,
                                        sep = "<br />"),
                               levels = word(string = levels(family),
                                             sep = "<br />")))

quartal <- paste("Quartal", 1:4)
year <- 2020:2022

quartal_df <- crossing(quartal, year) |> 
  mutate(quartal_year = factor(x = paste(year, quartal, sep = " - "),
                               levels = sort(paste(year, quartal, sep = " - ")))) |> 
  arrange(quartal_year) |> 
  mutate(quartal_num = seq_len(n())) |> 
  slice_tail(n = 9)

df <- species_df |> 
  crossing(quartal_df) |> 
  mutate(number = sample(x = 1:20, size = n(), replace = TRUE))
```

## Create plot

```{r}
# Prepare x axis breaks for ticks
quartal_breaks <- df |> 
  distinct(quartal_year, year) |> 
  group_by(year) |> 
  summarise(n_quartals = n()) |> 
  mutate(breaks = NA)

for (i in seq_len(nrow(quartal_breaks))) {
  
  quartal_breaks$breaks[i] <- 1 + sum(quartal_breaks$n_quartals[seq_len(i - 1)])
}



#Prepare colours
n_groups <- df |> distinct(family) |> nrow()
colour_group <- RColorBrewer::brewer.pal(name = "Dark2", n = n_groups)
colours <- c()

j <- 0

for (i in seq_len(n_groups)) {
  j <- j + 1
  
  n_in_group <- df |> filter(family == levels(df$family)[i]) |> distinct(fish_species) |> nrow()
    
  group_palette <- colorRampPalette(colors = c(colour_group[j], "#FFFFFF"))
    
  group_colours <- group_palette(n_in_group + 1) |> head(-1)
    
  colours <- append(colours, group_colours)
}

colours <- setNames(colours, df |> distinct(fish_species) |> pull(fish_species) |> sort())



#Create plot
fig <- ggplot(data = df) +
  geom_line(aes(x = quartal_num, y = number, colour = fish_species))

j <- 0

for (i in df |> distinct(family) |> arrange(family) |> pull()) {
  
  j <- j + 1
  
  fig <- fig +
    geom_line(aes(x = quartal_num, y = number, colour = fish_species)) +
    scale_colour_manual(aesthetics = "colour",
                        values = colours,
                        labels = df |> filter(family == i) |> distinct(fish_species) |> pull(fish_species),
                        breaks = df |> filter(family == i) |> distinct(fish_species) |> pull(fish_species),
                        name = i,
                        guide = guide_legend(title.position = "top", direction = "vertical", order = j)) +
    new_scale_colour()
}

fig <- fig +
  facet_wrap(vars(family_sober)) +
  scale_x_continuous(breaks = quartal_breaks$breaks,
                     labels = quartal_breaks$year,
                     minor_breaks = c(1:9)) +
  xlab("Time") +
  ylab("Number") +
  guides(color = guide_legend(override.aes = list(size = 0.8))) +
  theme(legend.position = "bottom",
        legend.text = element_markdown(size = 6),
        legend.key.height = unit(1.8, units = "char"),
        legend.margin = margin(t = 0, r = 0, b = 0, l = 0, unit='cm'),
        legend.spacing = unit(0.5, units = "char"),
        legend.title = element_markdown(size = 11),
        axis.text.x=element_text(angle=45, hjust=1, size = 7))



#Prepare plot to print
# fig_legend <- get_legend(fig)
# 
# fig_nolegend <- fig +
#   theme(legend.position = "none")
# 
# fig_print <- plot_grid(fig_nolegend,
#                        fig_legend,
#                        ncol = 1,
#                        rel_heights = c(3, 1))

print(fig)
# print(fig_print)
```

我尝试使用cowplot中的get_legend从图例中提取图例,然后将1)没有图例的图形(theme(legend.position = "none"))和2)单独的图例(cowplot::get_legend())组合起来(请参见可重现示例末尾的代码),但问题在于在图例提取过程中会创建虚拟图形,提取出的图例将根据所使用的渲染版本进行裁剪,如下所示:

Legend extracted with cowplot::get_legend

我已经在网上找到了很多关于通过更改图例文本大小和/或其他选项来调整图例大小的材料,但它们都要求对每个图形和每个渲染选项手动进行操作。
为了避免这种情况,我正在寻找另一种方法,在打印绘图之前提取整个图例(不切割任何边缘),以便能够将其单独与生成的没有图例的图形组合,以便调整图例的大小以适应应该打印在上面的材料。
提前感谢您的帮助!
1个回答

1

一种方法是使用if-else逻辑将不同ggplot组件大小的值存储在列表中,然后在ggplot对象中使用该列表而不是硬编码的值。我们可以使用knitr::is_html_outputknitr::is_latex_output来确定文档输出格式。(对于更多的输出变量,可以使用Quarto的条件内容)。

```{r}
if(knitr::is_html_output()) {
  size_list <- list(
    legend_text = 7,
    legend_title = 11,
    legend_space = 0.7,
    fig_height = 6,
    fig_width = 8
  )
} else if (knitr::is_latex_output()) {
  size_list <- list(
    legend_text = 7,
    legend_title = 10,
    legend_space = 0.5,
    fig_height = 6,
    fig_width = 8
  )
}
```

```{r}
#| fig-height: !expr size_list$fig_height
#| fig-width: !expr size_list$fig_width

fig <- fig +
  facet_wrap(vars(family_sober)) +
  scale_x_continuous(breaks = quartal_breaks$breaks,
                     labels = quartal_breaks$year,
                     minor_breaks = c(1:9)) +
  xlab("Time") +
  ylab("Number") +
  guides(color = guide_legend(override.aes = list(size = 0.8))) +
  theme(legend.position = "bottom",
        legend.text = element_markdown(size = size_list$legend_text),
        legend.key.height = unit(1.8, units = "char"),
        legend.margin = margin(t = 0, r = 0, b = 0, l = 0, unit='cm'),
        legend.spacing = unit(size_list$legend_space, units = "char"),
        legend.title = element_markdown(size = size_list$legend_title),
        axis.text.x=element_text(angle=45, hjust=1, size = 7))

print(fig)
```

PDF 输出


PDF 输出


HTML 输出


HTML 输出



3
谢谢,这是一个好的解决方法。然而,它意味着对于每个绘图和每个输出格式都必须通过试错找到正确的值,这是耗时的。此外,如果这是一份在给定时间段后实际更新的报告,则应每次检查它是否仍在工作,因为添加新类别可能会导致同样的问题。 我会等待看看是否有人提出另一个解决方案,如果没有,那么我将接受你的答案。 - GaryDe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接