【问题标题】:Assign ggplot2 layers to string and evaluate if needed将 ggplot2 层分配给字符串并在需要时进行评估
【发布时间】:2017-11-26 17:22:41
【问题描述】:

背景

我正在尝试使用 ggplot2 加速 an R package that plots high-resolution maps 的函数。函数 basemap 使用大的 shapefile(每个 15 Mb)绘制斯瓦尔巴地图。该地图是使用两个不同的 shapefile 图层、土地和冰川以及一组“定义”(例如主题、轴比例和网格线)创建的。按照现在编写的代码,绘制地图大约需要 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
}

【问题讨论】:

    标签: r ggplot2


    【解决方案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)
    

    【讨论】:

    • 非常感谢。以编程方式加入 ggplot2 层是我思考了很长时间但从未真正研究过的事情。我对这种类型的代码有各种用途。
    【解决方案2】:

    您可以将 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 方法允许您制作“模块”,您可以轻松地将这些“模块”粘贴到绘图函数的if else 语句中,从而使代码更易于阅读,如果您想将许多地图图层传递给绘图功能。

    【讨论】:

    • 我认为你应该保留这个问题,但要编辑它。要么修复替代方法,要么将其完全从问题中删除,而是将其放入此答案中。没有理由在问题中留下已知的错字。
    • @ClausWilke。感谢您的评论。我相应地编辑了问题和答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多