Crearea unui test de permutare, în două moduri

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

În ultima mea postare despre permutam-am uitat la seturi de partiții ordonate și noua interfață de formulă pentru parcele și blocuri. De data aceasta vreau să arăt doi asistenți pentru următoarea parte a scrierii unui test de permutare: luând acele permutări și faceți ceva cu ele.

permutations() vă oferă direct valorile amestecate. permutator() vă oferă o funcție care distribuie următorul set de indici de observație de fiecare dată când îl apelați. Ambele folosesc aceeași mașinărie de permutare dedesubt, așa că le putem folosi pentru a construi același test în două moduri ușor diferite.

Aceste funcții sunt în versiunea de dezvoltare a permutei. Pentru a urma, instalează-l din universul meu R:

install.packages("permute", repos = c(
  "https://gavinsimpson.r-universe.dev",
  "https://cloud.r-project.org"
))

Ceva diferit de șacali

Să folosim PlantGrowthcare vine cu R. Înregistrează greutățile plantelor uscate sub un control și două condiții de tratament:

library("permute")
data(PlantGrowth, package = "datasets")

aggregate(weight ~ group, data = PlantGrowth, FUN = mean)
  group weight
1  ctrl  5.032
2  trt1  4.661
3  trt2  5.526

Mijloacele grupului diferă, dar este aceasta mai multă variație decât ne-am aștepta dacă greutatea plantelor nu ar fi legată de tratamente? Putem folosi statistica generală F dintr-un model liniar pentru a măsura cât de diferite sunt grupurile. Valorile mai mari înseamnă mai multă variație între grupuri în raport cu variația în interiorul acestora.

Voi încheia acel calcul într-o funcție mică, pentru că o vom face de câteva ori:

f_stat <- function(weight, group) {
  fit <- lm(weight ~ group)
  unname(summary(fit)$fstatistic(("value")))
}

observed <- with(PlantGrowth, f_stat(weight, group))
observed

Acesta este un test general pentru grupuri. Nu ne spune ce tratamente particulare diferă.

Pentru testul de permutare, vom lăsa etichetele grupului pe loc și vom amesteca greutățile între ele. În conformitate cu ipoteza nulă, presupunem că acele ponderi sunt schimbabile între grupurile de tratament.

Dă-mi valorile amestecate

De obicei, shuffleSet() revine permutate indicipe care apoi îl folosim pentru a ne rearanja datele. permutations() face acel pas de indexare pentru noi:

ctrl <- how(nperm = 9999)

set.seed(42)
shuffled <- permutations(PlantGrowth$weight, control = ctrl)

dim(shuffled)

Fiecare rând conține o versiune amestecată a vectorului de greutate. Coloanele corespund pozițiilor inițiale de observare, așa că putem asocia fiecare rând cu cel neschimbat PlantGrowth$group vector. Iată primele valori din câteva rânduri:

as.matrix(shuffled)(seq_len(3), seq_len(5))
     (,1) (,2) (,3) (,4) (,5)
(1,) 6.03 4.50 4.17 5.37 5.14
(2,) 4.81 5.87 5.12 5.29 4.53
(3,) 5.14 4.53 4.50 4.17 6.03

Acestea sunt greutăți de plante, gata de utilizare. as.matrix() elimină doar metadatele de permutare suplimentare pentru acest mic aspect.

Acum potriviți modelul pentru fiecare rând în shuffled și colectați statisticile F:

null_values <- apply(shuffled, 1, f_stat, group = PlantGrowth$group)

Aceasta este distribuția noastră de permutare. The -valoarea este proporția de rezultate cel puțin la fel de mare ca statistica F observată, inclusiv rezultatul observat în sine:

tol <- sqrt(.Machine$double.eps)
p_value <- (1 + sum(null_values >= observed - tol)) /
  (length(null_values) + 1)

c(F = observed, p_value = p_value)
       F  p_value 
4.846088 0.014200 

Toleranța mică ne permite să numărăm legăturile în ciuda diferențelor numerice minore în potrivirile modelului. Adăugarea unuia la numărător și numitor include statistica observată în acest test Monte Carlo. Numărăm coada superioară deoarece statisticile F mai mari sunt mai extreme; nu este nevoie să dublezi rezultatul pentru un test al diferențelor în ambele direcții.

De asemenea, putem arunca o privire asupra datelor și distribuției de permutare:

old_par <- par(mfrow = c(1, 2))
boxplot(weight ~ group, data = PlantGrowth,
        xlab = "Group", ylab = "Dried plant weight",
        col = "grey85")
hist(null_values, breaks = 40, main = "",
     xlab = "F statistic after shuffling weights",
     col = "grey85", border = "white")
abline(v = observed, col = "firebrick", lwd = 2)
par(old_par)

Două panouri: boxploturi cu greutatea plantelor uscate pentru control și două tratamente și o histogramă a statisticilor F după amestecarea greutăților. Statistica F observată este marcată de o linie roșie în coada din dreapta.Două panouri: boxploturi cu greutatea plantelor uscate pentru control și două tratamente și o histogramă a statisticilor F după amestecarea greutăților. Statistica F observată este marcată de o linie roșie în coada din dreapta.

Greutățile plantelor pe grupe (stânga) și distribuția de permutare a statisticii F (dreapta). Linia roșie marchează statistica F observată.

Valoarea observată se află în coada dreaptă. Pentru acest test de permutare nerestricționat, există dovezi ale diferențelor între grupurile de tratament. Valoarea p este estimată din permutări aleatorii, așa că va varia în funcție de sămânța și numărul pe care îl solicităm.

permutations() funcționează cu un vector atomic, mai degrabă decât cu un întreg cadru de date. Dacă îi dați un factor, returnează etichetele de caractere amestecate. De asemenea, urmează obișnuitul shuffleSet() reguli privind verificarea și enumerarea completă, astfel încât numărul de rânduri returnate poate diferi de numărul cerut pentru spații mici de permutare. Utilizați dimensiunea obiectului returnat în calculele ulterioare, așa cum am făcut mai sus.

O permutare la un moment dat

Uneori o buclă1 este un loc mai convenabil pentru a potrivi un model, pentru a colecta mai multe rezultate sau pentru a urmări progresul. Aceasta este o situație în care permutator() intră.

Apelarea acestuia creează o funcție. Apel că funcția returnează următoarea permutare a indicilor de observație. Voi reseta sămânța, astfel încât să folosim același set pe care l-am folosit mai sus:

set.seed(42)
next_perm <- permutator(nrow(PlantGrowth), control = ctrl)

null_iter <- numeric(attr(next_perm, "nperm"))

for (b in seq_along(null_iter)) {
  i <- next_perm()
  null_iter(b) <- f_stat(PlantGrowth$weight(i), PlantGrowth$group)
}

Aici i conţine indici, deci PlantGrowth$weight(i) face amestecul. Al iteratorului "nperm" atributul ne spune câte permutări sunt de fapt în set; folosim asta pentru a aloca vectorul de rezultate.

După ce am folosit toate permutările, se întoarce un alt apel NULL:

Un iterator își amintește cât de departe ai ajuns. Dacă rulați din nou bucla cu acest iterator epuizat, nu va începe de la capăt; mai întâi creați unul proaspăt.

Cele două abordări ne-au oferit aceleași statistici F?

all.equal(null_values, null_iter)

Au făcut-o. Am resetat sămânța și am folosit același design, astfel încât ambii ajutoare au lucrat prin aceleași permutări ale indexului de bază. Putem calcula -valoarea din buclă rezultă exact în același mod:

p_iter <- (1 + sum(null_iter >= observed - tol)) /
  (length(null_iter) + 1)

c(values = p_value, iterator = p_iter)
  values iterator 
  0.0142   0.0142 

Un detaliu de reținut: permutator() generează și stochează matricea de permutare atunci când creați iteratorul. Distribuie rânduri pe rând, dar nu le generează leneș sau înlătură nevoia de a stoca matricea de index.

Reutilizarea unui set

De asemenea, puteți transmite o matrice de index existentă permutator(). Acest lucru este util atunci când doriți ca mai multe calcule să folosească exact aceleași permutări sau doriți să revedeți mai târziu un set salvat:

set.seed(42)
indices <- shuffleSet(nrow(PlantGrowth), control = ctrl)

next_perm <- permutator(perms = indices)
i <- next_perm()
head(PlantGrowth$weight(i))
(1) 6.03 4.50 4.17 5.37 5.14 6.11

Crearea unui alt iterator peste acea matrice începe de la început:

replay <- permutator(perms = indices)
identical(i, replay())

Aprovizionare fie perms sau argumentele generaţiei precum n, nsetși control. Matricea a trecut la perms trebuie să conțină indici de observație, cum ar fi cei returnați de shuffleSet(). Greutățile amestecate din permutations() nu sunt interschimbabile cu acea matrice.

Pentru un singur vector și un calcul scurt, îmi place permutations() cu apply(). Când calculul se potrivește mai natural într-o buclă sau vreau să reutilizam un set salvat de indici, permutator() este la îndemână. Ambele lasă alegerea designului de permutare cu how()astfel încât același flux de lucru se extinde și la modelele restricționate atunci când datele le solicită. Manualul pachetului conține detalii, iar feedbackul este binevenit cu privire la instrumentul de urmărire a problemelor.

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.