随机抽样分组

26

给定一个包含名为group的列的数据框df,如何在dplyr中随机抽取k个组?它应该从中返回所有来自k个组的行(假设df$group中至少有k个唯一值),并且df中的每个组被返回的概率应该相等。


2
也许您可以提供一些示例数据?另外,请参阅?sample_n - SymbolixAU
Iris足够了。分组变量是物种。 - Big Dogg
1
使用“sample_n”可以在每个组中随机抽取“n”行。我要求从“n”个随机抽样的组中获取所有行。 - Big Dogg
那么您想要随机选择三个“种类(Species)”级别中的两个级别,并返回这两个所选级别的所有行? - eipi10
5个回答

39

只需使用sample()函数选择若干组即可

iris %>% filter(Species %in% sample(levels(Species),2))

8
如果您有一个字符或数字标识列,可以使用“unique(Species)”代替“levels(Species)”。 - Dan Slone
1
这是一个不错的解决方案,但我在这里遇到了一个问题,因为我希望进行替换抽样,而这与%in%语句不兼容。 - Daniel Münch
非常优雅的答案。如果您正在寻找相反的操作(每个ID的n个条目),请查看这里:https://dev59.com/wGMl5IYBdhLWcg3woYEv - Samuel Saari

11

如果您正在使用dplyr,我认为这种方法最有意义:

iris_grouped <- iris %>% 
  group_by(Species) %>% 
  nest()

产生的结果是:

# A tibble: 3 x 2
  Species    data             
  <fct>      <list>           
1 setosa     <tibble [50 × 4]>
2 versicolor <tibble [50 × 4]>
3 virginica  <tibble [50 × 4]>

你可以使用sample_n来处理:

iris_grouped %>%
  sample_n(2)

# A tibble: 2 x 2
  Species    data             
  <fct>      <list>           
1 virginica  <tibble [50 × 4]>
2 versicolor <tibble [50 × 4]>

2
太好了。别忘了在最后使用 unnest() 进行进一步计算。 - Marco
9
我更喜欢这种语法,但在我的大数据集上,这种方法需要几个小时才能运行,而@MrFlick的答案只需要一秒钟。 - chakuRak
这在tidyverse 1.3.0上对我不起作用。 但是 iris_grouped <- iris %>% nest(-Species) %>% slice_sample(n=2) %>% unnest可以。 - Adam Lee Perelman
使用 tidyr 1.2.0dplyr 1.0.9,@AdamLeePerelman 的回答和建议都无效。我发现 iris %>% group_by(Species) %>% nest() %>% ungroup() %>% slice_sample(n=2) 是可行的。 - Cole Robertson

3
我非常喜欢Tristan Mahr在这里所描述的方法。我已经从博客中复制了他的函数,用于下面的示例:
library(tidyverse)

sample_n_of <- function(data, size, ...) {
  dots <- quos(...)
  
  group_ids <- data %>% 
    group_by(!!! dots) %>% 
    group_indices()
  
  sampled_groups <- sample(unique(group_ids), size)
  
  data %>% 
    filter(group_ids %in% sampled_groups)
}

set.seed(1234)
mpg %>% 
  sample_n_of(size = 2, model)
#> # A tibble: 12 x 11
#>    manufacturer model   displ  year   cyl trans   drv     cty   hwy fl    class 
#>    <chr>        <chr>   <dbl> <int> <int> <chr>   <chr> <int> <int> <chr> <chr> 
#>  1 audi         a6 qua~   2.8  1999     6 auto(l~ 4        15    24 p     midsi~
#>  2 audi         a6 qua~   3.1  2008     6 auto(s~ 4        17    25 p     midsi~
#>  3 audi         a6 qua~   4.2  2008     8 auto(s~ 4        16    23 p     midsi~
#>  4 ford         mustang   3.8  1999     6 manual~ r        18    26 r     subco~
#>  5 ford         mustang   3.8  1999     6 auto(l~ r        18    25 r     subco~
#>  6 ford         mustang   4    2008     6 manual~ r        17    26 r     subco~
#>  7 ford         mustang   4    2008     6 auto(l~ r        16    24 r     subco~
#>  8 ford         mustang   4.6  1999     8 auto(l~ r        15    21 r     subco~
#>  9 ford         mustang   4.6  1999     8 manual~ r        15    22 r     subco~
#> 10 ford         mustang   4.6  2008     8 manual~ r        15    23 r     subco~
#> 11 ford         mustang   4.6  2008     8 auto(l~ r        15    22 r     subco~
#> 12 ford         mustang   5.4  2008     8 manual~ r        14    20 p     subco~

创建于2021年3月24日,使用reprex包(v0.3.0)。

3

请注意,使用dplyr比普通数据框操作要慢得多:

library(microbenchmark)
microbenchmark(dplyr= iris %>% filter(Species %in% sample(levels(Species),2)),
               base= iris[iris[["Species"]] %in% sample(levels(iris[["Species"]]), 2),])

Unit: microseconds
  expr     min      lq     mean  median       uq      max neval cld
 dplyr 660.287 710.655 753.6704 722.629 771.2860 1122.527   100   b
  base  83.629  95.032 110.0936 106.057 119.1715  199.949   100  a 

注意:虽然两者都可以使用,但[[已知比$更快。


1

我也遇到了使用nest时出现的问题。但是当我更新为最新版本的nest()、unnest()和slice_sample()语法后,它就能正常工作了。

下面是另一种版本,如果输入框架按组变量排列,则将产生相同的答案。否则,平均而言,答案也将是一样好的。这个版本比嵌套版本有一些优点:1. 最终数据框中的列按原始顺序排列;相比之下,嵌套版本将把分组变量放在第一位。2:当您进行调试时,中间结果要容易阅读得多,因为它们是普通的列表。

我想对原始组数进行有放回的采样,就像集群引导一样。可以很容易地添加更多参数,使函数更加通用。

# function to compute a clustered bootstrap sample
samplebygroups <- function(df, groupvar){
  datalist <- df %>%
    group_by({{ groupvar }}) %>%
    group_split
  n <- length(datalist)
  samplegroups <- sample(n, replace = TRUE)
  datalist[samplegroups] %>%
    bind_rows
}

这是一个样例运行结果。
smallcars <- mtcars %>%  
  rownames_to_column(var = "Model") %>% 
  tail(5) %>%
  arrange(cyl) %>%
  select(Model, cyl, mpg)

 set.seed(1000)
 samplebygroups(smallcars, cyl)

带有输出

# A tibble: 5 x 3
  Model            cyl   mpg
  <chr>          <dbl> <dbl>
1 Ford Pantera L     8  15.8
2 Maserati Bora      8  15  
3 Ferrari Dino       6  19.7
4 Ford Pantera L     8  15.8
5 Maserati Bora      8  15  

使用Oscar的代码,您将获得完全相同的行,但cyl将成为第一列。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接