如何将vegan函数应用于dataframe列表并解决下标赋值报错?
问题
现有一个按地点拆分的dataframe列表,每个dataframe包含年度、地点和物种计数列。需要对每个dataframe除前两列外的所有列,应用vegan包的specaccum(已封装为specacc函数)和specnumber(已封装为specrich函数),最终生成一个新列表,每个元素是包含Year、spec_acum(specacc结果)、Richness(specrich结果)的dataframe,且新列表元素名称与原列表一致。
原循环代码执行时能打印临时dataframe,但保存结果时报错:
Error in [[<-(tmp, i, value = df_temp) : invalid subscript type 'list'
自定义函数及可复现代码如下:
# 自定义函数 specacc <- function(df) { out <- vegan::specaccum(df[,-c(1,2)], method = "collector") out$richness } specrich <- function(df) { vegan::specnumber(df[,-c(1,2)]) } # 生成示例数据 Years <- c(2022, 2023, 2024, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024) Location <- c(rep("A",3), rep("B",9), rep("C",9), rep("D",7), rep("E",9)) a <- c(rep(0,13), 2, rep(0, 23)) b <- c(rep(0,3),3,11,1,1,2,0,3,0,0,0,0,0,3,3,4,0,6,7,0,0,2,0,4,2,2,0,0,0,1,1,2,1,28,2) c <- c(rep(0,30),100,600,rep(0,5)) d <- c(rep(0,10),5, rep(0,14),4,1, rep(0,6), rep(3,3),0) e <- c(rep(0,4),20, rep(0,7),150,120,100,600,100,500,500,100,200,290,93,195,0,40,50,0,170,300,0,111,113,470,380,486,30) counts <- data.frame(Years, Location, a, b, c, d, e) # 拆分为列表 counts.lst <- split(counts, counts$Location)
错误原因
- 循环下标错误:原循环
for (i in counts.lst)中,i是列表中的子dataframe,而非索引或名称,用test2.lst[[i]]赋值时,列表下标仅接受字符(名称)或数值(索引),不能直接用dataframe,因此触发类型错误。 - 数据结构不符合需求:原代码将结果拼接为宽格式(年份为列),但需求是长格式(每行对应一个年份,包含三个字段)。
- 列名匹配错误:原数据中年份列名为
Years,但代码中用pull(years, Year)提取,会导致列名不匹配的隐性错误。
修正方案
方案1:修正for循环
遍历列表的名称(而非直接遍历元素),构造符合需求的长格式dataframe:
test2.lst <- list() # 遍历原列表的名称,保留元素命名 for (loc_name in names(counts.lst)) { # 获取当前地点的dataframe current_df <- counts.lst[[loc_name]] # 提取年份列(注意原列名是Years) year_vec <- current_df$Years # 计算两个指标 spec_acum_vec <- specacc(current_df) richness_vec <- specrich(current_df) # 构造目标结构的dataframe result_df <- data.frame( Year = year_vec, spec_acum = spec_acum_vec, Richness = richness_vec ) # 用原名称存入新列表 test2.lst[[loc_name]] <- result_df }
方案2:用purrr包简化操作(更高效)
利用purrr::map可以直接遍历列表并保留原名称,代码更简洁:
library(purrr) test2.lst <- map(counts.lst, function(df) { data.frame( Year = df$Years, spec_acum = specacc(df), Richness = specrich(df) ) })
执行后,test2.lst中的每个元素都是符合要求的长格式dataframe,且名称与原counts.lst一致。
内容的提问来源于stack exchange,提问作者Pedro Neves
相关产品推荐
相关产品推荐

