如何根据 R 中因子变量的每个值的不同比例,从数据集中抽取与大小成比例的随机样本

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

我想从我的数据集中抽取一个随机样本,对因子变量的每个值使用不同的比例,并使用存储在其他列中的权重。 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 

但是当我必须choose/sample每个物种的不同比例时,我卡住了,分别说 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

请帮忙

如果我没理解错的话:

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

使用 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

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

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