web-dev-qa-db-fra.com

Échantillon n lignes aléatoires par groupe dans une base de données

A partir de ces questions - Échantillon aléatoire de lignes d'un sous-ensemble d'une R dataframe & Exemples de lignes aléatoires dans dataframe I permet de voir facilement 'lignes qui proviennent d'un niveau spécifique d'un facteur dans un df.

Voici quelques exemples de données:

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.

Pour par exemple échantillonnez simplement 3 lignes aléatoires de la couleur 'rose' - en utilisant library(kimisc):

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

ou écrire une fonction personnalisée:

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

Cependant, je veux échantillonner 3 (ou n) lignes aléatoires de chaque niveau du facteur. C'est à dire. la nouvelle df aurait 12 rangées (3 de bleu, 3 de rouge, 3 de jaune, 3 de rose). Il est évidemment possible de l'exécuter plusieurs fois, de créer newdfs pour chaque couleur, puis de les lier entre elles, mais je cherche une solution plus simple.

22
jalapic

Vous pouvez affecter un ID aléatoire à chaque élément ayant un niveau de facteur particulier en utilisant ave. Ensuite, vous pouvez sélectionner tous les identifiants aléatoires dans une certaine plage.

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

Cela présente l'avantage de préserver l'ordre et le nom des lignes d'origine si cela vous intéresse. De plus, vous pouvez réutiliser le vecteur rndid pour créer assez facilement un sous-ensemble de longueurs différentes.

5
MrFlick

Dans les versions de dplyr 0.3 et versions ultérieures, cela fonctionne parfaitement:

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

Anciennes versions de dplyr (version <= 0.2)

J'ai décidé de répondre à cela en utilisant dplyr , en supposant que cela fonctionnerait:

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

Mais il se trouve qu'en 0.2 la méthode sample_n.grouped_df S3 existe mais qu'elle n'est pas enregistrée dans le fichier NAMESPACE, elle n'est donc jamais distribuée. Au lieu de cela, je devais faire ceci:

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

Vraisemblablement, cela sera corrigé dans une future mise à jour.

25
joran

Je considérerais ma fonction stratified , qui est actuellement hébergée en tant que GitHub Gist.

Obtenez le avec:

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

Et utilisez-le avec:

stratified(df, "color", 3)

Plusieurs caractéristiques différentes sont pratiques pour l’échantillonnage stratifié. Par exemple, vous pouvez également prendre un échantillon "à la volée".

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

Pour vous donner une idée de ce que fait la fonction, voici les arguments de stratified:

  • df: L'entrée data.frame
  • group: Un vecteur de caractère de la colonne ou des colonnes qui constituent les "strates".
  • size: La taille d'échantillon souhaitée .
    • Si size est une valeur inférieure à 1, un échantillon proportionnel est tiré de chaque strate.
    • Si size est un entier unique de 1 ou plus, ce nombre d'échantillons est pris dans chaque strate.
    • Si size est un vecteur d’entiers, le nombre d’échantillons spécifié est prélevé pour chaque strate. Il est recommandé d'utiliser un vecteur named. Par exemple, si vous avez deux strates, "A" et "B", et que vous voulez 5 échantillons de "A" et 10 de "B", vous devez entrer size = c(A = 5, B = 10).
  • select: Cela vous permet de sous-définir les groupes dans le processus d'échantillonnage. Ceci est une list. Par exemple, si votre variable group était "Groupe" et qu'elle contenait trois strates, "A", "B" et "C", mais que vous souhaitiez uniquement échantillonner à partir de "A" et "C", vous pouvez utiliser select = list(Group = c("A", "C")) .
  • replace: Pour échantillonnage avec remplacement.
7

Voici une solution. Nous divisons un data.frame en groupes de couleurs. Ensuite, nous échantillonnons 3 lignes de chaque groupe. Cela donne une liste de data.frames.

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

Pour obtenir le résultat souhaité, nous fusionnons la liste de data.frames en 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
6
gagolews

Voici un moyen, en base, qui permet plusieurs groupes et échantillonnages avec remplacement:

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),]

Pour ajouter un autre groupe, incluez-le dans l'argument 'by' à agréger. 

0
user3357177