4

我有一个数据框,每组包含多个样本(1-n)。我想在不替换的情况下对这个数据集进行采样,这样我每组最多有 5 个样本(1-5)。

此问题之前已在此处进行了描述和回答。在这个问题中,@evolvedmicrobe的回答对我来说是最令人满意的,并且过去一直有效。这似乎在过去一年左右的时间里打破了。

这是我想做的一个可行的例子:

在 mtcars 中,按“cyl”分组时行数不同。

table(mtcars$cyl)
 4  6  8 
11  7 14 

我想创建一个子样本,其中每组 cyl 的最大汽车数量为 10。结果的行数理论上看起来像:

table(subsample$cyl)
 4  6  8
10  7 10

我对此的天真尝试是:

library(dplyr)
subsample <- mtcars %>% group_by(cyl) %>% sample_n(10) %>% ungroup()

但是,因为一组少于 10 行:

错误:size必须小于或等于 7(数据大小),设置replace= TRUE 以使用带替换的采样

@evolvedmicrobe 对此的回答是创建一个自定义采样函数:

### Custom sampler function to sample min(data, sample) which can't be done with dplyr
 ### it's a modified copy of sample_n.grouped_df
 sample_vals <- function (tbl, size, replace = FALSE, weight = NULL, .env = parent.frame()) 
 {
   #assert_that(is.numeric(size), length(size) == 1, size >= 0)
   weight <- substitute(weight)
   index <- attr(tbl, "indices")
   sizes = sapply(index, function(z) min(length(z), size)) # here's my contribution
   sampled <- lapply(1:length(index), function(i) dplyr:::sample_group(index[[i]],  frac = FALSE, tbl = tbl, 
                                       size = sizes[i], replace = replace, weight = weight, .env = .env))
   idx <- unlist(sampled) + 1
   grouped_df(tbl[idx, , drop = FALSE], vars = groups(tbl))
 }

 samped_data = dataset %>% group_by(something) %>% sample_vals(size = 50000) %>% ungroup()

这个函数在过去一直有效,我刚刚尝试重新运行它,但它不再有效,相反,它会抛出与当前 mtcars 示例相同的错误:

library(dplyr)
subsample <- mtcars %>% group_by(cyl) %>% sample_vals(10) %>% ungroup()

dplyr:::sample_group(index[[i]], frac = FALSE, tbl = tbl, size = sizes[i], : 未使用的参数 (tbl = tbl) 调用自:FUN(X[[i]], ...)

有没有人有更好的按组抽样的方法,无需更换,达到每组的最大尺寸?我通常不是 dplyr 的大用户,因此也欢迎来自 base R 或其他软件包的所有选项。

否则,有没有人知道为什么以前的解决方法已经停止工作?

感谢大家的时间。

4

4 回答 4

4

这是一个简单的解决方案,使用slice-

samples_per_group <- 10

subsample <- mtcars %>%
  group_by(cyl) %>%
  slice(sample(n(), min(samples_per_group, n()))) %>%
  ungroup()

table(subsample$cyl)

#  4  6  8 
# 10  7 10
于 2018-10-15T13:25:00.393 回答
2

使用基础 R 也非常简单,例如:

do.call(rbind, lapply(split(mtcars, mtcars$cyl), function(x) {
  n <- nrow(x)
  s <- min(n, 10)
  x[sample(seq_len(n), s),]
}))

输出中的行将按cyl-- 但行顺序可能无论如何都无关紧要。

于 2018-10-15T15:04:22.377 回答
1

对于一个简单的功能,您可以使用这个解决方法,它首先用没有足够的样本来炸毁组,然后在最后过滤掉它们:

library(dplyr)
library(tidyr)

size <- 10

subsample <- mtcars %>% 
  group_by(cyl) %>% 
  mutate(group_count = n(), 
         group_count_along = 1:n()) %>% 
  ungroup() %>% 
  complete(cyl, group_count_along) %>% 
  group_by(cyl) %>% 
  filter(group_count_along <= max(group_count, size, na.rm = T)) %>% 
  sample_n(size) %>% 
  ungroup() %>% 
  filter(group_count_along <= group_count)

table(subsample$cyl)
 4  6  8 
10  7 10 
于 2018-10-15T12:28:24.943 回答
1

该函数sample_group已更新,参数tbl.env删除。从您的函数中删除这些参数sample_vals并摆脱它们会+1恢复您的函数的功能。

require(dplyr)

sample_vals <- function (tbl, size, replace = FALSE, weight = NULL){
    ## assert_that(is.numeric(size), length(size) == 1, size >= 0)
    weight <- substitute(weight)
    index <- attr(tbl, "indices")
    sizes <- sapply(index, function(z) min(length(z), size)) # here's my contribution
    sampled <- lapply(1:length(index),
                      function(i) dplyr:::sample_group(index[[i]],  frac = FALSE, 
                                                       size = sizes[i],
                                                       replace = replace,
                                                       weight = weight))
    idx <- unlist(sampled) ## + 1
    grouped_df(tbl[idx, , drop = FALSE], vars = groups(tbl))
}

samped_data <- mtcars %>% group_by(cyl) %>% sample_vals(size = 10) %>% ungroup()

table(samped_data$cyl)
于 2018-10-15T14:24:06.583 回答