【问题标题】:How can I draw a random sample from a dataset, proportionate to size, based on different proportions for each value of a factor variable, in R如何在 R 中根据因子变量的每个值的不同比例,从数据集中抽取与大小成比例的随机样本
【发布时间】:2023-04-11 01:12:01
【问题描述】:

我想从我的数据集中抽取一个随机样本,对因子变量的每个值使用不同的比例,以及使用存储在其他列中的权重。 dplyr 管道中的解决方案将是首选,因为它可以很容易地插入到长代码中。

我们以iris 数据集为例。 Species 列分为三个值,每个值 50 行。我们还假设样本权重存储在Sepal.Length 列中。如果我必须对每个物种进行等比例(或等行)的抽样,问题很容易解决

library(tidyverse)

iris %>% group_by(Species) %>% slice_sample(prop = 0.1, weight_by = Sepal.Length)

# A tibble: 15 x 5
# Groups:   Species [3]
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species   
          <dbl>       <dbl>        <dbl>       <dbl> <fct>     
 1          5.4         3.7          1.5         0.2 setosa    
 2          5.3         3.7          1.5         0.2 setosa    
 3          5.7         4.4          1.5         0.4 setosa    
 4          5           3.5          1.6         0.6 setosa    
 5          4.8         3.1          1.6         0.2 setosa    
 6          6.1         2.9          4.7         1.4 versicolor
 7          6.7         3.1          4.7         1.5 versicolor
 8          5           2            3.5         1   versicolor
 9          7           3.2          4.7         1.4 versicolor
10          5.7         2.9          4.2         1.3 versicolor
11          7.2         3.2          6           1.8 virginica 
12          6.7         2.5          5.8         1.8 virginica 
13          6.4         2.8          5.6         2.1 virginica 
14          6.3         3.3          6           2.5 virginica 
15          7.2         3            5.8         1.6 virginica 

但是当我必须为每个物种选择/采样不同的比例时,我遇到了困难,比如分别为 10%、20%、25%。

iris %>% group_by(Species) %>% slice_sample(prop = c(0.1, 0.2, 0.25), weight_by = Sepal.Length)

#Error: `prop` must be a single number

iris %>% group_split(Species) %>% map_df(c(0.1, 0.2, 0.25), ~ slice_sample(prop = ., weight_by = Sepal.Length))
# A tibble: 0 x 0

请帮忙

【问题讨论】:

    标签: r random tidyverse sample


    【解决方案1】:

    如果我没听错的话:

    iris %>% 
      group_split(Species) %>% 
      map2(c(0.1, 0.2, 0.25), ~ slice_sample(.x, prop = .y))
    
    [[1]]
    # A tibble: 5 x 5
      Sepal.Length Sepal.Width Petal.Length Petal.Width Species
             <dbl>       <dbl>        <dbl>       <dbl> <fct>  
    1          4.9         3            1.4         0.2 setosa 
    2          4.8         3            1.4         0.1 setosa 
    3          5.2         4.1          1.5         0.1 setosa 
    4          5           3.5          1.6         0.6 setosa 
    5          5.2         3.5          1.5         0.2 setosa 
    
    [[2]]
    # A tibble: 10 x 5
       Sepal.Length Sepal.Width Petal.Length Petal.Width Species   
              <dbl>       <dbl>        <dbl>       <dbl> <fct>     
     1          6.3         2.5          4.9         1.5 versicolor
     2          5.5         2.6          4.4         1.2 versicolor
     3          6.9         3.1          4.9         1.5 versicolor
     4          6.6         2.9          4.6         1.3 versicolor
     5          6.1         3            4.6         1.4 versicolor
     6          5.7         2.8          4.5         1.3 versicolor
     7          6.7         3.1          4.4         1.4 versicolor
     8          5.1         2.5          3           1.1 versicolor
     9          5.7         3            4.2         1.2 versicolor
    10          7           3.2          4.7         1.4 versicolor
    
    [[3]]
    # A tibble: 12 x 5
       Sepal.Length Sepal.Width Petal.Length Petal.Width Species  
              <dbl>       <dbl>        <dbl>       <dbl> <fct>    
     1          6.4         3.2          5.3         2.3 virginica
     2          7.2         3.2          6           1.8 virginica
     3          6.3         3.3          6           2.5 virginica
     4          6.2         2.8          4.8         1.8 virginica
     5          7.6         3            6.6         2.1 virginica
     6          5.7         2.5          5           2   virginica
     7          4.9         2.5          4.5         1.7 virginica
     8          6.7         3.1          5.6         2.4 virginica
     9          7.7         2.8          6.7         2   virginica
    10          6.7         3.3          5.7         2.5 virginica
    11          6           3            4.8         1.8 virginica
    12          5.6         2.8          4.9         2   virginica
    

    如果要返回数据框,只需将 map2 更改为 map2_df

    iris %>% 
      group_split(Species) %>% 
      map2_df(c(0.1, 0.2, 0.25), ~ slice_sample(.x, prop = .y))
    
    # A tibble: 27 x 5
       Sepal.Length Sepal.Width Petal.Length Petal.Width Species   
              <dbl>       <dbl>        <dbl>       <dbl> <fct>     
     1          5.7         3.8          1.7         0.3 setosa    
     2          4.8         3.1          1.6         0.2 setosa    
     3          5.1         3.8          1.5         0.3 setosa    
     4          4.9         3.6          1.4         0.1 setosa    
     5          4.8         3.4          1.6         0.2 setosa    
     6          5.7         2.8          4.1         1.3 versicolor
     7          6.6         3            4.4         1.4 versicolor
     8          6.8         2.8          4.8         1.4 versicolor
     9          5.8         2.7          4.1         1   versicolor
    10          6.4         3.2          4.5         1.5 versicolor
    # ... with 17 more rows
    

    【讨论】:

    • 是的,像这样,我可以在其中使用 weight_by 吗?
    • 我想你可以添加 weight_by = Species 作为参数,我只是不确定它有什么用途(这是你问题的一部分,我很难理解)。
    • 是的,感谢您的帮助。我正在使用map_df!顺便问一下map2_df和map_df有什么区别?
    • map2() 是当您想要遍历 2 组元素以进入函数的参数而不是仅 1 组时。例如map2(0:5, 1:6, function(x, y) rnorm(5, mean = x, sd = y)) 会做rnorm(5, 0, 1),然后是rnorm(5, 1, 2),等等,直到rnorm(5, 5, 6)
    • 在我之前的评论中,我的意思是说weight_by = Sepal.Length 而不是weight_by = Species。无论哪种方式,我认为这与您的特定问题无关。
    【解决方案2】:

    使用purrr 的类似解决方案。

    首先我们为每个Species指定我们的比例。

    props <- c(setosa=0.1, versicolor=0.2, virginica=0.5)
    

    然后我们使用imap 遍历props 中的每个名称-值对。对于props 中的每一对,我们过滤数据框的行以仅包含该物种,然后对使用slice_sample 指定的相应百分比进行采样。

    imap_dfr(props,
             ~filter(iris, Species==.y) %>%
               slice_sample(prop=.x))
    

    然后使用imap_dfr 将三个数据框(每个物种一个)组合成一个数据框。

    结果如下:

    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
    1           4.8         3.1          1.6         0.2     setosa
    2           5.0         3.5          1.3         0.3     setosa
    3           5.1         3.8          1.6         0.2     setosa
    4           5.0         3.6          1.4         0.2     setosa
    5           4.9         3.1          1.5         0.2     setosa
    6           6.7         3.1          4.7         1.5 versicolor
    7           5.7         2.8          4.1         1.3 versicolor
    8           6.1         3.0          4.6         1.4 versicolor
    9           5.6         3.0          4.5         1.5 versicolor
    10          6.6         2.9          4.6         1.3 versicolor
    11          5.5         2.6          4.4         1.2 versicolor
    12          6.7         3.0          5.0         1.7 versicolor
    13          5.7         2.6          3.5         1.0 versicolor
    14          5.9         3.2          4.8         1.8 versicolor
    15          5.4         3.0          4.5         1.5 versicolor
    16          5.8         2.8          5.1         2.4  virginica
    17          6.7         3.3          5.7         2.1  virginica
    18          7.4         2.8          6.1         1.9  virginica
    19          6.4         2.8          5.6         2.1  virginica
    20          6.7         3.1          5.6         2.4  virginica
    21          6.1         3.0          4.9         1.8  virginica
    22          6.0         2.2          5.0         1.5  virginica
    23          6.3         2.7          4.9         1.8  virginica
    24          6.3         2.8          5.1         1.5  virginica
    25          7.2         3.2          6.0         1.8  virginica
    26          7.7         2.6          6.9         2.3  virginica
    27          5.8         2.7          5.1         1.9  virginica
    28          4.9         2.5          4.5         1.7  virginica
    29          6.7         3.0          5.2         2.3  virginica
    30          7.7         3.8          6.7         2.2  virginica
    31          6.9         3.1          5.4         2.1  virginica
    32          5.8         2.7          5.1         1.9  virginica
    33          6.8         3.0          5.5         2.1  virginica
    34          6.3         2.5          5.0         1.9  virginica
    35          6.9         3.1          5.1         2.3  virginica
    36          6.3         3.3          6.0         2.5  virginica
    37          7.6         3.0          6.6         2.1  virginica
    38          6.5         3.0          5.5         1.8  virginica
    39          7.7         2.8          6.7         2.0  virginica
    40          6.5         3.2          5.1         2.0  virginica
    

    【讨论】:

      【解决方案3】:

      您可以将比例信息保留在数据框本身并从中采样行。

      library(dplyr)
      
      iris %>%
        distinct(Species) %>%
        mutate(prop = c(0.1, 0.2, 0.25)) %>%
        inner_join(iris, by = 'Species') %>%
        group_by(Species) %>%
        sample_n(first(prop)*n()) -> result
      
      result %>% count(Species)
      
      #  Species        n
      #  <fct>      <int>
      #1 setosa         5
      #2 versicolor    10
      #3 virginica     12
      

      我希望slice_sample(prop = first(prop)) 可以工作,但它没有,因此我使用了sample_n

      【讨论】:

      • 非常好的方法,一如既往的 Ronak。谢谢。只是想知道我可以在管道语法中设置种子还是必须在开头设置它?
      • 将其设置在开头本身应该会给您相同的样本。
      猜你喜欢
      • 2012-05-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-28
      • 1970-01-01
      相关资源
      最近更新 更多