如何使用 lapply 在 R 中批量处理 geoTIFF

Posted

技术标签:

【中文标题】如何使用 lapply 在 R 中批量处理 geoTIFF【英文标题】:How to batch process geoTIFFs in R with lapply 【发布时间】:2017-12-01 10:44:28 【问题描述】:

我有一些大的geoTIFF,现在我想将它们转换为ASCII文件,经过一些搜索,我写了这些代码:

library(raster)

f <- list.files("inputFolder", pattern = "*.tif", full.names = TRUE)
r <- lapply(f, raster)
a <- lapply(r, writeRaster, filename = "output", format = "ascii")

让我困惑的是,如何根据原始名称分别命名输出文件?

我试过了:

a <- lapply(r, writeRaster, filename = "outputFolder" + f, format = "ascii")

但我收到错误:

二元运算符的非数字参数

然后我尝试了:

a <- lapply(r, writeRaster, filename = paste0(f, ".asc"), format = "ascii")

但我收到了:

文件中的错误(文件名,“w”):无效的“描述”参数 另外:警告消息:1:在 if (filename == "") : 条件的长度 > 1,并且只使用第一个元素 2:在 if (!file.exists(dirname(filename))) : 条件长度 > 1 并且只使用第一个元素 3:在 if (toupper(x@file@name) == toupper(filename)) :条件有 长度 > 1 并且只使用第一个元素 4: 在 if (trim(filename) == "") : 条件长度 > 1 并且只有 第一个元素将被使用 5: 在 if (!file.exists(dirname(filename))) :条件的长度 > 1 并且只有第一个元素是 used 6: In if (filename == "") : 条件长度 > 1 并且 只使用第一个元素 7: 在 if (!overwrite & file.exists(filename)) : 条件的长度 > 1 并且只有 将使用第一个元素

【问题讨论】:

您能提供几个示例 geoTIFF 供我们测试吗? 我很想,但是这些例子太大了,不能上传,所以我给你一个类似的 biogeo.ucdavis.edu/data/climate/cmip5/2_5m/cc26bi50.zip 【参考方案1】:

我认为你基本上差不多了,但有两个更正:

首先,您调用writeRaster 是因为它的副作用(即它能够将文件写入文件系统),因此您不需要将lapply() 循环的输出分配给对象。因此,删除 a &lt;- 我们有:

lapply(r, writeRaster, filename = paste0(f, ".asc"), format = "ascii")

接下来,filename 参数不会以这种方式循环通过 f。您有两个选择,其中最简单的可能是使用匿名函数将r@file@name 槽传递给filename 参数:

lapply(r, function(x) 
  writeRaster(x, filename = x@file@name, format = "ascii", overwrite = TRUE)
)

您的另一个选择是并行循环 rf,就像在 python 中使用 for r, f in... 一样,这可以使用 purrr 完成:

library("purrr")
walk2(r, f, function(x, y) 
  writeRaster(x = x, filename = y, format = "ascii")
)

这里我们使用walk2() 而不是map2(),因为我们需要调用函数来获得副作用。这将循环通过 rf 一起,因此您可以传递一个作为要写入的对象,一个作为文件名。


编辑:这是我用来重现问题的代码

library("raster")

tmp_dir = tempdir()
tmp     = tempfile(tmpdir = tmp_dir, fileext = ".zip")

download.file(
  "http://biogeo.ucdavis.edu/data/climate/cmip5/10m/cc26bi50.zip",
  destfile = tmp
)
unzip(tmp, exdir = tmp_dir)

f = list.files(tmp_dir, pattern = ".tif$", full.names = TRUE)
r = lapply(f, raster)

# Solution one
lapply(r, function(x) 
  writeRaster(x, filename = x@file@name, format = "ascii", overwrite = TRUE)
)

# solution two
library("purrr")
walk2(r, f, function(x, y) 
  writeRaster(x = x, filename = y, format = "ascii")
)

【讨论】:

最好使用filename(x)而不是x@file@name 看来purrr是学习必备包,我去找找资料,谢谢 @RobertH 我以前从未真正遇到过filename()。为什么它比仅仅访问一个插槽更受欢迎?谢谢 最好将插槽视为对象的“私有”,并使用方法获取或设置它们的值。这是因为如果一个槽中的值改变了,其他槽中的值也可能需要改变。这通常不是读取槽值的问题,但可以(例如,从 Raster 对象读取值时)。最好使用方法,因为这些方法会根据需要对值进行完整性检查,这将导致更可靠的代码。我认为这是一个“合同”,“文件名”等方法返回的值不会随着时间而改变,但类的内部工作和插槽名可能会改变。 当然,在某些情况下,您合法地需要直接访问 S4 插槽。例如,如果包开发人员没有提供获取或设置值的方法。但即便如此,您也应该考虑这是因为他们认为没有必要编写这样的方法(这很常见),还是因为他们认为您不应该更改这些值。我相信 raster 包具有来自所有用户级相关插槽的方法。【参考方案2】:

要测试如何处理小文件:

library(raster)
s <- stack(system.file("external/rlogo.grd", package="raster")) 
writeRaster(s, file='testtif', format='GTiff', bylayer=T, overwrite=T)
f <- list.files(pattern="testtif_..tif")

现在您可以将f 与Phil 的优秀示例一起使用。您也可以一步合并所有内容 lapply:

f <- list.files("inputFolder", pattern = "*.tif", full.names = TRUE)
r <- lapply(f, function(i)  writeRaster(raster(i), filename=extension(i, '.asc'), overwrite=TRUE) )

但是如果你使用 lapply 有问题,写一个循环(没问题!):

for (i in 1:length(f)) 
   r <- raster(f[i])
   ff <- extension(f[i], '.asc')
   writeRaster(r, ff)

或者像这样

for (file in f) 
   r <- raster(file)
   ff <- extension(file, '.asc')
   writeRaster(r, ff)

【讨论】:

filename(x) 非常有用!我对x@file@name不熟悉,有文档吗? x@file@name 没有记录,因为您不应该使用它。 @TianjianQin @file@name 是基于您提供的数据的S4 插槽。您可以使用str() 检查您的数据结构

以上是关于如何使用 lapply 在 R 中批量处理 geoTIFF的主要内容,如果未能解决你的问题,请参考以下文章

R中的高效批量处理函数(lapply sapply apply tapply mapply)(转)

如何在 R 的 lapply() 中引用正在操作的行

如何在 R 中使用 lapply 消除异常

如何在 lapply (R) 中显式定义附加参数

如何使用lapply来计算r中列表中的唯一值

如何将 lapply 的输出保存(分配)到 R 中的单个变量中?