如何使用broom和dplyr将分组数据应用于分组模型?

5

我希望做的是将mtcars数据集中的mpg(每英里加仑数)转换为gpm(每英里消耗的加仑数),并将其与wt拟合成一个模型。看起来很简单:

data(mtcars)
library(dplyr)
library(tidyr)
library(broom)
library(ggplot2)
library(scales)

mtcars2 <-
    mtcars %>%
    mutate(gpm = 1 / mpg) %>%
    group_by(cyl, am)

lm1 <-
    mtcars2 %>%
    do(fit = lm(gpm ~ wt, data = .))

这使我得到了一个行数据框,有6行,正如预期的那样。

这张图证实了有六个组:

p1 <-
    qplot(wt, gpm, data = mtcars2) +
    facet_grid(cyl ~ am) +
    stat_smooth(method='lm',se=FALSE, fullrange = TRUE) +
    scale_x_continuous(limits = c(0,NA)) 

我可以使用augment()函数来获取拟合后的输出:

lm1 %>% augment(fit)

这给我32行,每一行对应mtcars2中的一行,正如预期的那样。

现在的挑战是:我想使用新数据得到拟合输出,其中我已经将wt增加了cyl/4:

newdata <-
    mtcars2 %>%
    mutate(
        wt = wt + cyl/4)

我希望这将产生一个数据框,其大小与lm1%>%augment(fit)相同:每个newdata中的行都对应一行,因为broom将通过grouping变量cyl和am匹配模型和newdata。
不幸的是,
pred1 <-
    lm1 %>%
    augment(
        fit,
        newdata = newdata)

这将为我提供一个数据框,其中包含192行(= 6 x 32),显然将每个模型适合于newdata的每一行。

从其他地方阅读,我了解到group_by和rowwise数据框不兼容,因此lm1未分组,augment无法关联模型和newdata。是否有另一种设计模式可以让我做到这一点?如果它像上面的尝试那样简单透明,那就太好了,但更重要的是它能工作。

这是我的sessionInfo():

> sessionInfo()
R version 3.3.1 (2016-06-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] scales_0.4.0  ggplot2_2.1.0 broom_0.4.1   tidyr_0.6.0   dplyr_0.5.0  

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.7      magrittr_1.5     mnormt_1.5-4     munsell_0.4.3   
 [5] colorspace_1.2-6 lattice_0.20-34  R6_2.1.3         stringr_1.1.0   
 [9] plyr_1.8.4       tools_3.3.1      parallel_3.3.1   grid_3.3.1      
[13] nlme_3.1-128     gtable_0.2.0     psych_1.6.9      DBI_0.5-1       
[17] lazyeval_0.2.0   assertthat_0.1   tibble_1.2       reshape2_1.4.1  
[21] labeling_0.3     stringi_1.1.1    compiler_3.3.1   foreign_0.8-67  

编辑:

@aosmith:我一直在探索你的第二个选项,我很喜欢它。但当我尝试在我的真实数据上运行它时,在 mutate 命令中出现了一个问题:它返回“错误:augment 不知道如何处理类别为 list 的数据”。

我的实际代码更像是:

newdata %>% 
dplyr::select(cyl, am, wt) %>% # wt holds new predictor values
group_by(cyl, am) %>%
nest() %>%
inner_join(regressions, .) %>% 
## looks like yours at this point
mutate(pred = list(augment(fit, newdata = data))) %>% # Error here
unnest(pred)

当我说它看起来像是你的时候,我的意思是我有以下列(此处重命名以保持一致):ID(chr)、attr1(dbl)、cyl(dbl)、am(chr)、fit(list)和data(list)。你有cyl、am(dbl)、fit和data。我将我的am更改为dbl,但这并没有帮助。
我认为差别在于,在此样本中,我有3(ID…类似于mtcars中的rownames)×2(cyl)×2(am)个单位(每个样本有12个测量值),而mtcars示例有3(cyl)×2(am)个单元格×每个单元格中的随机数量的汽车类型。在我的分析中,我需要查看ID值,但newdata同样适用于所有单位。如果有所帮助,请将其视为施加在测试中每辆汽车上的顺风速度。这是否说明augment不能处理class list数据的原因?
编辑:将ID与newdata(使用full = TRUE)合并解决了最后一个问题。目前我正在使用您提出的第一个解决方案。
1个回答

5

我曾经在这种情况下使用了purrr包中的map2函数。该函数能够同时遍历两个列表中的元素,这两个列表必须长度相同且顺序也必须相同。

列表中的元素将作为你想要应用的一些函数的参数(在你的情况下是augment函数)。您的两个列表将分别是模型列表和数据集列表(每个cyl/am组合一个列表)。

使用map2_df将结果返回为数据框而不是列表。

library(purrr)

我使用split制作了需要预测的数据框列表。因为拆分因子的顺序决定了列表的顺序,所以我确保它与lm1的顺序相同。

test_split = split(newdata, list(newdata$am, newdata$cyl)

map2_df(lm1$fit, test_split, ~augment(.x, newdata = .y))

为了避免过多担心顺序,您可以按组嵌套预测数据,将其加入到“lm1”中,并将“augment”的结果作为列表返回以进行取消嵌套。
newdata %>%
    group_by(cyl, am) %>%
    nest() %>%
    inner_join(lm1, .) %>%
    mutate(pred = list(augment(fit, newdata = data))) %>%
    unnest(pred)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接