Amostra n linhas aleatórias por grupo em um dataframe

A partir dessas perguntas – Amostra aleatória de linhas do subconjunto de um dataframeR e Amostragem de linhas aleatórias no dataframe Eu posso ver facilmente como aleatoriamente amostrar (selecionar) ‘n’ linhas de um df, ou ‘n’ linhas que se originam de um nível específico de um fator dentro de um df.

Aqui estão alguns dados de amostra:

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 exemplo, apenas amostra 3 linhas aleatórias de cor ‘rosa’ – usando library(kimisc) :

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

ou escrevendo uma function personalizada:

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

No entanto, quero amostrar 3 (ou n) linhas aleatórias de cada nível do fator. Ou seja, o novo df teria 12 linhas (3 de azul, 3 de vermelho, 3 de amarelo, 3 de rosa). Obviamente, é possível executar isso várias vezes, criar novos PDFs para cada cor e juntá-los, mas estou procurando uma solução mais simples.

Você pode atribuir um ID random a cada elemento que tenha um nível de fator específico usando ave . Então você pode selecionar todos os IDs randoms em um determinado intervalo.

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

Isso tem a vantagem de preservar a ordem original da linha e os nomes das linhas, se isso for algo de seu interesse. Além disso, você pode reutilizar o vetor rndid para criar subconjuntos de diferentes comprimentos com bastante facilidade.

Nas versões do dplyr 0.3 e posteriores, isso funciona bem:

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

Versões antigas do dplyr (versão <= 0.2)

Eu decidi responder isso usando dplyr , assumindo que isso funcionaria:

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

Mas acontece que, em 0.2, o método sample_n.grouped_df S3 existe, mas não está registrado no arquivo NAMESPACE, portanto nunca é despachado. Em vez disso, eu tive que fazer isso:

 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 

Presumivelmente isso será corrigido em uma atualização futura.

Eu consideraria minha function stratified , que atualmente é hospedada como um GitHub Gist.

Obtê-lo com:

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

E use-o com:

 stratified(df, "color", 3) 

Existem vários resources diferentes que são convenientes para amostragem estratificada. Por exemplo, você também pode pegar uma amostra de “on the fly”.

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

Para lhe dar uma ideia do que a function faz, aqui estão os argumentos para stratified :

  • df : o input data.frame
  • group : um vetor de caractere da coluna ou colunas que compõem os “estratos”.
  • size : o tamanho da amostra desejada.
    • Se size é um valor menor que 1, uma amostra proporcional é retirada de cada estrato.
    • Se size é um inteiro único de 1 ou mais, esse número de amostras é retirado de cada estrato.
    • Se size é um vetor de inteiros, o número especificado de amostras é obtido para cada estrato. Recomenda-se que você use um vetor nomeado . Por exemplo, se você tem dois estratos, “A” e “B”, e você queria 5 amostras de “A” e 10 de “B”, você digitaria size = c(A = 5, B = 10) .
  • select : Isto permite subconjunto dos grupos no processo de amostragem. Esta é uma list . Por exemplo, se sua variável de group era “Grupo” e continha três estratos, “A”, “B” e “C”, mas você só queria fazer uma amostra de “A” e “C”, você pode usar select = list(Group = c("A", "C")) .
  • replace : para amostragem com substituição.

Aqui está uma solução. Nós dividimos um data.frame em grupos de colors. De cada um desses grupos, amostramos 3 linhas. Em resultado, obtemos uma lista de data.frames.

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

Em seguida, a lista de data.frames deve ser mesclada em 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 

Aqui está uma maneira, na base, que permite vários grupos e amostragem com substituição:

 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 adicionar outro grupo, inclua-o no argumento 'por' para agregar.