working with lists of models using the pipe syntax

≯℡__Kan透↙ 提交于 2019-12-10 14:47:37

问题


I often like to fit and examine multiple models that relate two variables in an R dataframe.

I can do that using syntax like this:

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

But I'm used to the pipe syntax and was hoping to be able to something like this:

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

That makes it clear that I'm "starting" with mtcars and then doing stuff to it to generate my output. But that syntax doesn't work, giving an error Error: Index 1 must have length 1.

Is there a way to write my purrr:map() function in a way that I can pipe mtcars into it to get the same output as the working code above? I.e.

mtcars %>% <<<something>>>

回答1:


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

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


There are 2 problems with the solution you've tried.

The first is that you need to use curly braces if you want to place the dot in an unusual place.

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.

The second is that you can't use the dot with the ~ formulation in the way you intended, try map(c(1,2), ~ 3+.) and map(c(1,2), ~ 3+.x) (or even map(c(1,2), ~ 3+..1)) and you'll see you get the same result. By the time you use the dot in a ~ formula it's not linked to the pipe function anymore.

To make sure the dot is interpreted as mtcars you need to use the good old function(x) ... definition.

This works:

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

Finally, as a bonus, here's what I came up with, trying to find a solution without curly braces :

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



回答2:


This should work and does not involve the complexity of functions or {}. A purely purrr solution.

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



回答3:


This is a little at odds with how purrr::map works. You are mapping over the list of models (one item of the list at a time), not the dataframe (which would be one column of the dataframe at a time). Because the dataframe stays constant even with other model expressions, I don't think mapping will work for this situation.

However, you could get the syntax you want from defining a custom function based on the one you have above.

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


来源:https://stackoverflow.com/questions/48587951/working-with-lists-of-models-using-the-pipe-syntax

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!