【问题标题】:Sample from a data frame using group-specific sample sizes使用特定于组的样本大小从数据框中采样
【发布时间】:2021-06-03 03:58:52
【问题描述】:

我想使用每个组中不相等的样本大小从数据框中抽取行。

假设我们有一个按“组”分组的简单数据框:

library(dplyr)
set.seed(123)

df <- data.frame(group = rep(c("A", "B"), each = 10), 
                 value = rnorm(10))
df
#>    group       value
#> 1      A -0.56047565
#> 2      A -0.23017749
#> .....
#> 10     A -0.44566197
#> 11     B -0.56047565
#> 12     B -0.23017749
#> .....
#> 20     B -0.44566197

使用dplyr 包中的slice_sample 函数,您可以轻松地从该数据帧中分割相等 大小的组:

df %>% group_by(group) %>% slice_sample(n = 2) %>% ungroup()

#> # A tibble: 4 x 2
#>   group  value
#>   <fct>  <dbl>
#> 1 A     -0.687
#> 2 A     -0.446
#> 3 B     -0.687
#> 4 B      1.56

问题

您如何从每个组(大小不相等的切片组)中采样不同个值?例如,从 A 组中采样 4 行,从 B 组中采样 5 行?

【问题讨论】:

    标签: r dplyr sample


    【解决方案1】:

    我能想到的最简单的事情是使用purrrmap2 解决方案。

    library(dplyr)
    library(purrr)
    
    df %>% 
      group_split(group) %>% 
      map2_dfr(c(4, 5), ~ slice_sample(.x, n = .y))
    
    # A tibble: 9 x 2
      group   value
      <chr>   <dbl>
    1 A     -0.687 
    2 A      1.56  
    3 A      0.0705
    4 A      1.72  
    5 B     -0.560 
    6 B      0.461 
    7 B      0.129 
    8 B      0.0705
    9 B     -0.230 
    

    需要注意的是,您需要了解拆分的顺序。我认为group_split() 会将组作为因素进行排序。一种解决方法是像这样适应,并从命名向量中查找n

    group_slice_n <- c(A = 4, B = 5)
    
    df %>% 
      split(.$group) %>% 
      imap_dfr(~ slice_sample(.x, n = group_slice_n[.y]))
    

    【讨论】:

      【解决方案2】:

      试试这个:

      group_sizes <- tibble(group = c("A", "B"), size = c(4, 5))
      set.seed(2021)
      df %>%
        left_join(group_sizes, by = "group") %>%
        group_by(group) %>%
        mutate(samp = sample(n())) %>%
        filter(samp <= size) %>%
        ungroup()
      # # A tibble: 9 x 4
      #   group   value  size  samp
      #   <chr>   <dbl> <dbl> <int>
      # 1 A      0.0705     4     2
      # 2 A      0.129      4     4
      # 3 A     -0.687      4     1
      # 4 A     -0.446      4     3
      # 5 B     -0.560      5     5
      # 6 B      1.56       5     1
      # 7 B      0.129      5     4
      # 8 B      1.72       5     3
      # 9 B     -1.27       5     2
      

      【讨论】:

        【解决方案3】:

        您可以使用我的“splitstackshape”包中的stratified 函数:

        > library(splitstackshape)
        > stratified(df, "group", c(A = 4, B = 5))
           group      value
        1:     A -0.6868529
        2:     A  0.4609162
        3:     A  1.7150650
        4:     A -0.4456620
        5:     B  0.4609162
        6:     B -0.4456620
        7:     B  0.1292877
        8:     B -1.2650612
        9:     B -0.2301775
        

        【讨论】:

        • 分层看起来很简洁
        【解决方案4】:

        一种data.table 方法,使用mapply 在向量中循环使用样本大小的列表元素(列表长度!)

        library( data.table )
        setDT(df) #make it a data.table
        L <- split( df, by = "group" ) #split to a list by group
        #function
        mysamples <- function( dt, samplesize ) {
          dt[ sample( 1:nrow(dt), samplesize), ]
        }
        #mapply
        mapply( mysamples, L, samplesize = c(4,5), SIMPLIFY = FALSE )
        
        #output
        # $A
        # group      value
        # 1:     A -0.6868529
        # 2:     A -0.4456620
        # 3:     A -0.5604756
        # 4:     A  0.1292877
        # 
        # $B
        # group      value
        # 1:     B  1.5587083
        # 2:     B -1.2650612
        # 3:     B -0.2301775
        # 4:     B  0.4609162
        # 5:     B -0.6868529
        

        【讨论】:

          【解决方案5】:
          set.seed(123)
          library(tidyverse)
          
          map2_df(unique(df$group), c(4,5),
                  ~df %>% 
                    filter(group == .x) %>% 
                    slice_sample(n = .y))
          
            group      value
          1     A -0.3724388
          2     A -0.4168576
          3     A  0.5629895
          4     A -1.2601552
          5     B  1.0527115
          6     B -0.3745809
          7     B  0.9769734
          8     B -0.4168576
          9     B -1.0491770
          

          如果您的数据尚未整理,您可以使用以下方式:

          map2_df(unique(sort(df$group)), c(4,5),
                  ~df %>% arrange(group) %>% 
                    filter(group == .x) %>%
                    slice_sample(n = .y))
          

          【讨论】:

          • 如果分组顺序不受控制,这个如何将“4”锁定到A
          • map2_df(unique(sort(df$group)), c(4,5), ~df %&gt;% arrange(group) %&gt;% filter(group == .x) %&gt;% slice_sample(n = .y))
          • 但是,是的,我确实同意@Adam 的解决方案更短/更可取
          • (不要告诉,这属于答案。@Adam 的第一部分也遇到了同样的问题......似乎在更新中得到了修复。 )
          【解决方案6】:

          基于联接的另一种data.table 可能性。

          将特定组的样本大小放入“查找表”(此处为列表,.(...));加入“组”上的原始数据(on = .(group));对于i (by = .EACHI) 中的每场比赛,从大小 = size[1] 的“值”中选择一个样本

          setDT(df)[.(group = c("A", "B"), size = c(4, 5)), on = .(group), sample(value, size[1]),
                   by = .EACHI]
          
          #    group         V1
          # 1:     A -0.6868529
          # 2:     A -0.4456620
          # 3:     A -0.5604756
          # 4:     A  0.1292877
          # 5:     B  1.5587083
          # 6:     B -1.2650612
          # 7:     B -0.2301775
          # 8:     B  0.4609162
          # 9:     B -0.6868529
          

          【讨论】:

            猜你喜欢
            • 2019-03-19
            • 1970-01-01
            • 2019-05-31
            • 1970-01-01
            • 2020-05-11
            • 2020-06-10
            • 2021-08-08
            • 2014-03-23
            • 2023-03-23
            相关资源
            最近更新 更多