web-dev-qa-db-fra.com

exemples de lignes de sous-groupes de la trame de données avec dplyr

Si je veux sélectionner au hasard des échantillons de différents groupes, j'utilise le package plyr et le code ci-dessous

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

iris.sample<-ddply(iris,.(Species),function(df) sampleGroup(df,10))

Ici, 10 échantillons sont sélectionnés pour chaque espèce.

Certains de mes cadres de données sont très volumineux et ma question est la suivante: puis-je utiliser la même fonction sampleGroup avec le package dplyr? Ou existe-t-il une autre façon de faire la même chose dans dplyr?

MODIFIER

La version 0.2 du package dplyr a introduit deux nouvelles fonctions pour sélectionner des lignes aléatoires dans une table sample_n et sample_frac

30
Robert

Oui, vous pouvez utiliser dplyr avec élégance par la fonction do (). Voici un exemple:

mtcars %>% 
    group_by(cyl) %>%
    do(sample_n(.,2))

et les résultats sont comme ça

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

Mise à jour:

La fonction do n'est plus nécessaire pour sample_n dans les nouvelles versions de dplyr. Code actuel pour prélever un échantillon aléatoire de deux lignes par groupe:

mtcars %>% 
    group_by(cyl) %>% 
    sample_n(2)
57
PhilChang

C'est facile à faire avec data.table, et utile pour une grande table.

REMARQUE: Comme mentionné dans les commentaires de Troy, il existe un moyen plus efficace de le faire en utilisant data.table, mais je voulais respecter la fonction d'échantillon OP et le format dans la réponse.

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]
8
marbel

C'est une bonne question! Je ne vois pas de moyen facile de le faire avec la syntaxe documentée pour dplyr mais qu'en est-il pour une solution de contournement?

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

MODIFIER - COMPARAISON DES PERFORMANCES

Voici un test contre l'utilisation de data.table (à la fois natif et avec un appel de fonction selon l'exemple) pour 1 m de lignes, 26 groupes.

Le data.table natif est environ 2 fois plus rapide que la solution de contournement dplyr et également que l'appel data.table avec la légende. Donc probablement dplyr/data.table sont à peu près les mêmes performances.

J'espère que les gars de dplyr nous donneront bientôt une syntaxe native pour l'échantillonnage! (ou encore mieux, c'est peut-être déjà là)

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 
7
Troy