使用dplyr从dataframe中抽取子组行

Rob*_*ert 29 r sample dplyr

如果我想从不同的组中随机选择一些样本,我使用plyr包和下面的代码

require(plyr)
sampleGroup<-function(df,size) {
  df[sample(nrow(df),size=size),]
}

iris.sample<-ddply(iris,.(Species),function(df) sampleGroup(df,10))
Run Code Online (Sandbox Code Playgroud)

这里从每个物种中选择10个样品.

我的一些数据帧非常大,我的问题是我可以使用与dplyr包相同的sampleGroup函数吗?或者还有另一种方法在dplyr中做同样的事情吗?

编辑

dplyr软件包的0.2版引入了两个新函数来从表sample_n和sample_frac中选择随机行

Phi*_*ang 58

是的,您可以通过函数do()优雅地使用dplyr.这是一个例子:

mtcars %>% 
    group_by(cyl) %>%
    do(sample_n(.,2))
Run Code Online (Sandbox Code Playgroud)

结果是这样的

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
Run Code Online (Sandbox Code Playgroud)

更新:

在较新版本的dplyr中do不再需要该功能sample_n.用于获取每组两行的随机样本的当前代码:

mtcars %>% 
    group_by(cyl) %>% 
    sample_n(2)
Run Code Online (Sandbox Code Playgroud)

  • 你可以针对上面的`data.table`解决方案计算你的东西吗?我尽可能多地留在`dplyr`,因为语法更容易(或者至少我还没有学过`data.table`).有点让我疯狂的是SO上的每个'dplyr`问题得到一个`data.table`答案,所以我想看看这个新代码是否接近. (3认同)

mar*_*bel 8

这对data.table很容易,对大表很有用.

注意: 正如Troy的演讲中所提到的,使用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]
Run Code Online (Sandbox Code Playgroud)

  • +1表示data.table.使用`.I`使性能速度加倍:`iris [iris [,list(idx = sample(.I,10)),by ="Species"] $ idx]` (2认同)

Tro*_*roy 7

这是个好问题!使用文档化的语法无法看到任何简单的方法,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
Run Code Online (Sandbox Code Playgroud)

编辑 - 性能比较

这是针对1m行,26组使用data.table(本机和函数调用)的测试.

Native data.table的速度是dplyr变通方法的2倍,也是带callout的data.table调用的2倍.所以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 
Run Code Online (Sandbox Code Playgroud)

  • 此外,基准测试的一个重要方面是看它**的重要程度**.由于只有26个小组聚合,所以没有真正的区别可以检测到.将您的行更改为`testdata <-data.frame(group = sample(paste("id",1:1e5,sep =""),10e5,T),runif(10e5))`并再次运行基准测试 (3认同)