Sample within a group multiple times in r using dplyr

Viewed 214

I am trying to pick samples within each group:

df <- data.frame(ID=c(1,1,1,2,2,2), score=c(10,20,30,40,50,60))

  ID score
1  1    10
2  1    20
3  1    30
4  2    40
5  2    50
6  2    60

df %>% group_by(ID) %>% sample_n(2)

     ID score
1     1    20
2     1    30
3     2    50
4     2    40

But I want to do it n multiple times for each ID, for example 2 times to get something like this:

     ID score sample_num
1     1    20          1
2     1    30          1
3     1    20          2
4     1    10          2
5     2    50          1
6     2    40          1
7     2    60          2
8     2    40          2

Each sample set should be done without replacement. Is there a way to do this in dplyr? The long way I can think of is to do a for loop, create a df each iteration and then combine all the dfs together at the end.

3 Answers

If you have to do it N number of times, do this

  • create a variable N for times
  • map_dfr will iterate over its first argument i.e. seq_len(N) , do what you were doing manually, mutate one more variable which will store respective value of seq_len(N) i.e. .x in lambda formula, for each iteration.
  • final results will be compiled in a data frame as we are using map_dfr variant of map
df <- data.frame(ID=c(1,1,1,2,2,2), score=c(10,20,30,40,50,60))

library(tidyverse)
N <- 7
map_dfr(seq_len(N), ~df %>% group_by(ID) %>% sample_n(2) %>% 
             mutate(sample_no = .x))
#> # A tibble: 28 x 3
#> # Groups:   ID [2]
#>       ID score sample_no
#>    <dbl> <dbl>     <int>
#>  1     1    20         1
#>  2     1    10         1
#>  3     2    60         1
#>  4     2    50         1
#>  5     1    30         2
#>  6     1    10         2
#>  7     2    60         2
#>  8     2    40         2
#>  9     1    10         3
#> 10     1    20         3
#> # ... with 18 more rows

Created on 2021-06-11 by the reprex package (v2.0.0)

library(tidyverse)

df <- data.frame(ID=c(1,1,1,2,2,2), score=c(10,20,30,40,50,60))

set.seed(123)

#option 1
rerun(2, df %>% group_by(ID) %>% sample_n(2,replace = FALSE)) %>%
    map2(1:length(.), ~mutate(.x, sample_n = .y)) %>% 
    reduce(bind_rows) %>% 
    arrange(ID)
#> # A tibble: 8 x 3
#> # Groups:   ID [2]
#>      ID score sample_n
#>   <dbl> <dbl>    <int>
#> 1     1    30        1
#> 2     1    10        1
#> 3     1    30        2
#> 4     1    20        2
#> 5     2    60        1
#> 6     2    50        1
#> 7     2    50        2
#> 8     2    60        2

#option 2 
map(1:2, ~df %>% group_by(ID) %>%
        sample_n(2,replace = FALSE) %>%
        mutate(sample_num = .x)) %>% 
    reduce(bind_rows) %>% 
    arrange(ID)
#> # A tibble: 8 x 3
#> # Groups:   ID [2]
#>      ID score sample_num
#>   <dbl> <dbl>      <int>
#> 1     1    30          1
#> 2     1    10          1
#> 3     1    10          2
#> 4     1    20          2
#> 5     2    50          1
#> 6     2    60          1
#> 7     2    60          2
#> 8     2    50          2

Created on 2021-06-11 by the reprex package (v2.0.0)

library(tidyverse)
set.seed(1)
n_repeat <- 2
n_sample <- 2

df <- data.frame(ID=c(1,1,1,2,2,2), score=c(10,20,30,40,50,60))

df %>% 
  group_nest(ID) %>% 
  transmute(ID,
            Score = map(data, ~as.vector(replicate(n_repeat, sample(.x$score, 2))))) %>% 
  unnest(Score) %>%
  group_by(ID) %>% 
  mutate(sample_no = rep(seq(n_repeat), each = n_sample)) %>% 
  ungroup()
#> # A tibble: 8 x 3
#>      ID Score sample_no
#>   <dbl> <dbl>     <int>
#> 1     1    10         1
#> 2     1    20         1
#> 3     1    30         2
#> 4     1    10         2
#> 5     2    50         1
#> 6     2    40         1
#> 7     2    60         2
#> 8     2    40         2

Created on 2021-06-11 by the reprex package (v2.0.0)

Related