r - 在 R 中保存循环的输出(到数据帧中)
问题描述
我有一个循环遍历公式列表(其中包括我的输入数据的不同变量)并在我的数据上运行 adonis 函数。
此循环输出一个数据框,其中包含公式中每个变量的 R 平方和 p 值,由 adonis 在 PERMANOVA 分析中计算得出。
如何保存每次迭代生成的数据框(即每个公式?)
示例数据:
sampleID <- c("ID1", "ID2", "ID3", "ID4", "ID5")
variables <- c("stage", "type", "treatment")
n <- length(variables)
id <- unlist(lapply(1:n, function(i)combn(1:n,i,simplify=FALSE)),recursive=FALSE)
formulas <- sapply(id,function(i) paste("data~",paste(variables[i],collapse="+")))
stage <- c(1,2,2,3,3)
type <- c("cancer", "dysplasia", "dysplasia", "cancer", "cancer")
treatment <- c("yes", "no", "yes", "no", "no")
metadata <- data.frame(cbind(sampleID, stage, type, treatment))
data <- data.frame(sampleID, X = sample(1:5), Y = sample(1:5), Z = sample(1:5), A = sample(1:5))
data <- data.matrix(data)
这是循环:
library(vegan)
adonis_test <- for (i in 1:length(formulas)){
z = adonis(as.formula(formulas[i]) , data=metadata)
return(data.frame(name = rownames(z$aov.tab), R2 = z$aov.tab$R2, 'Pr(>F)' = z$aov.tab$'Pr(>F)')[1,])}
解决方案
for
循环什么也不返回。- 迭代地将行添加到
data.frame
一个或两个左右的作品中,但通常它的扩展性很差;请参阅The R Inferno中的“第 2 章:不断增长的对象”,了解一些有趣/轻松的阅读内容。长话短说:添加的每一行都要求将内存中的所有行复制到一个新对象中。这意味着当您有 500 行时,添加 1 行会导致需要将这 500 行(现在是内存中的 1000 行)复制到新对象。这对于小帧来说已经足够快了,但它的渐近增长是可怕的。
一般来说,最好将结果放入 alist
中(这不涉及每次向其附加一个东西时复制所有数据),然后rbind
一次将它们全部 -ing。
一般来说,我建议使用lapply
这个,也许像
res <- lapply(seq_along(formulas), function(i) {
z = adonis(as.formula(formulas[i]) , data=metadata)
data.frame(
name = rownames(z$aov.tab), R2 = z$aov.tab$R2,
'Pr(>F)' = z$aov.tab$'Pr(>F)'
)[1,]
})
resDF <- do.call(rbind, res)
如果您真的更喜欢for
循环(并且有一些优点),那么
res <- list()
for (i in seq_along(formulas)) {
z = adonis(as.formula(formulas[i]) , data=metadata)
res[[i]] <- data.frame(
name = rownames(z$aov.tab), R2 = z$aov.tab$R2,
'Pr(>F)' = z$aov.tab$'Pr(>F)'
)[1,]
}
resDF <- do.call(rbind, res)
for
此处使用循环代替 的一个优点lapply
:如果一个或多个迭代可能失败,则lapply
将失败并且不返回任何内容,而for
循环实现会将所有先前成功完成的模型存储在res
. 当然,如果你想继续,你需要手动修改for
循环索引以从你离开的地方继续(可选地跳过问题)。
但是……如果您希望发生这种情况,那么tryCatch
您的朋友就是您,即使lapply
实施也不会因一次失败而失去所有进展。做好这件事需要的不仅仅是这个问答主题,但要意识到有一些方法。
旁注:在编程中更具防御性,请使用seq_along(formulas)
(or seq_len(length(formulas))
) 而不是1:length(.)
. 为什么?以自动方式,如果formulas
为空,则为length(.)
0,并1:0
返回长度为 2 的向量,如c(1, 0)
. 由于for
循环的目的是仅在需要时执行,因此这是一个逻辑流错误,并且几乎肯定会Error:
(或至少破坏或不必要地处理数据)。另一方面,seq_along(formulas)
并seq_len(length(.))
解析到seq_len(0)
which ... 返回integer(0)
,并且for
循环根本不会迭代。
推荐阅读
- laravel - laravel 在迁移中设置 postgresql 标识列
- windows - 如何不显示 UI?
- reactjs - 如何在使用钩子同时更新 2 个状态时添加回调?
- c# - 如何使用 MailKit/MimeKit 将附件从一封电子邮件移动到另一封电子邮件?
- javascript - NPM 和 YARN 在同一个项目中
- python - Django 上的 AnyMail 和 MailGun 配置
- amadeus - 按需航班状态 | 登机口信息不起作用
- flutter - flutter:: 有没有能以条形形式显示这样的分贝信息的包?
- spring - Spring WebFlux - 如何捕获冲突异常
- javascript - 如何在带有 Chrome 调试的 VS Code 中启用文件的可编辑调试?