¿Cómo aleatorizar (o permutar) un marco de datos en filas y columnas?

96

Tengo un marco de datos (df1) como este.

     f1   f2   f3   f4   f5
d1   1    0    1    1    1  
d2   1    0    0    1    0
d3   0    0    0    1    1
d4   0    1    0    0    1

La columna d1 ... d4 es el nombre de la fila, la fila f1 ... f5 es el nombre de la columna.

Para hacer la muestra (df1), obtengo un nuevo marco de datos con un recuento de 1 igual que df1. Por lo tanto, el recuento de 1 se conserva para todo el marco de datos, pero no para cada fila o columna.

¿Es posible hacer la aleatorización por filas o por columnas?

Quiero aleatorizar la columna df1 para cada columna, es decir, el número de 1 en cada columna sigue siendo el mismo. y cada columna debe cambiarse al menos una vez. Por ejemplo, puedo tener un df2 aleatorio como este: (Noté que el recuento de 1 en cada columna sigue siendo el mismo, pero el recuento de 1 en cada fila es diferente.

     f1   f2   f3   f4   f5
d1   1    0    0    0    1  
d2   0    1    0    1    1
d3   1    0    0    1    1
d4   0    0    1    1    0

Del mismo modo, también quiero aleatorizar la fila df1 para cada fila, es decir, el no. de 1 en cada fila sigue siendo el mismo, y es necesario cambiar cada fila (pero el número de entradas cambiadas podría ser diferente). Por ejemplo, un df3 aleatorio podría ser algo como esto:

     f1   f2   f3   f4   f5
d1   0    1    1    1    1  <- two entries are different
d2   0    0    1    0    1  <- four entries are different
d3   1    0    0    0    1  <- two entries are different
d4   0    0    1    0    1  <- two entries are different

PD. Muchas gracias por la ayuda de Gavin Simpson, Joris Meys y Chase por las respuestas anteriores a mi pregunta anterior sobre la distribución aleatoria de dos columnas.

a83
fuente
¿Quiere permutar la fila y las columnas al mismo tiempo? Releyendo esto, parece que la restricción de columna (el mismo número de 1 en cada columna) no se mantuvo en su segundo ejemplo de permutación de filas.
Gavin Simpson
1
No se registre para varias cuentas. He pedido a los moderadores para fusionar la cuenta que utilizó aquí con la utilizada en el anterior P.
Gavin Simpson

Respuestas:

233

Dado el R data.frame:

> df1
  a b c
1 1 1 0
2 1 0 0
3 0 1 0
4 0 0 0

Mezclar por filas:

> df2 <- df1[sample(nrow(df1)),]
> df2
  a b c
3 0 1 0
4 0 0 0
2 1 0 0
1 1 1 0

Por defecto, sample()reordena aleatoriamente los elementos pasados ​​como primer argumento. Esto significa que el tamaño predeterminado es el tamaño de la matriz pasada. Pasar el parámetro replace=FALSE(el predeterminado) a sample(...)asegura que el muestreo se realiza sin reemplazo, lo que logra una mezcla inteligente de filas.

Mezclar por columnas:

> df3 <- df1[,sample(ncol(df1))]
> df3
  c a b
1 0 1 1
2 0 1 0
3 0 0 1
4 0 0 0
pms
fuente
5
Creo que es gracioso cómo este no es el comentario principal y, sin embargo, es más simple que ir y aprender sobre algún otro paquete. Eso es cierto para casi cualquier pregunta sobre la permutación. ¡SOLO UTILIZA SAMPLE ()!
Brash Equilibrium
¿Estoy en lo cierto al suponer que este método mantendrá los nombres de filas?
tumultous_rooster
¿Alguna razón para usar = sobre el estándar <- en este caso?
Christian
4
Bueno, esto está cambiando el orden de filas y columnas, pero lo que OP quería es diferente: barajar cada columna / fila de forma independiente
JelenaČuklina
exactamente lo que necesitaba!
ChuckCottrill
18

Esta es otra forma de mezclar el data.framepaquete using dplyr:

en hilera:

df2 <- slice(df1, sample(1:n()))

o

df2 <- sample_frac(df1, 1L)

por columna:

df2 <- select(df1, one_of(sample(names(df1)))) 
Enrique Pérez Herrero
fuente
10

Eche un vistazo permatswap()en el paquete vegano . A continuación, se muestra un ejemplo que mantiene los totales de filas y columnas, pero puede relajarlo y corregir solo una de las sumas de filas o columnas.

mat <- matrix(c(1,1,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,0,1,1), ncol = 5)
set.seed(4)
out <- permatswap(mat, times = 99, burnin = 20000, thin = 500, mtype = "prab")

Esto da:

R> out$perm[[1]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    1    1    1
[2,]    0    1    0    1    0
[3,]    0    0    0    1    1
[4,]    1    0    0    0    1
R> out$perm[[2]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    1    0    1    1
[2,]    0    0    0    1    1
[3,]    1    0    0    1    0
[4,]    0    0    1    0    1

Para explicar la llamada:

out <- permatswap(mat, times = 99, burnin = 20000, thin = 500, mtype = "prab")
  1. times es el número de matrices aleatorias que desea, aquí 99
  2. burnines el número de intercambios realizados antes de empezar a tomar muestras aleatorias. Esto permite que la matriz de la que tomamos muestras sea bastante aleatoria antes de comenzar a tomar cada una de nuestras matrices aleatorias.
  3. thindice que solo tome un sorteo aleatorio en cada thinintercambio
  4. mtype = "prab" dice tratar la matriz como presencia / ausencia, es decir, datos binarios 0/1.

Un par de cosas a tener en cuenta, esto no garantiza que ninguna columna o fila haya sido aleatorizada, pero si burnines lo suficientemente larga, debería haber una buena posibilidad de que eso haya sucedido. Además, puede dibujar más matrices aleatorias de las que necesita y descartar las que no coincidan con todos sus requisitos.

Su requisito de tener diferentes números de cambios por fila tampoco se trata aquí. Nuevamente, puede muestrear más matrices de las que desee y luego descartar las que no cumplan con este requisito también.

Gavin Simpson
fuente
6

también puede usar la randomizeMatrixfunción en el paquete Rpicante

ejemplo:

test <- matrix(c(1,1,0,1,0,1,0,0,1,0,0,1,0,1,0,0),nrow=4,ncol=4)
> test
     [,1] [,2] [,3] [,4]
[1,]    1    0    1    0
[2,]    1    1    0    1
[3,]    0    0    0    0
[4,]    1    0    1    0

randomizeMatrix(test,null.model = "frequency",iterations = 1000)

     [,1] [,2] [,3] [,4]
[1,]    0    1    0    1
[2,]    1    0    0    0
[3,]    1    0    1    0
[4,]    1    0    1    0

randomizeMatrix(test,null.model = "richness",iterations = 1000)

     [,1] [,2] [,3] [,4]
[1,]    1    0    0    1
[2,]    1    1    0    1
[3,]    0    0    0    0
[4,]    1    0    1    0
> 

La opción null.model="frequency"mantiene las sumas de columna y richnessmantiene las sumas de fila. Aunque se utiliza principalmente para aleatorizar conjuntos de datos de ausencia de presencia de especies en ecología comunitaria, funciona bien aquí.

Esta función también tiene otras opciones de modelo nulo, consulte el siguiente enlace para obtener más detalles (página 36) de la documentación picante

Anne Heloise Theo
fuente
4

Por supuesto, puede probar cada fila:

sapply (1:4, function (row) df1[row,]<<-sample(df1[row,]))

barajará las filas en sí, por lo que el número de 1en cada fila no cambia. Pequeños cambios y también funciona muy bien con columnas, pero este es un ejercicio para el lector :-P

binfalse
fuente
2
No hay nada en eso que intente implementar las restricciones que el OP le gustaría imponer.
Gavin Simpson
2

También puede "muestrear" la misma cantidad de elementos en su marco de datos con algo como esto:

nr<-dim(M)[1]
random_M = M[sample.int(nr),]
Marcos
fuente
en lugar de dim(M)[1], puede usar nrow(M)para que todo el procedimiento se convierta en una sola línea:random_M <- M[nrow(M),]
Agile Bean
1

Si el objetivo es mezclar aleatoriamente cada columna, algunas de las respuestas anteriores no funcionan, ya que las columnas se mezclan de forma conjunta (esto preserva las correlaciones entre columnas). Otros requieren la instalación de un paquete. Sin embargo, existe una sola línea:

df2 = lapply(df1, function(x) { sample(x) })
rimorob
fuente
0

Muestras aleatorias y permutaciones en un marco de datos Si está en forma de matriz, conviértalo en data.frame, use la función de muestra del paquete base índices = muestra (1: nrow (df1), tamaño = 1 * nrow (df1)) Muestras aleatorias y permutaciones

Thrinadhn
fuente