R 从列表中提取输出并保存在数据框中

R 从列表中提取输出并保存在数据框中,r,R,我正在做一些建模实验,我需要以特定的格式展示多个模型的输出,以供进一步分析 下面是一些生成多个模型的代码: # This to generate the data resp <- sample(0:1,100,TRUE) x1 <- c(rep(5,20),rep(0,15), rep(2.5,40),rep(17,25)) x2 <- c(rep(23,10),rep(5,10), rep(15,40),rep(1,25), rep(2, 15)) x3 <- c(re

我正在做一些建模实验,我需要以特定的格式展示多个模型的输出,以供进一步分析

下面是一些生成多个模型的代码:

# This to generate the data
resp <- sample(0:1,100,TRUE)
x1 <- c(rep(5,20),rep(0,15), rep(2.5,40),rep(17,25))
x2 <- c(rep(23,10),rep(5,10), rep(15,40),rep(1,25), rep(2, 15))
x3 <- c(rep(2,10),rep(50,10), rep(1,40),rep(112,25), rep(22, 15))
dat <- data.frame(resp,x1, x2, x3)


# This to build multiple models
InitLOogModel<-list()
n <- 3
for (i in 1:n)
{
  ### Create training and testing data
  ## 80% of the sample size
  # Note that I didn't use seed so that random split is performed every iteration.
  smp_sizelogis <- floor(0.8 * nrow(dat))

  train_indlogis <- sample(seq_len(nrow(dat)), size = smp_sizelogis)

  trainlogis <- dat[train_indlogis, ]
  testlogis  <- dat[-train_indlogis, ]

  InitLOogModel[[i]] <- glm(resp ~ ., data =trainlogis, family=binomial)
}
注意,这里的输出是一个列表。现在,这是我需要创建为数据帧的输出(我们称之为outDF):

请注意,outDF中每列内的数字只是回归系数。这是如何为模型1获取它们的方法,例如:

as.data.frame(coef(summary(InitLOogModel[[1]]))[,1])

您可以使用
sapply
循环浏览模型列表并获取所需的摘要信息:

as.data.frame(t(sapply(InitLOogModel, function(x) coef(summary(x))[,1])))
#   (Intercept)         x1          x2            x3
# 1   0.5047799 0.01932560 -0.01268125 -0.0041356214
# 2  -1.2712605 0.11281741  0.06717180  0.0050441023
# 3  -0.7052121 0.08568746  0.03964437  0.0003167443

sapply
在本例中,为每个模型创建一列系数。因为我们希望模型是行而不是列,所以我们使用
t
来转置结果。

sapply方法在@josliber的回答中是合理的,但我倾向于将结果保留在一个列表中,并在后面将其合并。原则是,你认为
sapply
所做的简化只是一种方便——如果不方便,就不要使用它。只要以适合您具体情况的任何方式将结果结合起来即可。这一原则导致以下情况:

do.call(rbind, lapply( InitLOogModel, coef))
我知道
coef.lm
返回一个向量,因为我知道每个模型都有相同的系数,所以我知道对它们进行
rbind
是有意义的。请注意,我避免使用每个模型的
摘要
,因为这不会产生我们想要实现的结果所需的任何东西

当然,
do.call(rbind…
返回一个矩阵,而不是data.frame。如果需要data.frame,则可以使用
as.data.frame

do.call(rbind, lapply( InitLOogModel, coef))
编辑: 受@jake kaupp的回答启发,我将如何在
tidyverse

系数的组合看起来与上述基本R方法非常相似:

library(tidyverse)
map(InitLOogModel, coef) %>%
  reduce(rbind)
用于构建模型列表的for循环可以替换为

library(modelr)

smp_sizelogis <- floor(0.8 * nrow(dat))
rows <- seq_len(nrow(dat))

rerun(3, dat %>%
         resample(sample(rows, size = smp_sizelogis))) %>%
  map(function(x) glm(resp ~ ., family = binomial, data = x))
库(modeler)
smp_sizelogis%
图(函数(x)glm(分别,族=二项式,数据=x))
把整件事放在一起会让我们

smp_sizelogis <- floor(0.8 * nrow(dat))
rows <- seq_len(nrow(dat))

rerun(3, dat %>%
         resample(sample(rows, size = smp_sizelogis))) %>%
  map(function(x) glm(resp ~ ., family = binomial, data = x)) %>%
  map(coef) %>%
  reduce(rbind)
smp\u sizelogis%
map(函数(x)glm(resp~,family=二项式,data=x))%>%
map(coef)%>%
减少(rbind)

与@jake kaupp的答案相比,它的主要优势在于:a)我们不计算我们不需要的东西,b)我们从不将结果填入data.frame,因此我们不必考虑如何将我们想要的东西取回

您还可以使用
tidyverse
解决方案,我个人认为它可以生成更易于阅读的代码,但代价是使用更多的软件包

编辑:尽管@Ista对嵌套列表框架方法看似复杂的看法可能是正确的,但它的吸引力在于保持从数据到模型再到模型细节的分析的完整步骤。这种方法不计算任何额外的内容,只是简单地将数据处理为所需的结果

我也更喜欢将内容保存在数据帧列表中,因为我发现它们使下游工作更容易访问。它可以归结为方法中的首选项以及它与您的工作流的匹配程度

library(tidyverse)

smp_sizelogis <- floor(0.8 * nrow(dat))
rows <- seq_len(nrow(dat))

analysis <- rerun(3, resample(dat, sample(rows, size = smp_sizelogis))) %>%
  tibble(data = .) %>% 
  add_rownames("model_number") %>% 
  mutate(model = map(data, ~glm('resp ~ .', family = binomial, data = .))) %>% 
  mutate(coefs = map(model, tidy))

analysis %>% 
  select(model_number, term, estimate) %>% 
  spread(term, estimate) %>% 
  select(-`(Intercept)`)

# A tibble: 3 × 4
  model_number          x1         x2          x3
*        <chr>       <dbl>      <dbl>       <dbl>
1            1 -0.08160034 0.03156254 0.008613346
2            2 -0.04740939 0.04084883 0.004282003
3            3 -0.05980735 0.01625652 0.002075468
库(tidyverse)
smp_sizelogis%
突变(模型=映射(数据,~glm('resp~。,家族=二项式,数据=)))%>%
变异(coefs=map(model,tidy))
分析%>%
选择(型号、术语、估计值)%>%
价差(期限、估计值)%>%
选择(-`(截取)`)
#一个tibble:3×4
型号x1 x2 x3
*                            
1            1 -0.08160034 0.03156254 0.008613346
2            2 -0.04740939 0.04084883 0.004282003
3            3 -0.05980735 0.01625652 0.002075468

do.call(rbind,lappy(InitLOogModel,coef))
应该这样做(未经测试)。我强烈建议
broom
获得好的数据帧模型系数等。示例逻辑错误,这似乎比需要的复杂。修复了采样,使用
modeler
可能会使事情变得太复杂。把这种方法称为复杂,然后在你的答案中使用它不是有点笨拙吗?所谓复杂,我的意思是
变异(coefs=map(model,tidy))%%>%select(model_number,term,estimate)%%>%spread(term,estimate)%%>%select(
(Intercept)
映射(InitLOogModel,coef)%%>%reduce(rbind)
复杂得多。除了使用tidyverse(尽管我看到你将我的答案中的代码直接复制到了你的答案中),我没有使用你的答案中的任何内容。更多步骤,不一定更复杂。它只是强调了一种方法,即将分析的所有部分放在一个
tibble
中,然后在需要时访问所需内容,而不是运行一次性管道。此外,使用通用的
modeler
函数和OP的变量会产生通用的代码片段,如果您觉得有误,我向您道歉。我们必须同意在相对复杂性方面存在分歧。无论如何,我为关于复制代码的讽刺评论道歉——我是在回应你的说法,我批评了你的方法,然后在我的回答中使用了你的方法。我的意思是我没有使用你的方法。OP请求了一个数据帧,我看不出我的方法如何计算出任何不需要的东西。@jake kaupp
broom::tidy
计算术语估计标准误差统计p.value,要求你
选择你真正想要的东西
coef
只提供所需的结果,因此您可以跳过一步。它实际上调用
coef
来提取结果并在数据帧中返回它们。额外的步骤只是为了满足OP的要求。
smp_sizelogis <- floor(0.8 * nrow(dat))
rows <- seq_len(nrow(dat))

rerun(3, dat %>%
         resample(sample(rows, size = smp_sizelogis))) %>%
  map(function(x) glm(resp ~ ., family = binomial, data = x)) %>%
  map(coef) %>%
  reduce(rbind)
library(tidyverse)

smp_sizelogis <- floor(0.8 * nrow(dat))
rows <- seq_len(nrow(dat))

analysis <- rerun(3, resample(dat, sample(rows, size = smp_sizelogis))) %>%
  tibble(data = .) %>% 
  add_rownames("model_number") %>% 
  mutate(model = map(data, ~glm('resp ~ .', family = binomial, data = .))) %>% 
  mutate(coefs = map(model, tidy))

analysis %>% 
  select(model_number, term, estimate) %>% 
  spread(term, estimate) %>% 
  select(-`(Intercept)`)

# A tibble: 3 × 4
  model_number          x1         x2          x3
*        <chr>       <dbl>      <dbl>       <dbl>
1            1 -0.08160034 0.03156254 0.008613346
2            2 -0.04740939 0.04084883 0.004282003
3            3 -0.05980735 0.01625652 0.002075468