如何在R中计算像素AGB恢复至扰动前值所需行数
问题描述
我有一个包含不同像素年度地上生物量(AGB)的data frame,每个像素都受到过扰动。需要确定每个像素的AGB恢复至**扰动前一年数值(大于等于该值)**所需的行数,但不清楚实现方法。
示例数据如下:
pixel.no <- c(1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2) year <- c(1995,1996,1997,1998,1999,2000,2001,2002,2003,2004, 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004) disturbed <- c("No","No","No","Yes","No","No","No","No","No","No", "No","No","Yes","No","No","Yes","No","No","No","No") AGB <- c(200,204,205,150,155,170,190,205,202,207, 180,181,100,110,150,130,150,160,180,200) data <- data.frame(pixel.no,year,disturbed,AGB) print(data)
输出结果:
pixel.no year disturbed AGB 1 1 1995 No 200 2 1 1996 No 204 3 1 1997 No 205 4 1 1998 Yes 150 5 1 1999 No 155 6 1 2000 No 170 7 1 2001 No 190 8 1 2002 No 205 9 1 2003 No 202 10 1 2004 No 207 11 2 1995 No 180 12 2 1996 No 181 13 2 1997 Yes 100 14 2 1998 No 110 15 2 1999 No 150 16 2 2000 Yes 130 17 2 2001 No 150 18 2 2002 No 160 19 2 2003 No 180 20 2 2004 No 200
针对每一个disturbed="Yes"的实例,需要统计AGB恢复至等于或超过扰动前一年数值所需的行数,上述示例的期望结果为[1] 4 2 3。请问在R中处理大型data frame时,有什么高效的自动实现方法?
高效实现方法
处理大型数据框时,推荐使用dplyr(tidyverse生态)或data.table,两者都能高效完成分组计算,避免循环带来的性能损耗。
方法一:使用tidyverse(dplyr)
核心思路是按像素分组,标记扰动事件后,对每个事件找到后续第一个满足恢复条件的行,计算间隔行数。
library(dplyr) result <- data %>% # 按像素分组并按年份排序(确保时序正确) group_by(pixel.no) %>% arrange(year, .by_group = TRUE) %>% # 提取每个扰动行的前一年AGB值 mutate(pre_disturb_agb = lag(AGB, 1)) %>% # 为每个像素内的扰动事件分配ID mutate(disturb_id = cumsum(disturbed == "Yes")) %>% filter(disturb_id > 0) %>% # 按像素+扰动事件分组,定位第一个恢复的行 group_by(pixel.no, disturb_id) %>% mutate( recovered = AGB >= first(pre_disturb_agb), row_diff = row_number() - 1 ) %>% filter(recovered) %>% slice_head(n = 1) %>% pull(row_diff) print(result) # [1] 4 2 3
方法二:使用data.table(超大型数据首选)
data.table内存效率更高,语法更简洁,适合处理百万级以上行数据。
library(data.table) dt <- as.data.table(data) # 按像素和年份排序,保证时序逻辑正确 setorder(dt, pixel.no, year) # 计算每个扰动行的前一年AGB值 dt[, pre_disturb_agb := shift(AGB, 1), by = pixel.no] # 为每个像素内的扰动事件分配ID dt[, disturb_id := cumsum(disturbed == "Yes"), by = pixel.no] # 筛选扰动组,提取第一个恢复的行数差 result <- dt[disturb_id > 0, .( row_diff = which(AGB >= first(pre_disturb_agb))[1] - 1 ), by = .(pixel.no, disturb_id)]$row_diff print(result) # [1] 4 2 3
关键说明
- 两种方法都要求数据按像素+年份排序,若原始数据未排序必须先执行排序操作,否则计算逻辑会出错。
- 若某个扰动事件后始终未达到恢复条件,结果会返回
NA,可根据需求添加replace_na()等逻辑处理。 - 两种方法均采用分组计算,避免了逐行循环,在大型数据上的性能远优于基础R循环。
内容的提问来源于stack exchange,提问作者helen slater
相关产品推荐
相关产品推荐

