【问题标题】:R markdown: remove white space around ggmap() plot automatically without manual fig.heigh/fig.widthR markdown:自动删除 ggmap() 绘图周围的空白,无需手动 fig.heigh/fig.width
【发布时间】:2022-08-15 23:29:00
【问题描述】:

我想用 R markdown 创建一个 html 输出,其中包括用 ggmap() 制作的绘图集合。当地图(或地图,如果是构面)的宽度大于高度时,html 输出中的绘图上方和下方都会有空白,我想自动删除它而无需太多额外的工作。

前面已经讨论过空白。我发现的一种解决方案是适当地指定 fig.height 和 fig.width (通过手动尝试)。但是,我宁愿避免为每个图尝试合适的高度/宽度值,因为我的每个图都有不同的高度/宽度比。

以前的想法是计算绘图的宽/高比,然后指定 fig.asp: How to remove white space above and below image in R Markdown? 有人建议使用 R 函数来确定比率: Rmarkdown crop white space around ggplots 但这仅在将绘图保存为 png 作为中间步骤时才有效。

有没有办法调整绘图的边距或它如何包含在降价中自动地(无需绕行保存的图像或手动调整某些高度/宽度/asp 值)以删除绘图上方和下方的额外空白?

一个工作示例:

---
title: \"Plot margins\"
output: html_document
---

The following plot has some white space above and below it.

```{r, echo=FALSE, message=FALSE, cache=TRUE}
require(ggmap)
df <- data.frame(lon = c(14.04, 14.06), lat = c(53.04, 53.07), species = c(\"species_1\", \"species_2\"))
cbbox <- make_bbox(lon = c(14.0, 14.2), lat = c(53.0, 53.1), f = .1)
map_data <- get_map(location = cbbox,  source = \"stamen\")
ggmap(map_data) +
  geom_point(data = df,
             aes(x=lon, y=lat), size=2) +
  facet_wrap(~ species, ncol=2)
```

The next plot does not have that large white margin.

```{r, echo=FALSE, message=FALSE, cache=TRUE}
require(ggmap)
df <- data.frame(lon = c(14.04, 14.06), lat = c(53.04, 53.07), species = c(\"species_1\", \"species_2\"))
cbbox <- make_bbox(lon = c(14.0, 14.2), lat = c(53.0, 53.1), f = .1)
map_data <- get_map(location = cbbox,  source = \"stamen\")
ggmap(map_data) +
  geom_point(data = df,
             aes(x=lon, y=lat), size=2)
```

Some text below.

    标签: r ggplot2 r-markdown ggmap


    【解决方案1】:

    更新:

    底部原创内容

    考虑到原始答案,我查看了函数get_dims() 以了解数据是如何收集的。

    我发现让它忽略预设的图形尺寸非常容易。我建议您将此函数作为一个块添加到自身中。您也不需要调用DeLuciatoR 库。

    我留下了该函数的原始代码get_dims(),将其重命名为getDims(),并注释掉了导致RMarkdown 预设图形大小问题的部分。您可以删除该部分,但我认为从包中查看原始代码可能很有用。

    ```{r getterDims}
    getDims = function(ggobj, maxheight, maxwidth = maxheight, units = "in", 
        ...) 
    {
        # if (inherits(ggobj, "ggplot") && !isTRUE(ggobj$respect) && 
        #     is.null(ggobj$theme$aspect.ratio) && is.null(ggobj$coordinates$ratio) && 
        #     is.null(theme_get()$aspect.ratio)) {
        #     return(list(height = maxheight, width = maxwidth))
        # }
        tmpf = tempfile(pattern = "dispos-a-plot", fileext = ".png")
        png(filename = tmpf, height = maxheight, width = maxwidth, 
            units = units, res = 120, ...)
        on.exit({
            dev.off()
            unlink(tmpf)
        })
        if (inherits(ggobj, "ggplot")) {
            g = ggplotGrob(ggobj)
        }
        else if (inherits(ggobj, "gtable")) {
            g = ggobj
        }
        else {
            stop("Don't know how to get sizes for object of class ", 
                deparse(class(ggobj)))
        }
        stopifnot(grid::convertUnit(grid::unit(1, "null"), "in", 
            "x", valueOnly = TRUE) == 0)
        known_ht = sum(grid::convertHeight(g$heights, units, valueOnly = TRUE))
        known_wd = sum(grid::convertWidth(g$widths, units, valueOnly = TRUE))
        free_ht = maxheight - known_ht
        free_wd = maxwidth - known_wd
        if (packageVersion("grid") >= "4.0.0") {
            null_rowhts <- as.numeric(g$heights[grid::unitType(g$heights) == 
                "null"])
            null_colwds <- as.numeric(g$widths[grid::unitType(g$widths) == 
                "null"])
            panel_asps <- (matrix(null_rowhts, ncol = 1) %*% matrix(1/null_colwds, 
                nrow = 1))
        }
        else {
            all_null_rowhts <- (grid::convertHeight(.null_as_if_inch(g$heights), 
                "in", valueOnly = TRUE) - grid::convertHeight(g$heights, 
                "in", valueOnly = TRUE))
            all_null_colwds <- (grid::convertWidth(.null_as_if_inch(g$widths), 
                "in", valueOnly = TRUE) - grid::convertWidth(g$widths, 
                "in", valueOnly = TRUE))
            null_rowhts <- all_null_rowhts[all_null_rowhts > 0]
            null_colwds <- all_null_colwds[all_null_colwds > 0]
            panel_asps <- (matrix(null_rowhts, ncol = 1) %*% matrix(1/null_colwds, 
                nrow = 1))
        }
        max_rowhts = free_ht/sum(null_rowhts) * null_rowhts
        max_colwds = free_wd/sum(null_colwds) * null_colwds
        rowhts_if_maxwd = max_colwds[1] * panel_asps[, 1]
        colwds_if_maxht = max_rowhts[1]/panel_asps[1, ]
        height = min(maxheight, known_ht + sum(rowhts_if_maxwd))
        width = min(maxwidth, known_wd + sum(colwds_if_maxht))
        return(list(height = height, width = width))
    }
    ```
    

    现在,当您使用此功能时,预设的图形大小是多少或是否指定coord_fixed() 都无关紧要。

    例如:

    ```{r whatChuGot, echo=FALSE, message=FALSE, cache=TRUE}
    
    df <- data.frame(lon = c(14.04, 14.06), lat = c(53.04, 53.07), 
                     species = c("species_1", "species_2"))
    cbbox <- make_bbox(lon = c(14.0, 14.2), lat = c(53.0, 53.1), f = .1)
    map_data <- get_map(location = cbbox,  source = "stamen")
    ggp <- ggmap(map_data) +
      geom_point(data = df,
                 aes(x = lon, y = lat), size = 2) +
      facet_wrap(~ species, ncol = 2) + 
      theme(plot.margin = unit(rep(.1, 4), "cm"),
            plot.background = element_rect(fill = "green"),
            panel.background = element_rect(fill = "blue"))
    
    ggpd <- getDims(ggp, maxwidth = 7, maxheight = 8)
    ```
    
    The new dims are `r ggpd`.
    
    ```{r showME,results="asis",fig.height=ggpd$height, fig.width=ggpd$width}
    # make me shine
    ggp
    ```
    



    起初:

    我使用了 DeLuciatoR 包中的函数 get_dims()。这不是 Cran 包。你必须通过 Github 获得它。

    devtools::install_github("infotroph/DeLuciatoR")
    

    如果您使用此功能运行绘图,您将获得最佳尺寸。但是,如果您预设图形大小然后运行它,您将得到您的预设值(即fig.heightfig.width)。为什么这很重要?好吧,R Markdown预设你的图表的大小。你必须解决这个限制。

    首先,我将向您展示其工作原理的基本前提。接下来,我将向您展示如何将其合并到 R Markdown 中。

    对于第一个图表,我为背景添加了颜色,因此您可以看到我对边距做了什么。 (有什么;没有什么;所有的爵士乐……)

    我为所有图像添加了边框,以便您可以看到 SO 和图像之间的区别。

    library(tidyverse)
    library(ggmap)
    library(DeLuciatoR)
    ggp <- ggmap(map_data) +
      geom_point(data = df,
                 aes(x = lon, y = lat), size = 2) +
      facet_wrap(~ species, ncol = 2) + 
      coord_fixed() +                                       # lat/lon equidistant
      theme(plot.margin = unit(rep(.1, 4), "cm"),           # make the plot margin 1 mm
            plot.background = element_rect(fill = "green"),
            panel.background = element_rect(fill = "blue")) # show me my margins
    


    现在创建了图形对象,获取尺寸。

    get_dims(ggp, maxwidth = 7, maxheight = 8)
    # $height
    # [1] 2.229751
    # 
    # $width
    # [1] 7
    #  
    

    这真是太棒了IMO。

    coord_fixed() 是使这项工作在 R Markdown 中工作的一部分。 (这会导致预设大小出现问题。)另一种方法是在您的 RMD 之外使用此 get_dims() 调用(讨厌)。可能有很多方法可以设置它,这样 RMD 就不会超出最佳尺寸。你必须尝试一下,看看什么对你有用。

    好的,将其合并到 RMD 中...

    您将把图表分成两个单独的块。首先,将图形调用到对象(此处为ggp)并调用维度(此处为ggpd)。在单独的块中,使用块选项中的维度打印图表。在第二个块中,ggpd$heightfig.height 的设置; ggpd$widthfig.width

    My change; no issues!
    
    ```{r whatChuGot,echo=FALSE,message=FALSE,cache=TRUE}
    
    df <- data.frame(lon = c(14.04, 14.06), lat = c(53.04, 53.07), 
                     species = c("species_1", "species_2"))
    cbbox <- make_bbox(lon = c(14.0, 14.2), lat = c(53.0, 53.1), f = .1)
    map_data <- get_map(location = cbbox,  source = "stamen")
    ggp <- ggmap(map_data) +
      geom_point(data = df,
                 aes(x = lon, y = lat), size = 2) +
      facet_wrap(~ species, ncol = 2) + 
      coord_fixed() +
      theme(plot.margin = unit(rep(.1, 4), "cm"),
            plot.background = element_rect(fill = "green"),
            panel.background = element_rect(fill = "blue"))
    
    ggpd <- get_dims(ggp, maxwidth = 7, maxheight = 8)
    # $height
    # [1] 2.229751
    # 
    # $width
    # [1] 7
    #      
    ```
    The dims are `r ggpd`.
    
    ```{r showMe,results="asis",fig.height=ggpd$height, fig.width=ggpd$width}
    # make me shine
    ggp
    ```
    


    如果您有任何问题,请告诉我!

    这是我整个 RMD 的一张照片(颜色在顶部,但重点突出)。如果您不觉得 html_document 有巨大的利润空间,您可以为 .main-container 添加样式 max-width: unset;。默认为 940 像素,与显示器大小无关。

    【讨论】:

    • 我认为这可能适用于 utm 坐标,但并不完全适用于 lon/lat。它依赖于 coord_equal(),当应用于 lon/lat 时,这会生成一个投影/纵横比不太正确的地图(我与谷歌地图和使用 coord_map() 和 coord_quickmap() 选项生成的绘图进行了比较)。受您的回答启发,我使用 coord_quickmap() 来获取地图的纵横比(然后可以将其粘贴到 fig.asp 中),给定数据输入(而不是绘图)。这不能作为完整的解决方案,例如在绘图阶段使用 scale_x/y_continuous() 放大绘图时。
    • 我应该补充一点:我会对 UTM 感到满意,但 ggmap() 似乎只适用于 lon/lat。任何使用 ggmap 切换到 utm 的建议都可能是解决方案的另一条途径。
    • 我想到了!我已经用这些信息更新了我的答案。
    猜你喜欢
    • 2020-08-30
    • 2020-11-10
    • 2019-09-29
    • 2019-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 2020-06-11
    相关资源
    最近更新 更多