什么是 **tidyverse** 方法用于按多列分割数据框?

10
我想按照多列对数据框进行拆分,以便查看每个子集的summary()输出。以下是使用base中的split()实现此操作的方法:
library(tidyverse)
#> Loading tidyverse: ggplot2
#> Loading tidyverse: tibble
#> Loading tidyverse: tidyr
#> Loading tidyverse: readr
#> Loading tidyverse: purrr
#> Loading tidyverse: dplyr
#> Conflicts with tidy packages ----------------------------------------------
#> filter(): dplyr, stats
#> lag():    dplyr, stats

mtcars %>% 
  select(1:3) %>% 
  mutate(GRP_A = sample(LETTERS[1:2], n(), replace = TRUE),
         GRP_B = sample(c(1:2), n(), replace = TRUE)) %>% 
  split(list(.$GRP_A, .$GRP_B)) %>% 
  map(summary)
#> $A.1
#>       mpg             cyl           disp          GRP_A          
#>  Min.   :10.40   Min.   :4.0   Min.   :108.0   Length:10         
#>  1st Qu.:14.97   1st Qu.:4.5   1st Qu.:151.9   Class :character  
#>  Median :18.50   Median :7.0   Median :259.3   Mode  :character  
#>  Mean   :17.61   Mean   :6.4   Mean   :283.4                     
#>  3rd Qu.:20.85   3rd Qu.:8.0   3rd Qu.:430.0                     
#>  Max.   :24.40   Max.   :8.0   Max.   :472.0                     
#>      GRP_B  
#>  Min.   :1  
#>  1st Qu.:1  
#>  Median :1  
#>  Mean   :1  
#>  3rd Qu.:1  
#>  Max.   :1  
#> 
#> $B.1
#>       mpg             cyl           disp          GRP_A          
#>  Min.   :15.00   Min.   :4.0   Min.   : 75.7   Length:5          
#>  1st Qu.:21.00   1st Qu.:4.0   1st Qu.: 78.7   Class :character  
#>  Median :21.50   Median :4.0   Median :120.1   Mode  :character  
#>  Mean   :24.06   Mean   :5.2   Mean   :147.1                     
#>  3rd Qu.:30.40   3rd Qu.:6.0   3rd Qu.:160.0                     
#>  Max.   :32.40   Max.   :8.0   Max.   :301.0                     
#>      GRP_B  
#>  Min.   :1  
#>  1st Qu.:1  
#>  Median :1  
#>  Mean   :1  
#>  3rd Qu.:1  
#>  Max.   :1  
#> 
#> $A.2
#>       mpg             cyl             disp          GRP_A          
#>  Min.   :15.20   Min.   :4.000   Min.   : 95.1   Length:9          
#>  1st Qu.:16.40   1st Qu.:6.000   1st Qu.:160.0   Class :character  
#>  Median :18.10   Median :8.000   Median :275.8   Mode  :character  
#>  Mean   :19.84   Mean   :6.667   Mean   :234.0                     
#>  3rd Qu.:21.00   3rd Qu.:8.000   3rd Qu.:275.8                     
#>  Max.   :30.40   Max.   :8.000   Max.   :360.0                     
#>      GRP_B  
#>  Min.   :2  
#>  1st Qu.:2  
#>  Median :2  
#>  Mean   :2  
#>  3rd Qu.:2  
#>  Max.   :2  
#> 
#> $B.2
#>       mpg             cyl         disp          GRP_A          
#>  Min.   :13.30   Min.   :4   Min.   : 71.1   Length:8          
#>  1st Qu.:14.97   1st Qu.:4   1st Qu.:125.3   Class :character  
#>  Median :20.55   Median :6   Median :201.5   Mode  :character  
#>  Mean   :20.99   Mean   :6   Mean   :213.5                     
#>  3rd Qu.:23.93   3rd Qu.:8   3rd Qu.:315.5                     
#>  Max.   :33.90   Max.   :8   Max.   :360.0                     
#>      GRP_B  
#>  Min.   :2  
#>  1st Qu.:2  
#>  Median :2  
#>  Mean   :2  
#>  3rd Qu.:2  
#>  Max.   :2

我该如何使用tidyverse中的动词来达到相同的结果呢?我最初的想法是使用purrr::by_slice(),但显然已被弃用。

你不能使用split的原因是什么?你是想要分割还是group_by也可以? - be_green
1
我尽量避免混合使用R的“方言”,所以.$GRP_A不符合我的口味。group_by也不行——它返回一个分组的数据框,但是summary()无法识别这些组。 - Tiernan
既然我已经打出来了,我对于“整洁宇宙”方言的偏好可能是不必要的挑剔……但如果我有选择,我会选择tidyverse动词而不是split,所以我想看看是否有我忽略的东西。 - Tiernan
2
你可以使用 do(s=summary(.)) - HubertL
@HubertL 这是一个不错的方法!你想提交答案还是我来? - Tiernan
@Tiernan 好的,请继续。谢谢。 - HubertL
2个回答

7

dplyr 0.8.0引入了你要寻找的动词:group_split()

从文档可以得知:

group_split() 的用法类似于 base::split(),但是:

  • 它使用 group_by() 中的分组结构,因此受到数据掩码的影响。

  • 它不会根据分组命名列表元素,因为这通常会丢失信息并令人困惑。

group_keys() 通过返回一个数据框来解释分组结构,该数据框每组一行,每个组变量一列。

对于你的例子:

mtcars %>% 
  select(1:3) %>% 
  mutate(GRP_A = sample(LETTERS[1:2], n(), replace = TRUE),
         GRP_B = sample(c(1:2), n(), replace = TRUE)) %>% 
  group_split(GRP_A, GRP_B) %>% 
  map(summary)

4

编辑:此答案现已过时。请参见@MartijnVanAttekum的解决方案


根据Hadley的说法,"整洁"的解决方案似乎是"mutate + list-cols + purrr"的组合


library(tidyverse) 
library(magrittr) 

# group, nest, create a new col leveraging purrr::map()
mt_summary <- 
    mtcars %>% 
    select(1:3) %>% 
    mutate(GRP_A = sample(LETTERS[1:2],  n(), replace = TRUE), 
           GRP_B = sample(c(1:2), n(), replace = TRUE)) %>% 
    group_by(GRP_A, GRP_B) %>% 
    nest() %>% 
    mutate(SUMMARY = map(data, .f = summary))

# check the structure
mt_summary
#> # A tibble: 4 × 4
#>   GRP_A GRP_B              data     SUMMARY
#>   <chr> <int>            <list>      <list>
#> 1     A     1 <tibble [11 × 3]> <S3: table>
#> 2     B     2  <tibble [9 × 3]> <S3: table>
#> 3     A     2  <tibble [7 × 3]> <S3: table>
#> 4     B     1  <tibble [5 × 3]> <S3: table>

# extract the summaries
extract2(mt_summary, "SUMMARY") %>% 
    set_names(paste0(extract2(mt_summary, "GRP_A"), 
                     extract2(mt_summary, "GRP_B")))
#> $A1
#>       mpg             cyl             disp      
#>  Min.   :10.40   Min.   :4.000   Min.   : 75.7  
#>  1st Qu.:15.25   1st Qu.:4.000   1st Qu.:120.9  
#>  Median :19.20   Median :6.000   Median :167.6  
#>  Mean   :20.43   Mean   :6.182   Mean   :229.0  
#>  3rd Qu.:25.85   3rd Qu.:8.000   3rd Qu.:309.5  
#>  Max.   :30.40   Max.   :8.000   Max.   :460.0  
#> 
#> $B2
#>       mpg             cyl             disp      
#>  Min.   :15.20   Min.   :4.000   Min.   : 78.7  
#>  1st Qu.:17.80   1st Qu.:4.000   1st Qu.:120.3  
#>  Median :19.20   Median :6.000   Median :167.6  
#>  Mean   :20.84   Mean   :6.222   Mean   :225.9  
#>  3rd Qu.:21.50   3rd Qu.:8.000   3rd Qu.:351.0  
#>  Max.   :32.40   Max.   :8.000   Max.   :400.0  
#> 
#> $A2
#>       mpg             cyl             disp      
#>  Min.   :15.20   Min.   :4.000   Min.   : 71.1  
#>  1st Qu.:18.90   1st Qu.:4.000   1st Qu.:114.5  
#>  Median :21.40   Median :6.000   Median :145.0  
#>  Mean   :21.79   Mean   :5.429   Mean   :176.0  
#>  3rd Qu.:22.10   3rd Qu.:6.000   3rd Qu.:241.5  
#>  Max.   :33.90   Max.   :8.000   Max.   :304.0  
#> 
#> $B1
#>       mpg             cyl           disp      
#>  Min.   :10.40   Min.   :4.0   Min.   :140.8  
#>  1st Qu.:13.30   1st Qu.:8.0   1st Qu.:275.8  
#>  Median :14.30   Median :8.0   Median :350.0  
#>  Mean   :15.62   Mean   :7.2   Mean   :319.7  
#>  3rd Qu.:17.30   3rd Qu.:8.0   3rd Qu.:360.0  
#>  Max.   :22.80   Max.   :8.0   Max.   :472.0

似乎其他人已经主张保存 by_slice:https://github.com/hadley/purrr/issues/270 - Tiernan
你可以在分组后立即使用dmap。你会得到一个数据框,而不是列表,并且会失去行名称。 - FlorianGD
@FlorianGD 我尝试用 dmap(.f = summary) 替换 do 行,但没有得到我想要的结果... 而且我还收到了一个警告,说 dmap 将很快被弃用。 - Tiernan
1
问题已发布到 dplyr 存储库:https://github.com/hadley/dplyr/issues/2518 - Tiernan
1
发布到 manipulatr 邮件列表 的问题:https://groups.google.com/d/msg/manipulatr/EMARFJfSvwY/BjXiY1kVDgAJ - Tiernan
显示剩余2条评论

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