Muestra n filas aleatorias por grupo en un dataframe

A partir de estas preguntas: muestra aleatoria de filas del subconjunto de un dataframe R y muestras de filas aleatorias en el dataframe. Puedo ver fácilmente cómo seleccionar aleatoriamente (seleccionar) ‘n’ filas de un df o ‘n’ filas que se originan en un nivel específico de un factor dentro de un df.

Aquí hay algunos datos de muestra:

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. 

Por ejemplo, simplemente muestree 3 filas aleatorias del color ‘rosa’ – usando la library(kimisc) :

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

o escribiendo una función personalizada:

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

Sin embargo, quiero muestrear 3 (o n) filas aleatorias de cada nivel del factor. Es decir, el nuevo df tendría 12 filas (3 de azul, 3 de rojo, 3 de amarillo, 3 de rosa). Obviamente, es posible ejecutar esto varias veces, crear nuevos dfs para cada color y luego unirlos, pero estoy buscando una solución más simple.

Puede asignar una identificación aleatoria a cada elemento que tenga un nivel de factor particular utilizando ave . Luego puede seleccionar todas las identificaciones aleatorias en un rango determinado.

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

Esto tiene la ventaja de preservar el orden de fila original y los nombres de fila si eso es algo que le interesa. Además, puede reutilizar el vector rndid para crear subconjuntos de diferentes longitudes con bastante facilidad.

En las versiones de dplyr 0.3 y posteriores, esto funciona bien:

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

Versiones anteriores de dplyr (versión < = 0.2)

Me puse a responder esto usando dplyr , suponiendo que esto funcionaría:

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

Pero resulta que en 0.2 existe el método sample_n.grouped_df S3 pero no está registrado en el archivo NAMESPACE, por lo que nunca se envía. En cambio, tuve que hacer esto:

 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 

Presumiblemente, esto se solucionará en una actualización futura.

Consideraría mi función stratified , que actualmente está alojada como GitHub Gist.

Consíguelo con:

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

Y úsala con:

 stratified(df, "color", 3) 

Hay varias características diferentes que son convenientes para el muestreo estratificado. Por ejemplo, también puede tomar una muestra de “sobre la marcha”.

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

Para darle una idea de lo que hace la función, aquí están los argumentos para stratified :

  • df : La entrada data.frame
  • group : un vector de caracteres de la columna o columnas que componen los “estratos”.
  • size : el tamaño de muestra deseado.
    • Si el size es un valor menor que 1, se toma una muestra proporcional de cada estrato.
    • Si el size es un entero único de 1 o más, se toma esa cantidad de muestras de cada estrato.
    • Si el size es un vector de números enteros, se toma el número especificado de muestras para cada estrato. Se recomienda que use un vector con nombre . Por ejemplo, si tiene dos estratos, “A” y “B”, y quiere 5 muestras de “A” y 10 de “B”, debe ingresar size = c(A = 5, B = 10) .
  • select : Esto le permite subconjuntos de los grupos en el proceso de muestreo. Esta es una list Por ejemplo, si su variable de group era “Grupo”, y contenía tres estratos, “A”, “B” y “C”, pero solo quería tomar muestras de “A” y “C”, puede usar select = list(Group = c("A", "C")) .
  • replace : para muestreo con reemplazo.

Aquí hay una solución. Dividimos un data.frame en grupos de colores. De cada uno de esos grupos, probamos 3 filas. En resultado, obtenemos una lista de data.frames.

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

Entonces, la lista de data.frames debe fusionarse 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 

Aquí hay una forma, en la base, que permite múltiples grupos y muestreo con reemplazo:

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

Para agregar otro grupo, inclúyalo en el argumento 'por' para agregar.