Як керувати своїм рандомізатором у R

Що відбувається, коли потрібен певний тип рандомізації?

Огляд генерації випадкових чисел у R

R має принаймні 20 функцій генератора випадкових чисел. Кожен використовує певний розподіл ймовірностей для створення чисел. Усі вони вимагають вказати кількість випадкових чисел, які ви хочете (на зображенні вище показано 200). Всі вони доступні в базовій версії R - пакунки не потрібні.

Загальними розподілами генераторів випадкових чисел є:

  • нормальний (rnorm): середнє значення за замовчуванням 0 і стандартне відхилення 1
  • біноміальний (rbinom): за замовчуванням не вказано кількість випробувань та ймовірність успіху для кожного випробування
  • рівномірний (runif): мінімальне значення за замовчуванням 0 і максимальне значення 1

Із трьох наведених вище лише двовимірний генератор випадкових чисел створює цілі числа.

Навіщо створювати випадкові числа?

Проблеми, пов’язані з випадковими числами, дуже поширені - на біржі стеків є близько 50 000 питань, що стосуються випадкових чисел.

Але навіщо їх використовувати?

Випадкові числа мають багато практичних застосувань. Вони використовуються в моделюванні Монте-Карло. Вони використовуються в криптографії. Вони були використані для створення вмісту CAPTCHA. Вони використовуються в ігрових автоматах. Вони також використовувались для більш звичних завдань, таких як створення порядку довільного сортування для масиву впорядкованих даних.

Проблеми з випадковими числами

Поширені запитання включають "чи мої випадкові числа насправді випадкові?" і "як я можу генерувати повторювані випадкові числа?"

Примітка : останнє зменшує випадковість, оскільки сукупність можливих випадкових чисел зменшується на одиницю кожного разу, коли вимальовується випадкове число. Метод доречний у таких ситуаціях, як лотереї або бінго, коли кожен квиток або м’яч можна розіграти лише один раз.

Ця проблема породжує ще одну проблему! Випадково згенерована вибірка без заміщення чисел має бути цілими числами. Ні в кого немає квитка 5.6932 або м'яча для бінго 0.18967.

Практичний приклад задач із випадковими числами

Візьмемо приклад, що у мене 20 студенток того ж віку. У мене є чотири методи навчання, які я хочу випробувати. Я хочу випробувати лише один метод навчання для кожного студента. Легка математика - мені потрібно п’ять учнів у кожній групі.

Але як я можу це зробити, щоб кожного учня було довільно призначено?

І як я можу переконатись, що у мене є лише цілі числа?

І як мені зробити все це, використовуючи випадково сформовані номери без заміни? Я не хочу, наприклад, шістьох учнів в одній групі та чотирьох учнів в іншій.

По-перше, мені потрібно створити кілька фіктивних даних, у R. Давайте створимо цей список фіктивних студенток.

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"))

Зараз у нас є одновимірний набір даних з наших 20 студентів.

Ми знаємо, що runif()функція не створює цілих чисел. Чому б нам не округлити випадкові числа так, щоб отримати лише цілі числа і використовувати цю функцію? Ми можемо обернути випадкове число у функцію округлення.

Питання 1: чому я використовую випадковий рівномірний розподіл, а не інший, такий як випадковий нормальний розподіл?

У R. існує п’ять типів функцій округлення. Ми будемо використовувати round().

Щоб ми отримали однакові результати, я встановлю насіння для генерації випадкових чисел. Кожного разу, коли ми генеруємо випадкові числа, ми будемо використовувати одне і те ж насіння. Я вибрав 5 як насіння. Якщо ви не встановите насіння або якщо ви встановите насіння, крім 5, ваші результати будуть відрізнятися від моїх.

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

Ну, здавалося, це спрацювало. Ми маємо кожного студента, який розподіляється до групи з номерами від 1 до 5.

Перевіримо розподіл.

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

Проклятий. Лише одна з п’яти груп має правильну кількість учнів (група 4). Чому це сталося?

Ми можемо перевірити фактично виведені числа, runif()не округлюючи і не даючи виводу надрукувати на консолі. Тут вивід друкується, оскільки я не призначив функцію об’єкту (наприклад, змінній 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

Як бачимо, округлення спричинило нашу проблему. Але якби ми не округлили, кожного учня було б віднесено до іншої групи.

Що ми робимо?

зразок ()

sample() зараз є однією з моїх улюблених функцій у R. Давайте подивимось, як це працює.

Випадковим розподілом до однаково великих груп (кількість має значення)

Як ми можемо використовувати його для випадкового розподілу наших 20 учнів до чотирьох однаково великих груп?

Що станеться, якщо ми спробуємо sample()нормально?

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

Питання 2: який результат ви отримали, коли використовували 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).