使用 dplyr 从数据帧中采样子组行
Posted
技术标签:
【中文标题】使用 dplyr 从数据帧中采样子组行【英文标题】:sample rows of subgroups from dataframe with dplyr 【发布时间】:2014-02-10 21:12:54 【问题描述】:如果我想从不同组中随机选择一些样本,我使用 plyr 包和下面的代码
require(plyr)
sampleGroup<-function(df,size)
df[sample(nrow(df),size=size),]
iris.sample<-ddply(iris,.(Species),function(df) sampleGroup(df,10))
这里从每个物种中选择了 10 个样本。
我的一些数据框非常大,我的问题是我可以使用与 dplyr 包相同的 sampleGroup 函数吗?或者在 dplyr 中是否有其他方法可以做到这一点?
编辑
0.2 版的 dplyr 包引入了两个新函数,用于从表 sample_n 和 sample_frac 中选择随机行
【问题讨论】:
这里是 dplyr 介绍的链接。 rpubs.com/hadley/dplyr-intro 谢谢,但我认为这个问题的解决方案还没有在文档中。不过 data.table 的解决方案很好! 为什么不直接使用iris %.% group_by(Species) %.% sampleGroup(size = 10)
我不认为有一个自然的纯 dplyr 解决方案,但采样似乎足够重要,它应该是一个***函数:github.com/hadley/dplyr/issues/202
@Robert 我不确定我是如何在你的问题中错过的;说得很清楚。删除我的评论。
【参考方案1】:
是的,您可以通过函数 do() 优雅地使用 dplyr。 这是一个例子:
mtcars %>%
group_by(cyl) %>%
do(sample_n(.,2))
结果是这样的
Source: local data frame [6 x 11]
Groups: cyl
mpg cyl disp hp drat wt qsec vs am gear carb
1 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
3 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
4 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
5 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
6 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
更新:
在较新版本的 dplyr 中,sample_n
不再需要 do
函数。当前用于每组随机抽取两行样本的代码:
mtcars %>%
group_by(cyl) %>%
sample_n(2)
【讨论】:
@Arun ,是的,但是您应该将 dplyr 更新到最新版本 0.1.3.0.99。 @Arun,对不起,你应该使用 sample_n() 有没有办法在不使用do
的情况下做到这一点?
你能根据上面的data.table
解决方案计时吗?我尽可能多地留在dplyr
,因为语法更容易(或者至少我还没有学过data.table
)。 SO 上的每个dplyr
问题都会得到data.table
的答案,这有点让我抓狂,所以我想看看这个新代码是否会接近。
@gregmacfarlane 只需阅读上面的 cmets,就会明白。当时没有一种可接受的方法来使用dplyr
来做到这一点。在阅读了当时的文档后,OP 回答:“谢谢,但我认为解决这个问题的方法还没有在文档中。不过 data.table 的解决方案很好!-罗伯特”。还请阅读问题提出时的其他答案,它们看起来不像是惊人的解决方案......【参考方案2】:
这对于 data.table 来说很容易做到,并且对于大表很有用。
注意: 正如特洛伊在评论中提到的那样,使用 data.table 有一种更有效的方法,但我想尊重答案中的 OP 示例函数和格式。
require(data.table)
DT <- data.table(x = rnorm(10e6, 100, 50), y = letters)
sampleGroup<-function(df,size)
df[sample(nrow(df),size=size),]
result <- DT[, sampleGroup(.SD, 10), by=y]
print(result)
# y x y
# 1: a 30.11659 m
# 2: a 57.99974 h
# 3: a 58.13634 o
# 4: a 87.28466 x
# 5: a 85.54986 j
# ---
# 256: z 149.85817 d
# 257: z 160.24293 e
# 258: z 26.63071 j
# 259: z 17.00083 t
# 260: z 130.27796 f
system.time(DT[, sampleGroup(.SD, 10), by=y])
# user system elapsed
# 0.66 0.02 0.69
Using the iris dataset:
iris <- data.table(iris)
iris[,sampleGroup(.SD, 10), by=Species]
【讨论】:
+1 用于 data.table。使用.I
使性能速度翻倍:iris[iris[,list(idx=sample(.I,10)),by="Species"]$idx]
我想你想要sampleGroup(.SD, 10)
(注意.SD
而不是DT
)【参考方案3】:
这是个好问题!使用dplyr
的记录语法看不到任何简单的方法,但是如何解决这个问题?
sampleGroup<-function(df,x=1)
df[
unlist(lapply(attr((df),"indices"),function(r)sample(r,min(length(r),x))))
,]
sampleGroup(iris %.% group_by(Species),3)
#Source: local data frame [9 x 5]
#Groups: Species
#
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#39 4.4 3.0 1.3 0.2 setosa
#16 5.7 4.4 1.5 0.4 setosa
#25 4.8 3.4 1.9 0.2 setosa
#51 7.0 3.2 4.7 1.4 versicolor
#62 5.9 3.0 4.2 1.5 versicolor
#59 6.6 2.9 4.6 1.3 versicolor
#148 6.5 3.0 5.2 2.0 virginica
#103 7.1 3.0 5.9 2.1 virginica
#120 6.0 2.2 5.0 1.5 virginica
编辑 - 性能比较
这是针对 1m 行、26 个组使用 data.table(本机和按照示例使用函数调用)的测试。
本机 data.table 的速度大约是 dplyr 解决方法的 2 倍,也比带有标注的 data.table 调用快。所以可能 dplyr / data.table 的性能差不多。
希望 dplyr 的家伙很快就会给我们一些本机的采样语法! (或者更好,也许它已经存在了)
sampleGroup.dt<-function(df,size)
df[sample(nrow(df),size=size),]
testdata<-data.frame(group=sample(letters,10e5,T),runif(10e5))
dti<-data.table(testdata)
# using the dplyr workaround with external function call
system.time(sampleGroup(testdata %.% group_by(group),10))
#user system elapsed
#0.07 0.00 0.06
#using native data.table
system.time(dti[dti[,list(val=sample(.I,10)),by="group"]$val])
#user system elapsed
#0.04 0.00 0.03
#using data.table with external function call
system.time(dti[, sampleGroup.dt(dti, 10), by=group])
#user system elapsed
#0.06 0.02 0.08
【讨论】:
+1 表示 Troy 的分析器以正确的方式使用 data.table。我的回答可能比较慢,因为它复制了两倍的表格。 +1 非常好的比较。但我不明白你最后一次基准测试的原因?您正在对每个组的 10 个元素的整个数据进行采样。而您正在使用attributes
为dplyr
做一些事情.. 为什么不对dplyr
进行相同的基准测试,其功能类似于DT
的第三种情况?
此外,基准测试的一个重要方面是查看它的扩展程度。只需汇总 26 个组,就无法检测到真正的差异。将您的行更改为 testdata<-data.frame(group=sample(paste("id", 1:1e5, sep=""),10e5,T),runif(10e5))
并再次运行您的基准测试
请注意,dplyr 的内部结构(例如 indices
属性)可能会不断发展。不要依赖它们的结构。【参考方案4】:
Dplyr 1.0.2 现在可以使用各种动词进行子集化:https://dplyr.tidyverse.org/reference/slice.html 包括随机 slice_sample:
mtcars %>%
slice_sample(n = 10)
并添加分组依据以按类别进行抽样:
mtcars %>%
group_by(cyl) %>%
slice_sample(n = 2)
【讨论】:
以上是关于使用 dplyr 从数据帧中采样子组行的主要内容,如果未能解决你的问题,请参考以下文章
如何确定一个值是不是在使用 Dplyr 的一组行中出现最多? [复制]
基于另一个数据帧 Python 和 Pandas 从数据帧中采样
从 len 18000 的 Dask 数据帧中采样 n = 2000 会产生错误 当“replace = False”时无法采用比总体更大的样本