Cómo controlar su aleatorizador en R

¿Qué sucede cuando necesita un tipo particular de aleatorización?

Descripción general de la generación de números aleatorios en R

R tiene al menos 20 funciones generadoras de números aleatorios. Cada uno usa una distribución de probabilidad específica para crear los números. Todos requieren que especifique la cantidad de números aleatorios que desea (la imagen de arriba muestra 200). Todos están disponibles en la base R, no se requieren paquetes.

Las distribuciones comunes del generador de números aleatorios son:

  • normal (rnorm): media predeterminada de 0 y desviación estándar de 1
  • binomial (rbinom): sin valores predeterminados, especifique el número de ensayos y la probabilidad de éxito en cada ensayo
  • uniform (runif): valor mínimo predeterminado de 0 y valor máximo de 1

De los tres anteriores, solo el generador de números aleatorios binomiales crea enteros.

¿Por qué crear números aleatorios?

Los problemas que involucran números aleatorios son muy comunes: hay alrededor de 50,000 preguntas relacionadas con números aleatorios en Stack Exchange.

Pero, ¿por qué usarlos?

Los números aleatorios tienen muchas aplicaciones prácticas. Se utilizan en simulaciones de Monte Carlo. Se utilizan en criptografía. Se han utilizado para producir contenido CAPTCHA. Se utilizan en máquinas tragamonedas. También se han utilizado para tareas más mundanas, como crear un orden de clasificación aleatorio para una matriz de datos ordenados.

Problemas con números aleatorios

Las preguntas comunes incluyen "¿son mis números aleatorios realmente aleatorios?" y "¿cómo puedo generar números aleatorios no repetidos?"

Nota : este último disminuye la aleatoriedad, porque la población de posibles números aleatorios se reduce en uno cada vez que se extrae un número aleatorio. El método es apropiado en situaciones como loterías o bingo, donde cada boleto o bola solo se puede sacar una vez.

¡Este problema trae otro problema! El muestreo generado aleatoriamente sin números de reemplazo debe ser entero. Nadie tiene el boleto 5.6932 o la bola de bingo 0.18967.

Un ejemplo práctico de problemas de números aleatorios

Tomemos el ejemplo de que tengo 20 alumnas de la misma edad. Tengo cuatro métodos de enseñanza que quiero probar. Solo quiero probar un método de enseñanza para cada alumno. Matemáticas sencillas: necesito cinco estudiantes en cada grupo.

Pero, ¿cómo hago esto para que cada estudiante sea asignado al azar?

¿Y cómo me aseguro de que solo se produzcan números enteros?

¿Y cómo hago todo esto mientras uso números generados aleatoriamente sin reemplazo? No quiero, por ejemplo, seis estudiantes en un grupo y cuatro estudiantes en otro.

Primero, necesito crear algunos datos ficticios, en R. Creemos esa lista de estudiantes simuladas.

FemaleStudents <- data.frame(Names=c("Alice", "Betty", "Carol", "Denise", "Erica", "Frances", "Gina", "Helen", "Iris", "Julie", "Katherine", "Lisa", "Michelle", "Ngaire", "Olivia", "Penelope", "Rachel", "Sarah", "Trudy", "Uma"))

Ahora tenemos un conjunto de datos unidimensional de nuestros 20 estudiantes.

Sabemos que la runif()función no crea números enteros. ¿Por qué no redondeamos los números aleatorios para que solo obtengamos enteros y usemos esta función? Podemos envolver el número aleatorio en una función de redondeo.

Pregunta 1: ¿por qué estoy usando la distribución uniforme aleatoria y no otra, como la distribución normal aleatoria?

Hay cinco tipos de funciones de redondeo en R. Usaremos round().

Para que obtengamos los mismos resultados, estableceré una semilla para la generación de números aleatorios. Cada vez que generamos números aleatorios, usaremos la misma semilla. Me he decidido por el 5 como semilla. Si no establece una semilla, o si establece una semilla que no sea 5, sus resultados serán diferentes a los míos.

set.seed(5)FemaleStudents$Group <- round(runif(20, 1, 5))

Bueno, eso pareció funcionar. Tenemos a cada alumno asignado a un grupo numerado entre 1 y 5.

Revisemos nuestra asignación.

table(FemaleStudents$Group)
1 2 3 4 5 2 6 5 4 3

Maldito. Solo uno de los cinco grupos tiene el número correcto de estudiantes (Grupo 4). ¿Por qué pasó esto?

Podemos verificar los números realmente generados runif()sin redondear y dejando que la salida se imprima en la consola. Aquí, la salida se imprime porque no he asignado la función a un objeto (por ejemplo, a una variable data.frame).

set.seed(5)runif(20,1,5)
[1] 1.800858 3.740874 4.667503 2.137598 1.418601 3.804230 3.111840 4.231741 4.826001 1.441812 2.093140 2.962053 2.273616 3.236691 2.050373[16] 1.807501 2.550103 4.551479 3.219690 4.368718

Como podemos ver, el redondeo causó nuestro problema. Pero si no hubiéramos redondeado, cada estudiante habría sido asignado a un grupo diferente.

qué hacemos?

muestra()

sample() es ahora una de mis funciones favoritas en R. Veamos cómo funciona.

Asignar aleatoriamente a grupos de igual tamaño (los recuentos son importantes)

¿Cómo podemos usarlo para asignar aleatoriamente a nuestros 20 estudiantes a cuatro grupos de igual tamaño?

¿Qué pasa si lo intentamos sample()normalmente?

set.seed(5)FemaleStudents$Sample <- sample(1:5, nrow(FemaleStudents), replace=TRUE)

Pregunta 2: ¿que resultado obtuviste cuando usaste table(FemaleStudents$Sample)?

We can fix this problem by creating a vector of group numbers, and then using sampling without replacement from this vector. The rep command is used to create a range of repeated values. You can use it to repeat each number in the series, as I have used here. Number 1 is repeated four times, then number 2 is repeated four times, and so forth. You can also use it to repeat a sequence of numbers, if you use this code instead: rep(1:5,4)

OurGroups <- rep(1:5, each=4)set.seed(5)FemaleStudents$Sample <- sample(OurGroups, nrow(FemaleStudents), replace=FALSE)

We used our vector of numbers (OurGroups) to allocate our students to groups. We used sampling without replacement (replace=FALSE) from OurGroups because we need to use each value in that vector. We need to remove each value as we use it.

And we get the result we wanted!

table(FemaleStudents$Sample)
1 2 3 4 5 4 4 4 4 4

Question 3: why did I still set a seed?

Another advantage of sample() is that it doesn’t care about type. We can repeat the allocation using a vector of strings. This can be useful if you don’t want to keep referring back to what “1” means.

OurNamedGroups <- rep(c("Up", "Down", "Charmed", "Strange", "Top"), each=4)set.seed(5)FemaleStudents$Sample2 <- sample(OurNamedGroups, nrow(FemaleStudents), replace=FALSE)table(FemaleStudents$Sample2)
Charmed Down Strange Top Up 4 4 4 4 4

Because we used the same seed, we can see that the same student allocation was performed, irrespective of whether we used numeric or character data for the assignment.

table(FemaleStudents$Sample,FemaleStudents$Sample2) Charmed Down Strange Top Up 1 0 0 0 0 4 2 0 4 0 0 0 3 4 0 0 0 0 4 0 0 4 0 0 5 0 0 0 4 0

Randomly allocate when group size is not restricted

Sometimes we want to randomly allocate to groups, but we don’t have a vector of groups. We are still only allocating each unit (person, sheep, block of cheese) to a single group, and we use completely random allocation.

Let’s say that our school has a new, special library room. It’s been constructed to be soundproof to give students a better studying environment. The chief librarian would like to know about the experiences of students in that room. The only problem is that the room is limited in size. The chief librarian thinks that around four students is a large enough group to provide the initial feedback.

Again, we can use sample() to pick our student groups. In this case, we have “students who will test the room” and “students who won’t test the room”. I’m going to call them “Test” and “Not test”. These labels have been chosen for being 1. short and 2. easily distinguished.

Because we did sampling without replacement earlier, we didn’t specify probabilities of assignment to groups — we simply pulled out an assignment from a vector. Now we are going to use sampling with replacement. With replacement refers to the group, not to the students.

We need to sample with replacement as we only have two groups (“Test”, “Not test”) and 20 students. If we tried to sample without replacement, our code would error.

Our code is very similar:

set.seed(5)FemaleStudents$Library <- sample(c("Test", "Not test"), nrow(FemaleStudents), replace=TRUE, prob=c(4/20,16/20))table(FemaleStudents$Library)
Not test Test 15 5

As you can see, we allocated five students to test the room, not four. This type of result is expected when dealing with small samples. However, our allocation of students is completely random. Each student had exactly the same probability of being assigned to test the room. Whether previous students were testers or not had no impact on the allocation of the next student.

Let’s walk through some of that code.

I’ve constructed a new variable in the data.frame to collect the allocation (Library).

Instead of dealing with numbers for group names, I’ve used the strings I mentioned earlier. Because I’ve used strings, the c() must wrap the group names (“Test”, “Not test”) and each group name is separated by a comma.

Replacement has been set to TRUE.

The probability of assignment to either group must be provided. This is the prob=c(4/20,16/20) part of the sample() function. Again, note how c() is used to contain the probabilities. Also of interest is that the probabilities can be expressed as fractions, rather than decimals.

Hooray for sample()

I use sample() all the time for the work I am doing. The ability to use strings, as well as to restrict numeric output to integers (and define the desired integer range), provides me with more control than trying to use one of the random number functions.

Answers

Answer 1: I used a random uniform distribution because I wanted each value to be equally probable.

Answer 2: I got this output:

1 2 3 4 5 2 7 4 2 5

Answer 3: If we don’t set a seed value, or we use a different one, the allocation of specific students will be different. For example, when the seed is 5, Alice is allocated to group 2. If the seed is 7, Alice is allocated to group 5. Replication is important when code needs to be re-run (for example, in testing).