如何在 R 中高效地将 25m 分辨率的 raster 聚合为 100m?
可以直接用 terra::aggregate(),通过自定义函数在每个 4 × 4 像元块内统计所有类别的比例。对于 21 类数据,结果是一个包含 21 个图层的 SpatRaster,每层对应一个类别。整个过程无需创建 polygon,也不用先生成 21 个 25m 二值栅格。
这样能减少中间栅格,但运行时间未必更短。自定义 R 函数有调用开销,原方案使用的内置 mean 则经过专门优化。处理大数据时,要根据内存占用和实际耗时选择。
为什么按 4 × 4 聚合就够了
在同一投影坐标系下,目标网格与原网格对齐时:
100 / 25 = 4
每个完整的 100m 单元格包含 16 个 25m 像元。这些像元的面积相同,因此可以直接按数量计算类别比例:
[ p_k=\frac{n_k}{N} ]
其中,(n_k) 是类别 (k) 的像元数量,(N) 是参与统计的像元总数。没有缺失值时,(N=16)。例如,某类别占 6 个像元,其比例就是 6 / 16 = 0.375。
一次聚合,返回所有类别的比例
示例使用两个类别,与问题中的数据对应:
library(terra)
set.seed(42)
# 模拟以米为单位的投影栅格
Land_cover <- rast(
nrows = 100,
ncols = 100,
xmin = 0,
xmax = 2500,
ymin = 0,
ymax = 2500,
crs = "EPSG:27700"
)
classes <- c(1L, 2L)
values(Land_cover) <- sample(
classes,
ncell(Land_cover),
replace = TRUE
)
class_fraction <- function(v) {
v <- v[!is.na(v)]
# 全部缺失时,仍返回固定数量的结果
if (length(v) == 0L) {
return(rep(NA_real_, length(classes)))
}
# 将类别编码映射到固定的输出图层顺序
idx <- match(v, classes)
if (anyNA(idx)) {
stop("发现未包含在 classes 中的类别编码")
}
tabulate(idx, nbins = length(classes)) / length(v)
}
cover100 <- aggregate(
Land_cover,
fact = 4,
fun = class_fraction
)
names(cover100) <- paste0("class_", classes)
cover100
cover100 是一个 25 × 25、分辨率为 100m 的两层栅格。class_1 和 class_2 分别保存两个类别的比例,取值范围为 [0, 1]。
处理实际的 21 类数据时,读取原始文件,并按数据字典设置 classes:
Land_cover <- rast("land_cover_25m.tif")
# 仅当真实类别编码确实是 1 到 21 时使用
classes <- 1:21
cover100 <- aggregate(
Land_cover,
fact = 4,
fun = class_fraction,
filename = "land_cover_fraction_100m.tif",
wopt = list(datatype = "FLT4S")
)
names(cover100) <- paste0("class_", classes)
class_fraction() 使用当前的 classes。如果实际编码是 10、20、30 等不连续值,就应填入完整的真实编码列表。match() 会将这些编码转换为 tabulate() 所需的位置编号。
某个聚合单元格中没有出现的类别,其比例会返回 0,所以输出图层的数量和顺序保持一致。
缺失值决定了比例的含义
代码先去掉 NA,计算的是各类别占有效像元的比例,与原方案加上 na.rm = TRUE 后的含义一致。
例如,一个目标单元格中有 8 个有效像元,全部属于类别 1,另外 8 个为 NA,类别 1 的比例就是:
8 / 8 = 1
如果要计算各类别占完整 100m 单元格的比例,分母应固定为 16。将函数最后一行改为:
tabulate(idx, nbins = length(classes)) / 16
同一个例子的结果就变为 0.5,所有类别的比例之和也可能小于 1。全为 NA 的单元格仍会在前面的判断中返回 NA。
还要检查土地覆盖数据是否用 0、255 等编码表示无数据。这些数值不会自动被当作 NA,需要按数据说明处理。
大栅格如何选择更快的方案
自定义函数省去了逐类生成高分辨率二值图层的步骤,但每个目标单元格都要调用一次 R 函数。处理英国全域这样规模的数据时,调用开销可能比较明显。
原先逐类使用 mean 的方法也合理。如果实测更快,可以逐类聚合并写入文件,只保留低分辨率结果:
files <- file.path(
tempdir(),
paste0("class_", classes, "_100m.tif")
)
for (i in seq_along(classes)) {
aggregate(
Land_cover == classes[i],
fact = 4,
fun = "mean",
na.rm = TRUE,
filename = files[i],
overwrite = TRUE,
wopt = list(datatype = "FLT4S")
)
}
cover100 <- rast(files)
names(cover100) <- paste0("class_", classes)
这种方法仍需逐类计算,但不用同时保留全部 25m 二值图层。代码使用临时目录,需要长期保存时,应换成正式输出目录。比较耗时时,要使用同一片有代表性的数据,并保持缺失值处理规则和文件输出条件一致。
fact = 4 生成的网格继承原栅格的聚合起点。如果要匹配已有的 100m 模板,还需检查其 CRS、范围和网格起点。源数据的行列数若不能被 4 整除,边缘就会出现不完整的聚合块,解释结果时需要结合前面的分母规则。
目标网格与源网格错位,或统计区域有不规则边界时,才需要考虑覆盖面积加权等方法。题目中的网格规则且对齐,无需先转成 polygon。
备注:内容仅供参考。
