首页 文章

将ggplot2图层分配给字符串并根据需要进行评估

提问于
浏览
2

背景

我正在尝试使用ggplot2加速an R package that plots high-resolution maps的功能 . 函数 basemap 使用大的shapefile(每个15 Mb)绘制Svalbard的映射 . 使用两个不同的shapefile,land和glaciers以及一组"definitions"(例如主题,轴刻度和网格线)创建 Map . 在现在编写代码时绘制 Map 大约需要30秒 .

我的想法是,如果我可以打破代码片段中的层并允许用户不绘制冰川的可能性,那么该函数的执行速度会快两倍 . 此外,如果我可以将ggplot2语法指定为文本字符串并仅评估所需的字符串,则R不会浪费时间制作该函数未使用的ggplot2对象 .

我知道如何使用 if else 语句执行此操作,但我想避免多次编写定义 . 似乎可以将 scale_x_continuous() 分配给对象,但分配 scale_x_continuous() + scale_y_continuous() 会产生错误:

scale_x_continuous()中的错误scale_y_continuous():二元运算符的非数字参数

问题

如何将ggplot2轴定义分配给文本字符串并将它们与数据层粘贴在一起?

示例

我使用 iris 数据集作为示例,这样您就不必下载大而且非常混乱的PlotSvalbard包 . 请注意,我知道如何将颜色映射到变量 . 这里的要点只是将shapefile作为 iris 数据集的两个子集来说明:

library(ggplot2)
data(iris)

## Datasets to imitate the shapefile layers
ds1 <- subset(iris, Species == "setosa")
ds2 <- subset(iris, Species == "versicolor")

## Example how the basemap function works at the moment:

ggplot() + geom_point(data = ds1, aes(x = Sepal.Length, y = Sepal.Width), color = "red") +
geom_point(data = ds2, aes(x = Sepal.Length, y = Sepal.Width), color = "blue") +
scale_x_continuous("Longitude", breaks = seq(3, 8, by = 0.5)) +
scale_y_continuous("Latitude", breaks = seq(1,5, by = 1)) + theme_bw()

## Now I would like to plot ds2 only if user defines "keep.glaciers = TRUE"

land <- ggplot() + geom_point(data = ds1, aes(x = Sepal.Length, 
y = Sepal.Width), color = "red")

glacier <- geom_point(data = ds2, aes(x = Sepal.Length, y = Sepal.Width),
 color = "blue")

def <- scale_x_continuous("Longitude", breaks = seq(3, 8, by = 0.5)) +
scale_y_continuous("Latitude", breaks = seq(1,5, by = 1)) + theme_bw() ## Error!
# Error in +scale_x_continuous("Longitude", breaks = seq(3, 8, by = 0.5)) :
#  invalid argument to unary operator

## The ultimate goal:
keep.glaciers = TRUE

if(keep.glaciers) {
  land + glacier + def # error, see above
} else {
  land + def
}

2 回答

  • 1

    您可以将图层保留在 list 中,并将 + 运算符与 ggplot 一起使用 .

    例如,函数 plot_glacierslanddefglacier 图层保留在列表中 . if 语句控制 glacier 图层的构造 . 该函数将返回带有所需图层的 ggplot 对象 .

    plot_glaciers <- function(keep_glaciers = TRUE) {
      layers <- vector('list')
    
      layers$land    <- geom_point(data = ds1, aes(x = Sepal.Length, y = Sepal.Width), color = "red")
      layers$def     <- list(scale_x_continuous("Longitude", breaks = seq(3, 8, by = 0.5)),
                        scale_y_continuous("Latitude", breaks = seq(1,5, by = 1)),
                        theme_bw())
    
      if (keep_glaciers) {
        layers$glacier <- geom_point(data = ds2, aes(x = Sepal.Length, y = Sepal.Width), color = "blue")
      } 
    
      ggplot() + layers
    }
    
    plot_glaciers(TRUE)
    plot_glaciers(FALSE)
    
  • 4

    您可以将ggplot2图层分配给字符串,并在将它们粘贴在一起后评估字符串:

    map_layers <- function(layer) {
    
       switch(layer, 
        land = 'ggplot() + geom_point(data = ds1, aes(x = Sepal.Length, y = Sepal.Width), color = "red")',
        glacier = 'geom_point(data = ds2, aes(x = Sepal.Length, y = Sepal.Width), color = "blue")',
        def = 'scale_x_continuous("Longitude", breaks = seq(3, 8, by = 0.5)) + scale_y_continuous("Latitude", breaks = seq(1,5, by = 1)) + theme_bw()',
        stop(paste("map layer", layer, "not found"))
    )
    }
    
    plot_glaciers <- function(keep.glaciers = TRUE) {
    
        if(keep.glaciers) {
          eval(parse(text=paste(map_layers("land"), map_layers("glacier"),
     map_layers("def"), sep = " + ")))
            } else {
          eval(parse(text=paste(map_layers("land"), map_layers("def"), sep = " + ")))
        }
    }
    
    plot_glaciers()
    plot_glaciers(FALSE)
    

    使用嵌套的 switch 方法可以使"modules"可以轻松地粘贴在绘图函数的 if else 语句中,使代码更容易阅读,如果要将许多 Map 图层传递给绘图函数 .

相关问题