如何使用 `sample_n` 将 `dplyr` 中的数据集自动平衡到最小的 class 的大小?

How to balance a dataset in `dplyr` using `sample_n` automatically to the size of the smallest class?

我有一个像这样的数据集:

df <- tibble(
  id = 1:18,
  class = rep(c(rep(1,3),rep(2,2),3),3),
  var_a = rep(c("a","b"),9)
)

# A tibble: 18 x 3
      id cluster var_a
   <int>   <dbl> <chr>
 1     1       1 a    
 2     2       1 b    
 3     3       1 a    
 4     4       2 b    
 5     5       2 a    
 6     6       3 b    
 7     7       1 a    
 8     8       1 b    
 9     9       1 a    
10    10       2 b    
11    11       2 a    
12    12       3 b    
13    13       1 a    
14    14       1 b    
15    15       1 a    
16    16       2 b    
17    17       2 a    
18    18       3 b 

该数据集包含几个 classes 中的许多观察结果。 classes 不平衡。在上面的示例中,我们可以看到,只有 3 个观测值是 class 3,而 class 2 有 6 个观测值,class 1.

有 9 个观测值

现在我想自动平衡该数据集,以便所有 class 的大小相同。所以我想要一个 9 行的数据集,每行 3 行 class。我可以使用 dplyr 中的 sample_n 函数来进行这样的采样。

我通过首先计算最小的 class 大小来做到这一点..

min_length <- as.numeric(df %>% 
  group_by(class) %>% 
  summarise(n = n()) %>% 
  ungroup() %>% 
  summarise(min = min(n)))

..然后应用sample_n函数:

set.seed(1)
df %>% group_by(cluster) %>% sample_n(min_length)

# A tibble: 9 x 3
# Groups:   cluster [3]
     id cluster var_a
  <int>   <dbl> <chr>
1    15       1 a    
2     7       1 a    
3    13       1 a    
4     4       2 b    
5     5       2 a    
6    17       2 a    
7    18       3 b    
8     6       3 b    
9    12       3 b    

我想知道是否可以一次性做到这一点(计算最小 class 尺寸然后采样)?

你可以一步完成,但有点作弊:

set.seed(42)
df %>%
  group_by(class) %>%
  sample_n(min(table(df$class))) %>%
  ungroup()
# # A tibble: 9 x 3
#      id class var_a
#   <int> <dbl> <chr>
# 1     1     1 a    
# 2     8     1 b    
# 3    15     1 a    
# 4     4     2 b    
# 5     5     2 a    
# 6    11     2 a    
# 7    12     3 b    
# 8    18     3 b    
# 9     6     3 b    

我说“作弊”是因为通常您不想从管道中引用 df$。然而,因为他们 属性 我们正在寻找的是整个框架,但是 table 函数一次只能看到一组,所以我们需要 side-step 那一点。

一个人可以做到

df %>%
  mutate(mn = min(table(class))) %>%
  group_by(class) %>%
  sample_n(mn[1]) %>%
  ungroup()
# # A tibble: 9 x 4
#      id class var_a    mn
#   <int> <dbl> <chr> <int>
# 1    14     1 b         3
# 2    13     1 a         3
# 3     7     1 a         3
# 4     4     2 b         3
# 5    16     2 b         3
# 6     5     2 a         3
# 7    12     3 b         3
# 8    18     3 b         3
# 9     6     3 b         3

虽然我不认为那是更多 elegant/readable。