在数据框中对每组随机采样 n 行

Posted

技术标签:

【中文标题】在数据框中对每组随机采样 n 行【英文标题】:Sample n random rows per group in a dataframe 【发布时间】:2014-07-12 23:02:14 【问题描述】:

从这些问题 - Random sample of rows from subset of an R dataframe 和 Sample random rows in dataframe 我可以很容易地看到如何从 df 中随机抽样(选择)“n”行,或者从 df 中某个因子的特定级别产生的“n”行.

以下是一些示例数据:

df <- data.frame(matrix(rnorm(80), nrow=40))
df$color <-  rep(c("blue", "red", "yellow", "pink"), each=10)

df[sample(nrow(df), 3), ] #samples 3 random rows from df, without replacement.

例如只需从“粉红色”颜色中随机抽取 3 行 - 使用 library(kimisc):

library(kimisc)
sample.rows(subset(df, color == "pink"), 3)

或编写自定义函数:

sample.df <- function(df, n) df[sample(nrow(df), n), , drop = FALSE]
sample.df(subset(df, color == "pink"), 3)

但是,我想从因子的 每个级别 中抽取 3 个(或 n 个)随机行。 IE。新的 df 将有 12 行(蓝色 3 行,红色 3 行,黄色 3 行,粉红色 3 行)。显然可以运行几次,为每种颜色创建 newdfs,然后将它们绑定在一起,但我正在寻找更简单的解决方案。

【问题讨论】:

另见How do you sample random rows within each group in a data.table? 这能回答你的问题吗? Take random sample by group 【参考方案1】:

dplyr 0.3 及更高版本中,这工作得很好:

df %>% group_by(color) %>% sample_n(size = 3)

dplyr 的旧版本(版本

我开始使用 dplyr 来回答这个问题,假设这可行:

df %.% group_by(color) %.% sample_n(size = 3)

但事实证明,在 0.2 中,sample_n.grouped_df S3 方法存在但未在 NAMESPACE 文件中注册,因此从未分派。相反,我必须这样做:

df %.% group_by(color) %.% dplyr:::sample_n.grouped_df(size = 3)
Source: local data frame [12 x 3]
Groups: color

            X1         X2  color
8   0.66152710 -0.7767473   blue
1  -0.70293752 -0.2372700   blue
2  -0.46691793 -0.4382669   blue
32 -0.47547565 -1.0179842   pink
31 -0.15254540 -0.6149726   pink
39  0.08135292 -0.2141423   pink
15  0.47721644 -1.5033192    red
16  1.26160230  1.1202527    red
12 -2.18431919  0.2370912    red
24  0.10493757  1.4065835 yellow
21 -0.03950873 -1.1582658 yellow
28 -2.15872261 -1.5499822 yellow

这可能会在未来的更新中得到修复。

【讨论】:

您使用的是哪个版本的dplyr?是后备箱吗? 我在 cran 上尝试了 0.2,然后从 github 安装;同样的事情。 @joran in dplyr 0.3 这就像一个魅力。这是我现在最喜欢的解决上述问题的方法。 谁能解释一下这在概念上是如何工作的? sample_n() 是否回头查看是否已应用 group_by()。 @axme100 管道%&gt;% 将每一步的结果传递给下一个函数,因此无需“向后看”。运行x &lt;- mtcars %&gt;% group_by(cyl),然后开始查看x。您会看到它有一个新的类属性以及许多其他属性 (attributes(x)),因此任何后续函数都“知道”它正在处理分组数据框。【参考方案2】:

您可以使用ave 为每个具有特定因子水平的元素分配一个随机 ID。然后可以选择一定范围内的所有随机ID。

rndid <- with(df, ave(X1, color, FUN=function(x) sample.int(length(x))))
df[rndid<=3,]

如果您对此感兴趣,这样做的好处是可以保留原始行顺序和行名。另外,您可以重复使用 rndid 向量来相当轻松地创建不同长度的子集。

【讨论】:

这个建议和另一个答案都很好用。我可以检查一下关于上述代码的两件事。 1) 变量 X1。在这里选择 df 中的哪个变量是否重要? (似乎没有)。 2)在不同因子水平的观察数量不同的情况下 - 我想返回每个因子水平的行子集,超过某些因子水平中存在的总量,该解决方案仍然有效。即,如果我要求每种颜色 11 行,它将返回 10。这在我的实际数据中可能很有用,因为每个因子级别的 obs/rows 确实有所不同。 @jalapic 1)您是正确的,因为您将哪个变量作为第一个参数传递并不重要。传递数字向量有助于保持结果为数字。 2) 如果您要求 10 行 (rndid&lt;=10) 而一个组只有 3 行,则该组的所有三行都将返回,并且不会引入缺失值,也不会进行替换采样。所以你最终可能会遇到不平衡的群体。 谢谢。我不介意这种情况下的不平衡组,所以效果很好。 @MrFlick ,我想满足卡方检验中的样本量条件,所以我需要在每组中采样at least 5 个案例,我该如何使用您的解决方案来做到这一点?【参考方案3】:

我会考虑 my stratified function,它目前作为 GitHub Gist 托管。

获取它:

library(devtools)  ## To download "stratified"
source_gist("https://gist.github.com/mrdwab/6424112")

并将其用于:

stratified(df, "color", 3)

有几种不同的功能便于分层抽样。例如,您还可以采用“即时”的示例。

stratified(df, "color", 3, select = list(color = c("blue", "red")))

为了让您了解函数的作用,以下是stratified 的参数:

df:输入data.frame group:构成“层”的一列或多列的字符向量。 size:所需的样本量。 如果 size 的值小于 1,则从每个层中抽取一个成比例的样本。 如果 size 是 1 或更大的单个整数,则该数量的样本取自每个层。 如果size 是整数向量,则为每个层获取指定数量的样本。建议您使用命名向量。例如,如果您有两个层,“A”和“B”,并且您想要“A”中的 5 个样本和“B”中的 10 个样本,您可以输入 size = c(A = 5, B = 10)select:这允许您在采样过程中对组进行子集化。这是list。例如,如果您的group 变量是“组”,它包含三个层,“A”、“B”和“C”,但您只想从“A”和“C”中抽样,您可以使用select = list(Group = c("A", "C"))replace:用于带替换采样。

【讨论】:

这是一个非常简洁的功能 - 非常有用 很好,很有帮助。在某些版本中,source_gist 函数中似乎存在错误,这会引发错误。我使用了这样的解决方法:source_gist("https://gist.github.com/mrdwab/6424112", filename = "stratified.R")【参考方案4】:

这里有一个解决方案。我们将 data.frame 拆分为颜色组。然后我们从每组中抽取 3 行。这会产生一个 data.frames 列表。

df2 <- lapply(split(df, df$color),
   function(subdf) subdf[sample(1:nrow(subdf), 3),]
)

为了得到想要的结果,我们将data.frames列表合并为1个data.frame:

do.call('rbind', df2)
##                    X1          X2  color
## blue.3    -1.22677188  1.25648082   blue
## blue.4    -0.54516686 -1.94342967   blue
## blue.1     0.44647071  0.16283326   blue
## pink.40    0.23520296 -0.40411906   pink
## pink.34    0.02033939 -0.32321309   pink
## pink.33   -1.01790533 -1.22618575   pink
## red.16     1.86545895  1.11691250    red
## red.11     1.35748078 -0.36044728    red
## red.13    -0.02425645  0.85335279    red
## yellow.21  1.96728782 -1.81388110 yellow
## yellow.25 -0.48084967  0.07865186 yellow
## yellow.24 -0.07056236 -0.28514125 yellow

【讨论】:

@gagolews.If 我想为 groupwise 选择不同的样本大小? 例如这里 3 对于每个组都是固定的,但我想要不同的组值 喜欢blue 我需要2 ,pink 我需要1,red 我需要5,最后yellow 我需要3。 我用 c(2,1,5,3) 替换了3,但它考虑了第一个元素,即2【参考方案5】:

这是一种基本的方法,它允许多个组和替换抽样:

n <- 3
resample <- TRUE
index <- 1:nrow(df)
fun <- function(x) sample(x, n, replace = resample)
a <- aggregate(index, by = list(group = df$color), FUN = fun )

df[c(a$x),]

要添加另一个组,请将其包含在聚合的 'by' 参数中。

【讨论】:

以上是关于在数据框中对每组随机采样 n 行的主要内容,如果未能解决你的问题,请参考以下文章

Pandas 数据框中的随机行选择

从两个文本文件(平行语料库)中随机抽样N行

如何在 dplyr 中每 n 行非随机采样?

在pyspark数据框中orderby之后选择第n行

如何根据条件在 Python 中对数据帧进行下采样

为spark scala中的数据框中的每个组采样不同数量的随机行