如何使用 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 <-
我们有:
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)
)
您的另一个选择是并行循环 r
和 f
,就像在 python 中使用 for r, f in...
一样,这可以使用 purrr
完成:
library("purrr")
walk2(r, f, function(x, y)
writeRaster(x = x, filename = y, format = "ascii")
)
这里我们使用walk2()
而不是map2()
,因为我们需要调用函数来获得副作用。这将循环通过 r
和 f
一起,因此您可以传递一个作为要写入的对象,一个作为文件名。
编辑:这是我用来重现问题的代码
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的主要内容,如果未能解决你的问题,请参考以下文章