如何使用“ sample_n”自动平衡“ dplyr”中的数据集与最小类的大小?

问题描述

我有一个数据集,例如:

df <- tibble(
  id = 1:18,class = rep(c(rep(1,3),rep(2,2),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 

该数据集包含多个类别的多个观测值。班级不平衡。在上面的样本中,我们可以看到,只有3个观测属于3类,而有2个观测属于6类,有9个属于1类观测。

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

我是通过首先计算最小的班级人数来实现的。.

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    

我想知道是否有可能一口气做到这一点(计算最小的班级人数然后抽样)?

解决方法

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

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函数一次只能看到一组,因此我们需要稍微回避一下。

一个可以做

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

尽管我认为那不是更优雅/更易读的内容。