使用组特定的样本大小从数据框中抽取样本。

10

我希望从一个数据框中使用不同的样本大小来对行进行抽样。这个数据框按 'group' 分组:

假设我们有一个简单的数据框:

library(dplyr)
set.seed(123)

df <- data.frame(group = rep(c("A", "B"), each = 10), 
                 value = rnorm(10))
df
#>    group       value
#> 1      A -0.56047565
#> 2      A -0.23017749
#> .....
#> 10     A -0.44566197
#> 11     B -0.56047565
#> 12     B -0.23017749
#> .....
#> 20     B -0.44566197

使用 dplyr 包中的 slice_sample 函数,您可以轻松地从此数据框中切片出相等大小的组:
df %>% group_by(group) %>% slice_sample(n = 2) %>% ungroup()

#> # A tibble: 4 x 2
#>   group  value
#>   <fct>  <dbl>
#> 1 A     -0.687
#> 2 A     -0.446
#> 3 B     -0.687
#> 4 B      1.56

问题

如何从每个组中取出不同数量的值进行采样(对于大小不相等的切片组)?例如,从A组中随机抽取4行,从B组中随机抽取5行?

7个回答

11
我能想到的最简单的方法是使用 purrr 中的 map2 函数解决。
library(dplyr)
library(purrr)

df %>% 
  group_split(group) %>% 
  map2_dfr(c(4, 5), ~ slice_sample(.x, n = .y))

# A tibble: 9 x 2
  group   value
  <chr>   <dbl>
1 A     -0.687 
2 A      1.56  
3 A      0.0705
4 A      1.72  
5 B     -0.560 
6 B      0.461 
7 B      0.129 
8 B      0.0705
9 B     -0.230 

需要注意的是,您需要了解拆分的顺序。我认为group_split()将按因子对组进行排序。解决这个问题的方法是适应以下方式,并从命名向量中查找n

group_slice_n <- c(A = 4, B = 5)

df %>% 
  split(.$group) %>% 
  imap_dfr(~ slice_sample(.x, n = group_slice_n[.y]))

6

试试这个:

group_sizes <- tibble(group = c("A", "B"), size = c(4, 5))
set.seed(2021)
df %>%
  left_join(group_sizes, by = "group") %>%
  group_by(group) %>%
  mutate(samp = sample(n())) %>%
  filter(samp <= size) %>%
  ungroup()
# # A tibble: 9 x 4
#   group   value  size  samp
#   <chr>   <dbl> <dbl> <int>
# 1 A      0.0705     4     2
# 2 A      0.129      4     4
# 3 A     -0.687      4     1
# 4 A     -0.446      4     3
# 5 B     -0.560      5     5
# 6 B      1.56       5     1
# 7 B      0.129      5     4
# 8 B      1.72       5     3
# 9 B     -1.27       5     2

3
你可以使用我“splitstackshape”包中的stratified函数:
> library(splitstackshape)
> stratified(df, "group", c(A = 4, B = 5))
   group      value
1:     A -0.6868529
2:     A  0.4609162
3:     A  1.7150650
4:     A -0.4456620
5:     B  0.4609162
6:     B -0.4456620
7:     B  0.1292877
8:     B -1.2650612
9:     B -0.2301775

1
看起来非常简洁,具有分层结构。 - akrun

1

使用data.table方法,利用mapply循环遍历列表元素并使用向量中的样本大小(与列表长度相同)。

library( data.table )
setDT(df) #make it a data.table
L <- split( df, by = "group" ) #split to a list by group
#function
mysamples <- function( dt, samplesize ) {
  dt[ sample( 1:nrow(dt), samplesize), ]
}
#mapply
mapply( mysamples, L, samplesize = c(4,5), SIMPLIFY = FALSE )

#output
# $A
# group      value
# 1:     A -0.6868529
# 2:     A -0.4456620
# 3:     A -0.5604756
# 4:     A  0.1292877
# 
# $B
# group      value
# 1:     B  1.5587083
# 2:     B -1.2650612
# 3:     B -0.2301775
# 4:     B  0.4609162
# 5:     B -0.6868529

1
set.seed(123)
library(tidyverse)

map2_df(unique(df$group), c(4,5),
        ~df %>% 
          filter(group == .x) %>% 
          slice_sample(n = .y))

  group      value
1     A -0.3724388
2     A -0.4168576
3     A  0.5629895
4     A -1.2601552
5     B  1.0527115
6     B -0.3745809
7     B  0.9769734
8     B -0.4168576
9     B -1.0491770

如果您的数据还没有被整理,可以使用以下方法:
map2_df(unique(sort(df$group)), c(4,5),
        ~df %>% arrange(group) %>% 
          filter(group == .x) %>%
          slice_sample(n = .y))

1
当组的顺序不受控制时,如何将“4”锁定到A - r2evans
map2_df(unique(sort(df$group)), c(4,5), ~df %>% arrange(group) %>% filter(group == .x) %>% slice_sample(n = .y)) - Lennyy
但是,我同意@Adam的解决方案更短/更可取。 - Lennyy
1
(不要告诉我,那属于答案。而且@Adam的第一部分也存在同样的问题... 看起来在更新中已经修复。) - r2evans

1

基于连接的另一种 data.table 可能性。

将特定组的样本大小放入“查找表”中(这里是列表,.(...));在“group”上与原始数据连接(on = .(group));对于每个匹配项在 i 中(by = .EACHI),从 'value' 中选择一个大小为 size[1] 的样本

setDT(df)[.(group = c("A", "B"), size = c(4, 5)), on = .(group), sample(value, size[1]),
         by = .EACHI]

#    group         V1
# 1:     A -0.6868529
# 2:     A -0.4456620
# 3:     A -0.5604756
# 4:     A  0.1292877
# 5:     B  1.5587083
# 6:     B -1.2650612
# 7:     B -0.2301775
# 8:     B  0.4609162
# 9:     B -0.6868529

0

这里提供一种使用 nest/unnest 的替代答案:

library(tidyverse)
set.seed(123)

df <- data.frame(
  group = rep(c("A", "B"), each = 10),
  value = rnorm(10)
)

df %>%
  nest(data = value) %>%
  mutate(
    sample_size = c(4, 5),
    data_sample = map2(data, sample_size, ~ slice_sample(.x, n = .y))
  ) %>%
  select(group, data_sample) %>%
  unnest(cols = data_sample)
#> # A tibble: 9 × 2
#>   group  value
#>   <chr>  <dbl>
#> 1 A     -0.687
#> 2 A     -0.446
#> 3 A     -0.560
#> 4 A      0.129
#> 5 B      1.56 
#> 6 B     -1.27 
#> 7 B     -0.230
#> 8 B      0.461
#> 9 B     -0.687

reprex package (v2.0.1)于2022年10月28日创建


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