Hogyan lehet irányítani a randomizálót az R-ben

Mi történik, ha egy bizonyos típusú randomizálásra van szükség?

A véletlenszám-generálás áttekintése R-ben

R-nek legalább 20 véletlenszám-generátor funkciója van. Mindegyik egy adott valószínűségeloszlást használ a számok létrehozásához. Mindegyikhez meg kell adni a kívánt véletlenszerű számok számát (a fenti képen 200 látható). Mindegyik R alapban kapható - nincs szükség csomagra.

Általános véletlenszám-generátor eloszlások:

  • normál (rnorm): alapértelmezett átlag 0 és szórás 1
  • binomiális (rbinom): nincs alapértelmezés, adja meg az egyes kísérletek számát és a siker valószínűségét
  • uniform (runif): alapértelmezett minimális értéke 0, maximális értéke 1

A fenti három közül csak a binomiális véletlenszám-generátor hoz létre egész számokat.

Miért kell véletlen számokat létrehozni?

A véletlenszámokkal kapcsolatos problémák nagyon gyakoriak - a Stack Exchange-ben körülbelül 50 000 kérdés merül fel a véletlenszámokkal kapcsolatban.

De miért használja őket?

A véletlenszámoknak sok gyakorlati alkalmazása van. Monte Carlo szimulációkban használják őket. A kriptográfiában használják őket. CAPTCHA tartalom előállítására használták fel őket. Nyerőgépekben használják őket. Hétköznapibb feladatokhoz is használták őket, például véletlenszerű rendezési sorrend létrehozására a rendezett adatok tömbjéhez.

Véletlenszámokkal kapcsolatos problémák

Gyakori kérdések: „a véletlenszerű számaim véletlenszerűek?” és "hogyan generálhatok nem ismétlődő véletlen számokat?"

Megjegyzés : ez utóbbi csökkenti a véletlenszerűséget, mert a véletlenszerű számok populációja minden egyesével csökken, ha véletlenszerű számot rajzolunk. A módszer olyan helyzetekben megfelelő, mint a lottó vagy a bingó, ahol minden jegyet vagy labdát csak egyszer lehet kihúzni.

Ez a probléma újabb problémát jelent! A véletlenszerűen generált, pótszámok nélküli mintavételnek egész számnak kell lennie. Senkinek nincs jegye 5,6932 vagy bingógolyó 0,188967.

Gyakorlati példa a véletlenszám-problémákra

Vegyük azt a példát, hogy nekem 20 egyidős diáklányom van. Négy tanítási módszerem van, amelyeket ki akarok próbálni. Csak egy tanítási módszert szeretnék kipróbálni minden hallgató számára. Könnyű matematika - öt csoportra van szükségem.

De hogyan tegyem ezt úgy, hogy minden tanuló véletlenszerűen legyen kijelölve?

És hogyan győződhetek meg arról, hogy csak egész számokat állítok elő?

És hogyan tegyem mindezt úgy, hogy véletlenszerűen generált számokat használok pótlás nélkül? Nem akarok például hat diákot egy csoportba, négyet pedig egy másikba.

Először létre kell hoznom néhány próbabábu adatot, az R.-ben. Készítsük el azt a gúnyos női hallgatók listáját.

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

Most 20 diákunk egydimenziós adatkészlete van.

Tudjuk, hogy a runif()függvény nem hoz létre egész számokat. Miért nem kerekítjük a véletlenszerű számokat úgy, hogy csak egész számokat kapjunk és használjuk ezt a függvényt? A véletlen számot kerekítő függvénybe csomagolhatjuk.

1. kérdés: miért használom a véletlenszerű egyenletes eloszlást, és nem egy másikat, például a véletlenszerű normál eloszlást?

Az R-ben ötféle kerekítési funkció létezik round().

Ahhoz, hogy ugyanazokat az eredményeket kapjuk, beállítok egy magot a véletlenszám-generáláshoz. Valahányszor véletlenszámokat generálunk, ugyanazt a magot fogjuk használni. Úgy döntöttem, hogy az 5-ös a mag. Ha nem állít be magot, vagy ha 5-től eltérő magot állít be, akkor az eredményei eltérnek az enyémtől.

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

Nos, úgy tűnt, hogy ez működik. Minden hallgatót 1 és 5 közötti csoportba osztunk.

Ellenőrizzük még egyszer az elosztásunkat.

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

Stoppol. Az öt csoport közül csak az egyiknek van megfelelő hallgatói létszáma (4. csoport). Miért történt ez?

Ellenőrizhetjük a ténylegesen kiadott számokat runif()kerekítés nélkül, és hagyjuk, hogy a kimenet a konzolra nyomtasson. Itt a kimenet azért nyomtat, mert nem rendeltem egy objektumhoz a függvényt (például egy data.frame változóhoz).

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

Mint láthatjuk, a kerekítés okozta a problémánkat. De ha nem kerekítettünk volna, minden hallgatót más csoportba osztottak volna be.

Mit csináljunk?

minta()

sample() most az egyik kedvenc funkcióm az R.-ben. Nézzük meg, hogyan működik.

Véletlenszerű felosztás egyenlő méretű csoportokba (számít az anyag)

Hogyan használhatjuk 20 diákunk véletlenszerű kiválasztására négy egyforma méretű csoportba?

Mi történik, ha sample()normálisan próbálkozunk ?

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

2. kérdés: milyen eredményt ért el, amikor használta 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).