使用管道语法处理模型列表

working with lists of models using the pipe syntax

我经常喜欢拟合和检查多个模型,这些模型与 R 数据框中的两个变量相关。

我可以使用这样的语法来做到这一点:

require(tidyverse)
require(broom)
models <- list(hp ~ exp(cyl), hp ~ cyl)
map_df(models, ~tidy(lm(data=mtcars, formula=.x)))

但我已经习惯了管道语法,并希望能够做到这样的事情:

mtcars %>% map_df(models, ~tidy(lm(data=., formula=.x)))

这清楚地表明我是 "starting" 和 mtcars,然后对其进行处理以生成我的输出。但是那个语法不起作用,报错 Error: Index 1 must have length 1.

有没有一种方法可以编写我的 purrr:map() 函数,使我可以通过管道将 mtcars 传递给它以获得与上面的工作代码相同的输出? IE。

mtcars %>% <<<something>>>

这与 purrr::map 的工作方式有点不一致。您正在映射模型列表(一次是列表的一项),而不是数据框(一次是数据框的一列)。因为即使使用其他模型表达式,数据帧也保持不变,所以我认为映射不适用于这种情况。

但是,您可以根据上面的函数定义自定义函数来获得所需的语法。

library(tidyverse)
library(broom)
models <- list(hp ~ exp(cyl), hp ~ cyl)

models_to_rows <- function(data, models_list) {
  models_list %>%
    map_df(~tidy(lm(data=data, formula=.x)))
}

mtcars %>%
  models_to_rows(models)
#>          term     estimate    std.error statistic      p.value
#> 1 (Intercept)  89.60052274  9.702303069  9.234975 2.823542e-10
#> 2    exp(cyl)   0.04045315  0.004897717  8.259594 3.212750e-09
#> 3 (Intercept) -51.05436157 24.981944312 -2.043650 4.985522e-02
#> 4         cyl  31.95828066  3.883803355  8.228604 3.477861e-09

tl/dr: mtcars %>% {map_df(models, function(.x) tidy(lm(data=., formula=.x)))}

mtcars %>% map_df(models, ~tidy(lm(..1,..2)), ..2 = .)


您尝试的解决方案有 2 个问题。

首先,如果你想把点放在不寻常的地方,你需要使用花括号。

library(magrittr)
1 %>% divide_by(2)   # 0.5     -> this works
1 %>% divide_by(2,.) # 2       -> this works as well
1 %>% divide_by(2,mean(.,3))   #  this doesn't    
1 %>% divide_by(.,2,mean(.,3)) #  as it's equivalent to this one
1 %>% {divide_by(2,mean(.,3))} #  but this one works as it forces all dots to be explicit.

第二个是你不能按照你想要的方式使用带有 ~ 公式的点,尝试 map(c(1,2), ~ 3+.)map(c(1,2), ~ 3+.x)(甚至 map(c(1,2), ~ 3+..1) ),你会看到你得到了相同的结果。当您在 ~ 公式中使用点时,它不再链接到管道函数。

要确保点被解释为 mtcars,您需要使用良好的旧 function(x) ... 定义。

这个有效:

mtcars %>% {map_df(models, function(.x) tidy(lm(data=., formula=.x)))}

最后,作为奖励,这是我想出的,试图找到一个没有花括号的解决方案:

mtcars %>% map(models,lm,.) %>% map_df(tidy)
mtcars %>% map_df(models, ~tidy(lm(..1,..2)), ..2 = .)

这应该可行,不涉及 function{} 的复杂性。纯粹的 purrr 解决方案。

library(tidyverse)
library(broom)

models <- list(hp ~ exp(cyl), hp ~ cyl)

mtcars %>% 
    list %>%                         # make it a list
    cross2(models) %>%               # get combinations
    transpose %>%                    # put into a nice format
    set_names("data", "formula") %>% # set names to lm arg names
    pmap(lm) %>%                     # fit models
    map_df(tidy)                     # tidy it up