使用dplyr从数据框中生成样本

时间:2014-04-23 07:53:00

标签: r dplyr

我有一个非常大的数据框(150.000.000行),格式如下:

df = data.frame(pnr = rep(500+2*(1:15),each=3), x = runif(3*15))

pnr是person id,x是一些数据。我想抽样10%的人。在dplyr中有一种快速的方法吗?

以下是一个解决方案,但由于merge-statement

,它很慢
prns = as.data.frame(unique(df$prn))
names(prns)[1] = "prn"
prns$s = rbinom(nrow(prns),1,0.1)

df = merge(df,prns)
df2 = df[df$s==1,]

3 个答案:

答案 0 :(得分:4)

在基数R中,对10%的行进行采样,向上舍入到下一行

> df[sample(nrow(df), ceiling(0.1*nrow(df)), FALSE), ]
##    pnr         x
## 16 512 0.9781232
## 21 514 0.5279925
## 33 522 0.8332834
## 14 510 0.7989481
## 4  504 0.7825318

或向下舍入到下一行

> df[sample(nrow(df), floor(0.1*nrow(df)), FALSE), ]
##    pnr           x
## 43 530 0.449985180
## 35 524 0.996350657
## 2  502 0.499871966
## 25 518 0.005199058

或取样pnr列的10%,向上舍入

> sample(df$pnr, ceiling(0.1*length(df$pnr)), FALSE)
## [1] 530 516 526 518 514 

添加

如果您希望抽样10%的人(唯一pnr ID),并返回这些人及其各自的数据,我认为您需要

> S <- sample(unique(df$pnr), ceiling(0.1*length(unique(df$pnr))), FALSE)
> df[df$pnr %in% S, ]
##    pnr         x
## 1  502 0.7630667
## 2  502 0.4998720
## 3  502 0.4839460
## 22 516 0.8248153
## 23 516 0.5795991
## 24 516 0.1572472

PS:我会等dplyr个答案。它可能会快15米行。

答案 1 :(得分:4)

我实际上建议在“dplyr”上使用“data.table”包。这是一个带有一些大样本数据的例子(不比你自己的1500万行小得多)。

我还会展示一些正确和错误的做事方式: - )

这是示例数据。

library(data.table)
library(dplyr)
library(microbenchmark)
set.seed(1)
mydf <- DT <- data.frame(person = sample(10000, 1e7, TRUE),
                   value = runif(1e7))

我们还会创建一个“data.table”并将键设置为“person”。创建“data.table”不会占用大量时间,但设置密钥可以。

system.time(setDT(DT))
#    user  system elapsed 
#   0.001   0.000   0.001 

## Setting the key takes some time, but is worth it
system.time(setkey(DT, person)) 
#    user  system elapsed 
#   0.620   0.025   0.646

我想不出比下面更有效的选择“人”值的方法,所以我从基准中删除了这些 - 它们对所有方法都是通用的。

## Common to all tests...
A <- unique(mydf$person)
B <- sample(A, ceiling(.1 * length(A)), FALSE)

为方便起见,不同的测试以函数形式表示......

## Base R #1
fun1a <- function() {
  mydf[mydf$person %in% B, ]
}

## Base R #2--sometimes using `which` makes things quicker
fun1b <- function() {
  mydf[which(mydf$person %in% B), ]
}

## `filter` from "dplyr"
fun2 <- function() {
  filter(mydf, person %in% B)
}

## The "wrong" way to do this with "data.table"
fun3a <- function() {
  DT[which(person %in% B)]
}

## The "right" (I think) way to do this with "data.table"
fun3b <- function() {
  DT[J(B)]
}

现在,我们可以进行基准测试:

## The benchmarking
microbenchmark(fun1a(), fun1b(), fun2(), fun3a(), fun3b(), times = 20)
# Unit: milliseconds
#     expr       min        lq    median        uq       max neval
#  fun1a() 382.37534 394.27968 396.76076 406.92431 494.32220    20
#  fun1b() 401.91530 413.04710 416.38470 425.90150 503.83169    20
#   fun2() 381.78909 394.16716 395.49341 399.01202 417.79044    20
#  fun3a() 387.35363 397.02220 399.18113 406.23515 413.56128    20
#  fun3b()  28.77801  28.91648  29.01535  29.37596  42.34043    20

看看我们以正确的方式使用“data.table”获得的性能!然而,所有其他方法都令人印象深刻。


summary显示结果相同。 (“data.table”解决方案的行顺序会有所不同,因为它已经过排序。)

summary(fun1a())
#      person         value         
#  Min.   :  16   Min.   :0.000002  
#  1st Qu.:2424   1st Qu.:0.250988  
#  Median :5075   Median :0.500259  
#  Mean   :4958   Mean   :0.500349  
#  3rd Qu.:7434   3rd Qu.:0.749601  
#  Max.   :9973   Max.   :1.000000  

summary(fun2())
#      person         value         
#  Min.   :  16   Min.   :0.000002  
#  1st Qu.:2424   1st Qu.:0.250988  
#  Median :5075   Median :0.500259  
#  Mean   :4958   Mean   :0.500349  
#  3rd Qu.:7434   3rd Qu.:0.749601  
#  Max.   :9973   Max.   :1.000000  

summary(fun3b())
#      person         value         
#  Min.   :  16   Min.   :0.000002  
#  1st Qu.:2424   1st Qu.:0.250988  
#  Median :5075   Median :0.500259  
#  Mean   :4958   Mean   :0.500349  
#  3rd Qu.:7434   3rd Qu.:0.749601  
#  Max.   :9973   Max.   :1.000000 

答案 2 :(得分:1)

如果您不一定需要完整的随机样本,那么您可以

filter(df, pnr %% 10 ==0).

哪个会占用每10个人(通过更改为==1可以获得10个不同的样本,...)。您可以通过随机重新分配ID来实现此随机 - 使用sample(15)[(df$pnr-500)/2]为您的玩具示例执行此操作非常简单 - 将pnr的映射转换为适合sample的集合可能是现实案例不太容易。