Visualizzazione post con etichetta ANOVA. Mostra tutti i post
Visualizzazione post con etichetta ANOVA. Mostra tutti i post

domenica 30 ottobre 2011

Test di Friedman: confronti multipli non parametrici

Il test di Friedman viene usato per comparare tra loro più di due campioni dipendenti, in termini di popolazione. Il confronto che viene fatto si basa sulla mediana della popolazione.
La statistica test (Fr) viene calcolata con le seguenti due formule.


La prima si applica se non ci sono ripetizioni, mentre la seconda nel caso in cui ci sono ripetizioni nella assegnazione del rango.





Nella formula, n è il numero di righe nella tabella dei dati; k è il numero delle colonne (o condizioni); Ri è la somma dei ranghi per ciascuna colonna; Cf è la correzione apportata nel caso, appunto, in cui siano presenti delle ripetizioni dei ranghi, ed equivale a (1/4)nk(k+1)^2; rij è il rango assegnato a ciascuna cella della tabella.
I gradi di libertà sono pari a df = k-1.
La statistica test segue una distribuzione di Friedman; per campioni molto grandi (molti gruppi, o molti elementi per gruppo) allora si usa la distribuzione Chi-quadro, per la scelta del valore critico.

Consideriamo i seguenti esempi.




Il gestore di un locale ha 7 dipedenti, i quali sono sempre ritardatari. Per cercare di risolvere tale problema, adotta due strategie. Durante il primo mese (di strategia) decide di premiare i dipendenti che non fanno ritardo; il mese successivo invece punisce i dipedenti che ritardano. Raccoglie gli orari di arrivo (come differenza tra l'ora d'inizio del lavoro, e l'ora di arrivo), ne calcola le medie, e ottiene una tabella come la seguente:



Il gestore vuole sapere se uno dei due metodi applicati apporti un cambiamento nei ritardi dei suoi dipedenti. Le ipotesi del problema sono quindi:



Per prima cosa, assegnamo il rango a ciascuna cella, disponendo in ordine crescente le celle in base alla riga. Otteniamo la seguente tabella:



Calcoliamo ora la somma di ciascuna colonna della riga, ottenendo:



Applicando la prima equazione, con n=7 e k=3; otteniamo il valore:



Il valore critico, per n=7 e k=3 è:



Poichè quindi il valore calcolato è inferiore al valore tabulato, non possiamo rifiutare l'ipotesi nulla.




Un allenatore di pallavolo di 12 ragazzi vuole sperimentare due nuovi tipi di allenamento. Per valutarne l'efficacia, usa come parametro il numero di piegamenti che ciascun ragazzo riesce a fare in un minuto. Raccoglie i dati in una tabella: la prima colonna contiene il numero (medio) di piegamenti che i ragazzi riescono a fare di base (prima degli allenamenti), mentre le altre due colonne fanno riferimento ai risultati ottenuti dopo i due nuovi tipi di allenamento, durante il primo e il secondo mese.



A questi valori vengono assegnati i seguenti ranghi. Poichè sono presenti delle ripetizioni, le celle con i valori ripetuti conterranno la media dei ranghi:



Le ipotesi del problema sono:



Applichiamo la formula per calcolare il valore di F, con la correzione per le ripetizioni:



Essendo i dati numerosi, possiamo confrontare tale valore con la distribuzione Chi-quadro, a 2 gradi di libertà. Poichè il valore tabulato è stavolta inferiore al valore calcolato, concludiamo che la mediana di uno (o più) dei gruppi considerati è significativamente diversa (p < 0.05).
Per capire adesso quali confronti risultano significativi, possiamo applicare il Wilcoxon signed rank test.

Con questo test confrontiamo i 3 gruppi, a coppie. Poichè stiamo facendo un confronto multiplo, applichiamo la correzione di Bonferroni, impostando quindi alpha = 0.05 / 3 = 0.0167.
Lascio a voi il prosieguo dei calcoli. Qui riporto soltanto i risultati:

  • Baseline - mese1: non c'è differenza (p > 0.0167)

  • Baseline - mese2: i due gruppi sono differenti (p < 0.0167)

  • Mese1 - mese2: i due gruppi sono differenti (p < 0.0167)



Qual è quindi il tipo di allenamento migliore? A voi la risposta.






Risolviamo i due problemi con R.

Problema 1:


mydata <- matrix(c(
16, 13, 12,
10, 5, 2,
7, 8, 9,
13, 11, 5,
17, 2, 6,
10, 7, 9,
11, 6, 7),
nrow = 7,
byrow = T,
dimnames = list(1 : 7,
c("Baseline", "Mese 1", "Mese 2")))

friedman.test(mydata)




Friedman rank sum test

data: mydata
Friedman chi-squared = 5.4286, df = 2,
p-value = 0.06625





Problema 2:


mydata <- matrix(c(
66, 67, 69,
49, 50, 56,
51, 52, 49,
65, 65, 69,
42, 43, 46,
38, 39, 40,
33, 31, 39,
41, 41, 44,
46, 47, 48,
45, 46, 46,
36, 33, 34,
51, 55, 67),
nrow = 12,
byrow = T,
dimnames = list(1 : 12,
c("Baseline", "Mese 1", "Mese 2")))

friedman.test(mydata)




Friedman rank sum test

data: mydata
Friedman chi-squared = 10.9778, df = 2,
p-value = 0.004132


martedì 6 aprile 2010

Repeated measures ANOVA: confronto tra gruppi di misure ripetute

Supponiamo di voler analizzare una certa variabile in n individui, e che questa variabile venga misurata più di 2 volte per ogni soggetto (ad esempio la pressione arteriosa prima del trattamento, durante il trattamento e al termine del trattamento farmacologico). E' intuitivo che i valori misurati siano tra loro appaiati, essendo stati misurati sugli stessi soggetti. Se avessimo effettuato solo 2 misurazione, avremmo potuto effettuare il confronto tra i due gruppi con un paired t-test. Avendo qui più di due gruppi, dobbiamo ricorrere invece alla ANOVA per misure ripetute.

Il modello più semplice che ora vedremo per primo, considera la presenza di un solo fattore, entro cui ciascun soggetto viene misurato più volte. Si parla di modelli within, per distinguerli dai modelli between a 1 solo fattore (che si analizzano con la classica ANOVA per campioni indipendenti).
Dire one factor within subject ANOVA (o within subject one-way ANOVA) significa proprio eseguire una ANOVA a 1 fattore di classificazione, con misure ripetute all'interno (within) dello stesso fattore.

La formula generale del modello between è la seguente:

In altri termini, ciascuna misurazione può essere spiegata considerando la media generale (Grand Mean, in inglese), l'effetto del trattamento e l'errore individuale (che dipende dal trattamento e dal soggetto).

Mentre la formula generale del modello within one-way é:

In questo modello è stato aggiunto il parametro che tiene conto delle caratteristiche intrinseche del soggetto e cioè della variabilità individuale.

In termini di varianza, nel modello a campioni indipendenti abbiamo:

Ossia la varianza totale è pari alla somma della varianza che dipende dal trattamento, più la varianza d'errore (o residua), che non viene spiegata dal modello.

Nel modello within invece abbiamo:

Quindi la varianza totale dipende dai parametri di prima, più la varianza dei soggetti (ossia la variabilità individuale che dicevamo prima).
La somma è la varianza within-subjects.

Considerando i gradi di libertà di ciascuna varianza (per i quali c'è una dimostrazione, che però non riporto qui), possiamo scrivere la tabella ANOVA per il modello a misure ripetute:





Cominciamo adesso l'esercizio, e cerchiamo dapprima di svolgerlo by-hand; in seguito passeremo alle funzioni di R. Supponiamo di avere la seguente tabella


Subject time1 time2 time3
1 45 20 30
2 56 18 29
3 59 10 24
4 49 15 25


Un certo parametro è stato misurato in 4 soggetti, e ciascun soggetto è stato sottoposto a 3 misurazioni, in 3 tempi successivi. Ci chiediamo se la variabile tempo influisce sul parametro in considerazione.

Cominciamo a calcolare i vari parametri che ci servono:













Abbiamo ora completato i calcoli (la SS residuals non l'ho calcolata manualmente, perchè i calcoli sono troppo lunghi e noiosi). I valori F-calcolati vanno confrontati con gli F-tabulati ai rispettivi gradi di libertà. Risulta comunque immediato che
- F_soggetti è minore di F_calcolato: quindi non c'è differenza significativa tra i soggetti
- F_fattore tempo è maggiore di F_calcolato: quindi il fattore tempo influenza il parametro che abbiamo calcolato (in pratica c'è differenza tra le colonne della tabella esaminata).

Cerchiamo adesso di ottenere la stessa tabella ANOVA in R.
Innanzitutto scriviamo i dati, sottoforma di dataframe:


subj <- rep(1:4, each=3)
time <- rep(c("time1", "time2", "time3"), 4)
weights <- c(45, 20, 30, 56, 18, 29, 59, 10, 24, 49, 15, 25)
mydata <- data.frame(factor(subj), factor(time), weights)
names(mydata) <- c("subj", "time", "weights")

> mydata
subj time weights
1 1 time1 45
2 1 time2 20
3 1 time3 30
4 2 time1 56
5 2 time2 18
6 2 time3 29
7 3 time1 59
8 3 time2 10
9 3 time3 24
10 4 time1 49
11 4 time2 15
12 4 time3 25


Adesso utilizziamo la funzion aov() (aov sta per Analysis Of Variance), facendo attenzione alla formula da specificare:


myANOVA <- aov(weights ~ time + Error(subj/time), mydata)


Possiamo "tradurre" la formula specificata, in questo modo:
Effettuare il confronto dei parametri weights ordinati nelle colonne relative ai vari time, considerando che le misurazioni dei vari soggetti sono ripetute nella colonna time.

L'output è il seguente:


summary(myANOVA)

Error: subj
Df Sum Sq Mean Sq F value Pr(>F)
Residuals 3 34.667 11.556

Error: subj:time
Df Sum Sq Mean Sq F value Pr(>F)
time 2 2795.17 1397.58 49.086 0.0001911 ***
Residuals 6 170.83 28.47
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1


Abbiamo ottenuto il calcolo si SS, MS e del valore F calcolato sulle colonne time. A meno di approssimazioni, i calcoli risultano tutti corretti, e i 3 asterischi accanto al p-value indicano che la differenza tra i gruppi è altamente significativa.

Una rappresentazione grafica della situazione, può essere ottenuta con la funzione interaction.plot(). Osserviamo i grafici che otteniamo:


par(mfrow=c(1,2))
interaction.plot(time, subj, weights)
interaction.plot(subj, time, weights)




Come possiamo osservare dai grafici, le differenze tra soggetti sono irrilevanti (come abbiamo ottenuto dalla nostra analisi numerica), mentre le differenze within-subjects sono significative (le linee sono separate e non parallele tra loro).




Ecco ora un pò di dati per esercitarvi:


levels
subj high low moderate
1 9 12 8
2 3 9 6
3 11 13 18
4 8 6 8
5 3 19 12
6 1 7 5
7 5 4 5
8 7 6 7
9 9 14 18
10 13 19 10


Un certo parametro è stato misurato su 10 soggetti, in 3 condizioni diffenti. Viene chiesto di valutare se le 3 condizioni influiscono sul parametro considerato.

RISPOSTA: la variabile considerata influisce sulle misurazione, essendo p-value = 0.0436 *.




Introduco brevemente il capitolo di disegni più complessi, a 2 fattori. Si parla di two-way within subject ANOVA. I possibili disegni sperimentali che possono essere fatti sono:
- two factor ANOVA with repeated measures on one factor (= two way mixed model ANOVA / between-within design)
- two factor ANOVA with repeated measures on both factor (=two-way within subject ANOVA)

Se outcome è il valore del parametro che viene misurato, mentre factor1 e factor2 sono i due fattori di classificazione, tra i quali si vuole stimare se vi è differenza, le formule da applicare in R sono rispettivamente:

per il mixed model:
aov(outcome ~ factor1*factor2 + Error(subject/factor1))
in questo caso abbiamo factor1=within, factor2=between
o in alternativa
aov(outcome ~ factor1*factor2 + Error(subject/factor2))
e qui abbiamo factor1=between, factor2=within

mentre per la ripetizione in entrambi i fattori abbiamo:
aov(outcome ~ factor1*factor2 + Error(subject/(factor1*factor2)))

Possiamo ottenere solo i valori F usando la funzione lme della library(nlme):
anova(lme(outcome ~ factor1*factor2, random= ~1|subject/factor1)) #oppure subject/factor2
e per il mixed model:
anova(lme(outcome ~ factor1*factor2, random= ~1|subject))

venerdì 26 febbraio 2010

Quando le assunzioni dell'ANOVA sono violate: tests e post-hoc tests

Il capitolo dei confronti multipli è uno dei più vasti nella letteratura statistica. Cominciamo con un riepilogo. Le due principali assunzioni della ANOVA sono:
1. tutti i gruppi seguono una distribuzione normale
2. le varianze sono omogenee (omoschedasticità)

Queste due assunzioni vengono verificate rispettivamente con i test di normalità, e con i test di omogeneità della varianza.
Se queste due assunzioni sono confermate, si può procedere con una ANOVA parametrica omoschedastica, già discussa in questo post. Se essa risulta significativa, si procede con i test post-hoc per i confronti multipli, tra cui il test HSD di Tukey, il test LSD di Fisher, il test di Bonferroni e il test di Scheffé, e il test di Student-Newman-Keuls (SNK test).

Supponiamo che sia violata la assunzione 1., ma che sia valida la assunzione 2. In questo caso si procede con una ANOVA omoschedastica non parametrica, più nota come test di Kruskal-Wallis, di cui si è parlato in questo post. Se questo test risulta significativo (ossia se le medie risultano non uguali), occorre procedere con test post-hoc non parametrici, tra cui:
- Nemenyi-Damico-Wolfe-Dunn test
- Behrens-Fisher test
- Steel test (Nonparametric Multiple Comparisons)
Ricordo che per poter procedere con il test di Kruskal-Wallis, occorre fare l'assunzione di omogeneità delle varianze dei k gruppi in analisi.

Supponiamo ora che i dati siano normali, ma che le varianze non siano omogenee. In questo caso si procede con una ANOVA eteroschedastica: gli approcci sono:
- Welch one-way ANOVA (disponibile in R)
- Brown-Forsythe procedure (non disponibile in R, ma presente in SPSS)
- James first-and-second-order procedure (non disponibile in R)
Tra breve parlerò del primo metodo.

Se il test di Welch (siamo quindi sotto l'assunzione di non omogeneità delle varianze) risulta significativo, si deve procedere con test post-hoc per i confronti multipli non omoschedastici. Tra i test noti vi sono:
- test di Tamhane
- test di Games-Howell
- test di Duncan
i quali però non sono disponibili in R (se non ricordo male sono però implementati in SPSS).

Infine può accadere che i dati non siano normali, e che le varianze non siano omogenee. In tal caso si procede con il test di Friedman, e con i relativi post-hoc test: Schaich and Hamerle test, e Conover test.

Prima di procedere con gli esempi, ecco una tabella riassuntiva sulla situazione. Mi preme però fare 3 precisazioni:
1) esistono altri test oltre a quelli qui citati e discussi;
2) ciascun test offre a considerare proprie caratteristiche che qui non vengono considerate. Ad esempio il test di Friedman è maggiormente raccomandato per una "ANOVA con misure ripetute", mentre qui si parla di violazioni delle assunzioni della ANOVA classica. Non è un errore, ma è sempre bene fare riferimento alla letteratura specifica di ogni test;
3) ogni tanto può capitare qualche errore. Per cui consiglio sempre di controllare quanto è scritto qui (prima di incorrere in "rimproveri" da parte dei referer statistici!). Se trovate qualche errore, sarei grato se me lo segnalate nei commenti, così che possa correggere il tutto.

Ho compilato questa tabella per riassumere i test che possono essere utilizzati nei vari casi:



Tenendo ben presente questi concetti, vediamo qui di seguito 3 esempi:
- come fare i confronti multipli dopo un test di Kruskal-Wallis significativo
- come procedere con una ANOVA eteroschedastica
- come effettuare una ANOVA nonparametrica eteroschedastica, e il relativo post-hoc test




ANOVA non parametrica in R e Kruskal-Wallis post-hoc test

Consideriamo i seguenti dati:

a <- c(1, 2, 3, 4, 4, 5, 6)
b <- c(3, 3, 4, 5, 6, 6, 7)
c <- c(10, 11, 13, 17, 9, 15)
d <- c(10, 21, 25, 27, 22, 23)
Verifichiamo la normalità dei 4 gruppi con il test di Shapiro-Wilk:
> shapiro.test(a)

        Shapiro-Wilk normality test

data:  a 
W = 0.4172, p-value = 4.706e-07

> shapiro.test(b)

        Shapiro-Wilk normality test

data:  b 
W = 0.5481, p-value = 5.458e-06

> shapiro.test(c)

        Shapiro-Wilk normality test

data:  c 
W = 0.5142, p-value = 2.786e-06

> shapiro.test(d)

        Shapiro-Wilk normality test

data:  d 
W = 0.6986, p-value = 0.000163
Tutt'e 4 i gruppi risultano non normali. Ora verifichiamo la omoschedasticità. Essendo gruppi che non seguono una distribuzione normale, dobbiamo usare il test di Levene non parametrico:
dati <- a="" b="" br="" c="" d="">gruppi <- br="" c="" d="" factor="" length="" letters="" rep="" times="c(length(a),length(b),">
> levene.test(dati, gruppi, location="mean", kruskal.test=T)

        rank-based (Kruskal-Wallis) classical Levene's test based on the absolute deviations from
        the mean ( none not applied because the location is not set to median )

data:  dati 
Test Statistic = 5.484, p-value = 0.1396
Essendo p-value > 0.05, accettiamo l'ipotesi di omogeneità delle varianze. Alla luce di questi due dati (distribuzione non-normale, varianze omogenee), possiamo procedere con il test di Kruskal-Wallis:
kruskal.test(dati, gruppi)

        Kruskal-Wallis rank sum test

data:  dati and gruppi 
Kruskal-Wallis chi-squared = 31.4139, df = 3, p-value = 6.955e-07
Essendo p-value < 0.05, rifiutiamo l'ipotesi nulla: le medie sono significativamente differenti. Dobbiamo quindi indagare tra quali gruppi vi è una forte differenza. Prima di procedere, proviamo a mettere in grafico i dati, in questo modo:
plot(gruppi, dati)
Se osserviamo il grafico, risulta abbastanza evidente che: 1. le medie sono differenti (come afferma il test di Kruskal-Wallis che è significativo) 2. le varianze sono omogenee (questo non è molto evidente, essendo presente degli outliers). I test post-hoc non parametrici a nostra disposizione sono: - Nemenyi-Damico-Wolfe-Dunn test - Behrens-Fisher test (Nonparametric Multiple Comparisons) - Steel test Il codice per effettuare il Nemenyi-Damico-Wolfe-Dunn test (o più semplicemente Dunn's test) è disponibile nel package coin, nell'help relativo alla funzione oneway_test. Occorre installare anche la library multcomp. Innanzitutto creiamo un dataframe con i dati, e i gruppi:
dft
Ora utilizziamo il seguente codice, e analizziamo l'output:
library(coin)
if (require("multcomp")) {

    NDWD <- data="dft,<br" dati="" gruppi="" oneway_test="">        ytrafo = function(data) trafo(data, numeric_trafo = rank),
        xtrafo = function(data) trafo(data, factor_trafo = function(x)
            model.matrix(~x - 1) %*% t(contrMat(table(x), "Tukey"))),
        teststat = "max", distribution = approximate(B = 90000))

    
    print(pvalue(NDWD))

    
    print(pvalue(NDWD, method = "single-step"))
  }

#output

[1] 0
99 percent confidence interval:
 0.000000e+00 5.886846e-05 

                  
b - a 0.3819666667
c - a 0.0015555556
d - a 0.0000000000
c - b 0.2104555556
d - b 0.0009666667
d - c 0.3237666667
Le coppie con medie significativamente differenti sono quelle con p-value < 0.05. Quindi risultano significativamente differenti le coppie a-c, a-d, b-d (come possiamo controllare dal plot). Doveroso da citare anche il Mann-Whitney U test, utilizzato in diversi articoli scientifici, anche se non tutti sono d'accordo sulla correttezza di tale test come analisi post-hoc. Nella library pgirmess è disponibile la funzione kruskalmc che permette di ottenere i confronti multipli per dati non parametrici (anche se non viene specificato quale test è implementato). Si citano inoltre il test di Conover-Iman, e il test di Dwass-Steel-Citchlow-Fligner. Può essere utile, infine la funzione nparcomp della library omonima nparcomp, che implementa diversi algoritmi ("Tukey", "Dunnett", "Sequen", "Williams", "Changepoint", "AVE", "McDermott", "Marcus", "UmbrellaWilliams"). Il test non parametrico per i confronti multipli di Behrens-Fisher e quello di Steel sono disponibili nella library npmc. Anche in questo caso dobbiamo creare un dataframe, in cui necessariamente le due colonne relative ai dati e ai gruppi devono avere i nomi rispettivamente var e class:
var <- br="" dati="">class <- br="" gruppi="">
dft <- br="" class="" data.frame="" var="">
> summary(npmc(dft))

                                       
 /////////////////////////////////////////////////////////////
 / npmc executed                                             /
 /                                                           /
 / NOTE:                                                     /
 / -Used Satterthwaite t-approximation (df=15.4762442214526) /
 / -Calculated simultaneous (1-0.05) confidence intervals    /
 / -The one-sided tests 'a-b' reject if group 'a' tends to   /
 /  smaller values than group 'b'                            /
 /////////////////////////////////////////////////////////////
 
 
$`Data-structure`
  group.index class.level nobs
a           1           a   16
b           2           b   16
c           3           c   16
d           4           d   16

$`Results of the multiple Behrens-Fisher-Test`
  cmp    effect  lower.cl upper.cl   p.value.1s   p.value.2s
1 1-2 0.7558594 0.4902196 1.021499 3.184958e-02 0.0613214501
2 1-3 0.8867188 0.6698053 1.103632 2.877271e-04 0.0005863507
3 1-4 0.9257812 0.7521589 1.099404 7.622567e-06 0.0000321084
4 2-3 0.8007812 0.5482831 1.053279 8.410051e-03 0.0170268058
5 2-4 0.8652344 0.6317030 1.098766 9.485910e-04 0.0020723217
6 3-4 0.7988281 0.5544711 1.043185 7.261303e-03 0.0142616548

$`Results of the multiple Steel-Test`
  cmp    effect  lower.cl upper.cl   p.value.1s   p.value.2s
1 1-2 0.7558594 0.4917138 1.020005 0.0329254942 0.0616483846
2 1-3 0.8867188 0.6213672 1.152070 0.0005619949 0.0010558237
3 1-4 0.9257812 0.6600126 1.191550 0.0002169494 0.0001998778 *
4 2-3 0.8007812 0.5353070 1.066256 0.0096495950 0.0186865845
5 2-4 0.8652344 0.5990248 1.131444 0.0012742103 0.0025523271
6 3-4 0.7988281 0.5326920 1.064964 0.0102897944 0.0206920249
Nell'output vengono forniti i risultati dei due test separatamente.

ANOVA parametrica eteroschedastica in R Utilizziamo il dataset presente in questa pagina. Ho caricato i dati nel dataframe di nome data. Per prima cosa effettuiamo un test di Levene non parametrico, poichè 4 dei 5 gruppi risultano non-normali secondo il test di Shapiro-Wilk, per la verifica dell'omoschedasticità:
data[1:5,]
  group smell
1     1 1.381
2     1 1.322
3     1 1.162
4     1 1.275
5     1 1.381
...

group5 <- 2="" br="" data="">group4 <- 2="" br="" data="">group3 <- 2="" br="" data="">group2 <- 2="" br="" data="">group1 <- 2="" br="" data="">
> shapiro.test(group1)

        Shapiro-Wilk normality test

data:  group1 
W = 0.9201, p-value = 0.009898

> shapiro.test(group2)

        Shapiro-Wilk normality test

data:  group2 
W = 0.9167, p-value = 0.01008

> shapiro.test(group3)

        Shapiro-Wilk normality test

data:  group3 
W = 0.9323, p-value = 0.1529

> shapiro.test(group4)

        Shapiro-Wilk normality test

data:  group4 
W = 0.8499, p-value = 5.181e-05

> shapiro.test(group5)

        Shapiro-Wilk normality test

data:  group5 
W = 0.9034, p-value = 0.001820


dati <- br="" c="" group1="" group2="" group3="" group4="" group5="">gruppi <- factor="" letters="" rep="" times="c(length(group1),length(group2),length(group3),length(group4),length(group5))))<br">levene.test(dati, gruppi, location="mean", kruskal.test=T)

        rank-based (Kruskal-Wallis) classical Levene's test based on the absolute
        deviations from the mean ( none not applied because the location is not set
        to median )

data:  dati 
Test Statistic = 35.486, p-value = 3.691e-07
Essendo p-value < 0.05, rifiutiamo l'ipotesi di omogeneità delle varianze. Per confrontare le medie, dobbiamo quindi procedere con una ANOVA eteroschedastica (questi sono metodi parametrici, anche se i dati sono non-normali. Non avendo trovato dataset maggiormente adatti a questo esempio, dobbiamo fingere che i dati siano normali), utilizzando una delle seguenti procedure: - Welch one-way ANOVA - Brown-Forsythe procedure (non disponibile in R) - James first-and-second-order procedure (non disponibile in R) Per procedere con il test di Welch, il codice è il seguente:
oneway.test(smell ~ group, data=data, var.equal=F)

        One-way analysis of means (not assuming equal variances)

data:  smell and group 
F = 13.7208, num df = 4.000, denom df = 78.749, p-value = 1.555e-08
Essendo p-value < 0.05, rifiutiamo l'ipotesi nulla: le medie tra gruppi sono significativamente differenti. A questo punto dovremmo procedere con i confronti multipli. Nel caso di eteroschedasticità, i test che dovremmo eseguire sono: - Games-Howell post-hoc test (non disponibile in R) - Tamhane post-hoc test (disponibile in SPSS) - Duncan-Waller post-hoc test Solo il terzo test è disponibile in R, e questo è il codice:
library(agricolae)  
model <- br="" dati="" gruppi="" lm="">df<-df .residual="" br="" model="">MSerror<-deviance br="" df="" model="">Fc<-anova br="" model="">comparison <- dati="" df="" fc="" group="FALSE)<br" gruppi="" mserror="" waller.test="">
Study:

Waller-Duncan K-ratio t Test for dati 

This test minimizes the Bayes risk under additive
loss and certain other assumptions.
                               ......
K ratio                  100.00000000
Error Degrees of Freedom 175.00000000
Error Mean Square          0.03211259
F value                   16.65064329
Critical Value of Waller   1.79200000

Treatment Means
  gruppi     dati    std.err replication
1      a 1.316895 0.01681486          38
2      b 1.345139 0.01762372          36
3      c 1.306143 0.02782045          21
4      d 1.201093 0.03349086          43
5      e 1.059619 0.03795035          42

Different MSD for each comparison
Comparison between treatments means

   tr.i tr.j       diff        MSD significant
1     1    2 0.02824415 0.07468760       FALSE
2     1    3 0.01075188 0.08731729       FALSE
3     1    4 0.11580171 0.07149772        TRUE
4     1    5 0.25727569 0.07189592        TRUE
5     2    3 0.03899603 0.08817637       FALSE
6     2    4 0.14404587 0.07254438        TRUE
7     2    5 0.28551984 0.07293687        TRUE
8     3    4 0.10504983 0.08549128        TRUE
9     3    5 0.24652381 0.08582458        TRUE
10    4    5 0.14147398 0.06966687        TRUE
Nell'ultima tabella, la colonna significant ci dice quali sono le coppie di gruppi con medie statisticamente differenti. In pratica sono i gruppi 4 e 5 che presentano differenze tra loro e con tutti gli altri gruppi. Si può notare questo plottando i dati con il codice:
plot(gruppi, dati)

ANOVA non-parametrica eteroschedastica in R Immaginiamo infine che entrambe le assunzioni (normalità ed omoschedasticità) siano violate. In questo caso si può procedere con il test di Friedman, e con i post-hoc test di Schaich-Hamerle, e Conover. Il test di Friedman viene facilmente richiamato con la funzione friedman.test(), mentre il codice per il test post-hoc è disponibile qui. Rimando a questo link per un esempio svolto: LINK.

sabato 2 gennaio 2010

Latin squares design in R

Il disegno a quadrati latini (latin square design) permette di analizzare 3 fattori contemporaneamente, così come si farebbe con una ANOVA a 3 fattori.
Questo metodo è stato creato inizialmente in ambito agrario, per avere un metodo di analisi della produttività di un terreno, coltivato in maniera differente. Pertanto userò un esempio prettamente agrario, ma questa analisi può essere applicata anche in altri ambiti.
In un quadrato latino si crea una tabella NxN, con le righe e le colonne rappresentati rispettivamente il 'trattamento' e i 'blocchi'. Ciascuna cella sarà a sua volta riempita con il terzo fattore, denominato 'fattore principale'. Tutti e 3 i criteri devono avere lo stesso numero di livelli.

Per organizzare un disegno sperimentale di questo genere, ci risulta utile il software R già per organizzare il quadrato latino, in quanto non vi devono essere ripetizioni nell'ambito delle righe e delle colonne. Per ottenere un quadrato latino da utilizzare, usiamo la library(crossdes):


library(crossdes)
LatSqua <- des.MOLS(5,5)
LatSqua[1:5,1:5]

[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 3 4 5
[2,] 2 3 4 5 1
[3,] 3 4 5 1 2
[4,] 4 5 1 2 3
[5,] 5 1 2 3 4


Abbiamo così ottenuto un possibile schema di quadrato latino da utilizzare. Una volta raccolti i dati, potremo passare alla fase analitica.

Supponiamo ad esempio di avere un appezzamento di terreno, e di dividerlo in 5 strisce e 5 colonne, così da ottenere 25 quadrati. Per ogni striscia viene utilizzata una profondità di aratura differente; in ogni colonna viene utilizzato un concime differente; per ogni combinazione viene utilizzato una diversa varietà di seme. La nostra domanda è: la produttività dipende dal seme, dalla profondità dell'aratura o dal tipo di concime usato? O da tutt'e 3 i fattori?

Adesso supponiamo di avere ottenuto il seguente quadrato latino:


trattamentoA trattamentoB trattamentoC trattamentoD trattamentoE
concime1 "A42" "C47" "B55" "D51" "E44"
concime2 "E45" "B54" "C52" "A44" "D50"
concime3 "C41" "A46" "D57" "E47" "B48"
concime4 "B56" "D52" "E49" "C50" "A43"
concime5 "D47" "E49" "A45" "B54" "C46"


I tre fattori sono quindi: tipo di concime (concime1:5), tipo di trattamento del terreno (trattamentoA:E), tipo di seme (A:E). I numeri rappresentano la produttività, in quintali, di un certo frutto.

Per analizzare questi dati in R dobbiamo creare un dataframe. Utilizziamo il seguente codice:


concime <- c(rep("concime1",1), rep("concime2",1), rep("concime3",1), rep("concime4",1), rep("concime5",1))
trattamento <- c(rep("tratA",5), rep("tratB",5), rep("tratC",5), rep("tratD",5), rep("tratE",5))
varieta <- c("A","E","C","B","D", "C","B","A","D","E", "B","C","D","E","A", "D","A","E","C","B", "E","D","B","A","C")
freq <- c(42,45,41,56,47, 47,54,46,52,49, 55,52,57,49,45, 51,44,47,50,54, 44,50,48,43,46)

mydata <- data.frame(trattamento, concime, varieta, freq)

mydata

trattamento concime varieta freq
1 tratA concime1 A 42
2 tratA concime2 E 45
3 tratA concime3 C 41
4 tratA concime4 B 56
5 tratA concime5 D 47
6 tratB concime1 C 47
7 tratB concime2 B 54
8 tratB concime3 A 46
9 tratB concime4 D 52
10 tratB concime5 E 49
11 tratC concime1 B 55
12 tratC concime2 C 52
13 tratC concime3 D 57
14 tratC concime4 E 49
15 tratC concime5 A 45
16 tratD concime1 D 51
17 tratD concime2 A 44
18 tratD concime3 E 47
19 tratD concime4 C 50
20 tratD concime5 B 54
21 tratE concime1 E 44
22 tratE concime2 D 50
23 tratE concime3 B 48
24 tratE concime4 A 43
25 tratE concime5 C 46


Per conferma, creiamo due matrici, con le varietà, e con le frequenze:


matrix(mydata$varieta, 5,5)

[,1] [,2] [,3] [,4] [,5]
[1,] "A" "C" "B" "D" "E"
[2,] "E" "B" "C" "A" "D"
[3,] "C" "A" "D" "E" "B"
[4,] "B" "D" "E" "C" "A"
[5,] "D" "E" "A" "B" "C"

matrix(mydata$freq, 5,5)

[,1] [,2] [,3] [,4] [,5]
[1,] 42 47 55 51 44
[2,] 45 54 52 44 50
[3,] 41 46 57 47 48
[4,] 56 52 49 50 43
[5,] 47 49 45 54 46


Osservate che queste due tabelle combinate, danno la tabella originale da cui abbiamo preso i dati, per cui abbiamo inserito correttamente i valori.

Prima di procedere con l'analisi della varianza di questo disegno a quadrato latino, è opportuno eseguire un boxplot, per avere un'idea del risultato che ci aspettiamo:


par(mfrow=c(2,2))
plot(freq ~ concime+trattamento+varieta, mydata)




Osservate che le medie sono realmente differenti considerando il fattore varieta, poco differenti considerando il fattore trattamento, praticamente uguali considerando il tipo di concime usato.
Verifichiamo queste osservazioni grafiche, con la tabella ANOVA:


myfit <- lm(freq ~ concime+trattamento+varieta, mydata)
anova(myfit)

Analysis of Variance Table

Response: freq
Df Sum Sq Mean Sq F value Pr(>F)
concime 4 17.760 4.440 0.7967 0.549839
trattamento 4 109.360 27.340 4.9055 0.014105 *
varieta 4 286.160 71.540 12.8361 0.000271 ***
Residuals 12 66.880 5.573
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1


Osservate la significatività (il p-value) dei valori F di Fisher per i 3 test che sono stati effettuati. Considerando il fattore concime, non c'è differenza tra le medie; considerando il fattore trattamento la significatività è moderata (p-value compreso tra 0.05 e 0.1), mentre considerando il fattore varietà, la differenza è altamente significativa.

Adesso procediamo con la diagnostica del modello, osservando la distribuzione dei residui:



La distribuzione dei residui del primo grafico è casuale; il Q-Q plot dei residui è distribuito sufficientemente sulla bisettrice, quindi concludo che i residui seguono una distribuzione normale, assunzione indispensabile per un modello lineare come il nostro.

sabato 22 agosto 2009

Calcolo della tabella ANOVA partendo da dati aggregati

Può capitare di dover eseguire un test per verificare l'uguaglianze delle medie tra più gruppi, di cui però non disponiamo dei dati grezzi (il valore di ogni singolo dato), ma solamente della media, della deviazione standard e della numerosità campionaria di ciascun gruppo. Questo capita frequentemente nel momento in cui vogliamo fare ulteriori analisi sui dati pubblicati in articoli scientifici, che ci forniscono i dati descrittivi dei gruppi, e non i gruppi in sè.
Si possono ricavare facilmente le formule per ottenere la tabella ANOVA partendo dai dati descrittivi (in questo pdf sono presenti le formule per eseguire la one-way ANOVA, e le ANOVA bi- e tridimensionale partendo dai dati descrittivi). Supponiamo di avere i seguenti 4 gruppi:


a <- c(7.4, 6.6, 6.7, 6.1, 6.5, 7.2)
b <- c(7.1, 7.3, 6.8, 6.9, 7)
c <- c(6.8, 6.3, 6.4, 6.7, 6.5, 6.8)
d <- c(6.4, 6.9, 7.6, 6.8, 7.3)


Da questi dati calcoliamo: numerosità campionaria, media e deviazione standard, e raccogliamo tutti i dati in una tabella di riepilogo:


> mean(a)
[1] 6.75
> mean(b)
[1] 7.02
> mean(c)
[1] 6.583333
> mean(d)
[1] 7
>
> length(a)
[1] 6
> length(b)
[1] 5
> length(c)
[1] 6
> length(d)
[1] 5
>
> sd(a)
[1] 0.4764452
> sd(b)
[1] 0.1923538
> sd(c)
[1] 0.2136976
> sd(d)
[1] 0.4636809




Adesso dimentichiamo i dati campionari, e facciamo una ANOVA parametrica sui dati di quest'ultima tabella. Le formule sono:



La GrandMean è la media di tutti i gruppi; la SSB è la devianza tra gruppi, e la SSE è la devianza entro gruppi (o d'errore). Le altre formule sono quelle consuete, che già conoscevamo:



I df sono i gradi di liberta; MSB e MSE sono la varianza tra gruppi e d'errore; F è il valore della statistica test.

Traduciamo questi calcoli in R, e procediamo:


nA <- 6; nB <- 5; nC <- 6; nD <- 5
meanA <- 6.75; meanB <- 7.02; meanC <- 6.58; meanD <- 7
sdA <- .476; sdB <- .192; sdC <- .214; sdD <- .464

GrandMean <- (meanA * nA + meanB * nB + meanC * nC + meanD * nD) / (nA + nB + nC + nD)
> GrandMean
[1] 6.821818

SSB <- (((meanA - GrandMean)^2)*nA) + (((meanB - GrandMean)^2)*nB) + (((meanC - GrandMean)^2)*nC) + (((meanD - GrandMean)^2)*nD)
> SSB
[1] 0.7369273

SSE <- (sdA^2 * (nA-1)) + (sdB^2 * (nB-1)) + (sdC^2 * (nC-1)) + (sdD^2 * (nD-1))
> SSE
[1] 2.3705

dfSSB <- 4 - 1
dfSSE <- (nA+nB+nC+nD) - 4

MSB <- SSB / dfSSB
> MSB
[1] 0.2456424

MSE <- SSE / dfSSE
> MSE
[1] 0.1316944

F <- MSB / MSE
> F
[1] 1.865245


Possiamo calcolare il p-value di una distribuzione F, per prendere la nostra decisione statistica, in questo modo:


p.value <- 1 - pf(F, dfSSB, dfSSE)
> p.value
[1] 0.1716690


Raccogliamo tutti i dati nella classica tabella ANOVA:



Essendo p-value > 0.05, accettiamo l'ipotesi nulla: i 4 gruppi hanno medie uguali.

Adesso vediamo i valori che avremmo ottenuto, se avessimo utilizzato i dati campionari:


a <- c(7.4, 6.6, 6.7, 6.1, 6.5, 7.2)
b <- c(7.1, 7.3, 6.8, 6.9, 7)
c <- c(6.8, 6.3, 6.4, 6.7, 6.5, 6.8)
d <- c(6.4, 6.9, 7.6, 6.8, 7.3)

dati <- c(a,b,c,d)
gruppi = factor(rep(letters[1:4], times=c(length(a),length(b),length(c),length(d))))

anova(lm(dati ~ gruppi))

Analysis of Variance Table

Response: dati
Df Sum Sq Mean Sq F value Pr(>F)
gruppi 3 0.72730 0.24243 1.8402 0.1760
Residuals 18 2.37133 0.13174


Potete confrontare i valori contenuti nella tabella, dopo averla resa leggibile in Latex, con la funzione xtable, disponibile nel pacchetto xtable (converte le tabelle di R in codice LaTeX):

Confrontando i valori, essi risultano molto simili, approssimazioni a parte.

giovedì 20 agosto 2009

ANOVA post-hoc tests #2

In questo post continuiamo a vedere come utilizzare R per i test post-hoc di una ANOVA significativa. Raccomando la lettura del primo post in riguardo allo stesso argomento, dove si è parlato del test LSD di Fisher, e del test HSD di Tukey. Qui utilizzerò gli stessi dati, per esaminare insieme i seguenti test:

  • test t di Bonferroni

  • test S di Scheffé






Test t di Bonferroni

Il test per i confronti multipli simultanei proposto dallo statistico italiano Bonferroni, si basa sul confronto tra il valore t di Bonferroni, e una quantità che viene calcolata, di volta in volta, per ogni coppia di gruppi che si esamina.
Il t di Bonferroni si ricerca sulle tavole t di Student, in corrispondenza dei gradi di libertà della varianza d'errore (N-k), e del valore alpha ottenuto dividendo l'alpha di scelta (solitamente 0.05) per il doppio del numero di confronti da effettuare. Nel nostro caso abbiamo 4 gruppi; il numero di confronti da effettuare è quindi 6, che moltiplicato per 2 fa 12. In R tale t si calcola in questo modo. Supponiamo di avere una lista, con i k gruppi; il doppio del numero di confronti da effettuare è semplicemente k*(k-1). Quindi


a <- c(13.47, 10.21, 15.10, 14.65, 9.03, 15.14, 6.09, 3.43, 5.95, 10.72, 10.01, 8.17)
b <- c(4.02, 14.03, 3.09, 10.25, 6.25, 1.00, 7.50, 10.18, 6.07, 2.03, 4.17, 7.28)
c <- c(10.56, 14.61, 11.88, 11.43, 9.73, 12.37, 3.82, 13.04, 13.28, 15.28, 12.90, 10.98)
d <- c(7.74, 2.77, 10.29, 4.03, 10.23, 7.67, 7.93, 6.75, 5.60, 8.19, 2.02, 7.88)

dati <- list(g1=a, g2=b, g3=c, g4=d)

confronti2 <- length(dati) * (length(dati) - 1)
alpha.bonferr <- 0.05 / confronti2

abs(qt(alpha.bonferr, 44))
[1] 2.762815


Questo valore va confrontato con il risultato della seguente formula. Siano r ed s due generici gruppi in esame:



con = varianza entro gruppi.
Se il t di Bonferroni calcolato è maggiore di quello tabulato (nel nostro caso 2.76), allora la differenza è significativa (le medie sono significativamente differenti); altrimenti sono uguali.
Procedendo manualmente, quindi, si crea una matrice triangolare con i t di Bonferroni calcolati, e si individuano quelli maggiori (in valore assoluto) del t-tabulato, ossia le differenze significative (qui le indico con un asterisco). Con i nostri dati avremmo:



In R non è disponibile una funzione per eseguire direttamente il test di Bonferroni. Tuttavia sono riuscito a trovare in rete una bozza della funzione, che ho modificato per svolgere i calcoli visti finora. Ecco il codice, quindi, per eseguire il test di Bonferroni in R:


bonferroni.test = function(x, y)
{
# x is a list of vectors
# y is the alpha
num <- 0
denom <- 0
grandmean.num <- 0
sb2.num <- 0
nn <- numeric(length(x))
ss2 <- numeric(length(x))
for(i in 1:length(x)) {
nn[i] <- length(x[[i]])
ss2[i] <- (sum((x[[i]] - mean(x[[i]]))^2))/(nn[i] - 1
)
num <- ((nn[i] - 1) * ss2[i]) + num
denom <- nn[i] + denom
grandmean.num <- (nn[i] * mean(x[[i]])) +
grandmean.num
}
sw2 <- num/(denom - length(x))
grandmean <- grandmean.num/denom
for(j in 1:length(x)) {
sb2.num <- (nn[j] * ((mean(x[[j]]) - grandmean)^2)) +
sb2.num
}
sb2 <- sb2.num/(length(x) - 1)
FF <- sb2/sw2
p.val <- 1 - pf(FF, length(x) - 1, denom - length(x))
effort <- numeric(length(x) * (length(x) - 1))
effort1 <- character(length(x) * (length(x) - 1))
tt <- matrix(effort, ncol = length(x))
pp <- matrix(effort, ncol = length(x))
qq <- matrix(effort1, ncol = length(x))
alpha.star <- y/((length(x) * (length(x) - 1)))
deg.free <- denom - length(x)
for(k in 1:(length(x) - 1)) {
for(l in 2:length(x)) {
if(k >= l) {
tt[k, l] <- 0
}
else {
tt[k, l] <- abs((mean(x[[k]]) - mean(x[[l
]]))/sqrt(sw2 * ((1/nn[k]) + (1/nn[
l]))))
alpha <- 1 - (alpha.star)
t.tab <- qt(alpha, deg.free)
pp[k, k] <- 0
if(tt[k, l] == 0) {
qq[k, l] <- c("Zero")
}
else if(tt[k, l] < t.tab) {
qq[k, l] <- c("Not")
}
else if(tt[k, l] > t.tab) {
qq[k, l] <- c("Signif")
}
}
}
}
results <- list(grandmean, sw2, sb2, FF, p.val, tt,
alpha.star, qq)
results <- list(Grand.mean = grandmean, Within.var = sw2,
Between.var = sb2, F.value = FF, p.value = p.val,
bonferroni.t = tt, alpha.star = alpha.star,
bonferroni.sig = qq)
results
}


L'input è costituito dalla lista dei gruppi (x), e dal valore di alpha da cui vogliamo partire (qui userò alpha = 0.05). Applichiamo subito la funzione, e studiamo l'output:


a <- c(13.47, 10.21, 15.10, 14.65, 9.03, 15.14, 6.09, 3.43, 5.95, 10.72, 10.01, 8.17)
b <- c(4.02, 14.03, 3.09, 10.25, 6.25, 1.00, 7.50, 10.18, 6.07, 2.03, 4.17, 7.28)
c <- c(10.56, 14.61, 11.88, 11.43, 9.73, 12.37, 3.82, 13.04, 13.28, 15.28, 12.90, 10.98)
d <- c(7.74, 2.77, 10.29, 4.03, 10.23, 7.67, 7.93, 6.75, 5.60, 8.19, 2.02, 7.88)

dati <- list(g1=a, g2=b, g3=c, g4=d)

bonferroni.test(dati, 0.05)

$Grand.mean
[1] 8.725417

$Within.var
[1] 11.32240

$Between.var
[1] 81.22261

$F.value
[1] 7.173622

$p.value
[1] 0.0005046445

$bonferroni.t
[,1] [,2] [,3] [,4]
[1,] 0 2.796572 1.086478 2.4793041
[2,] 0 0.000000 3.883050 0.3172684
[3,] 0 0.000000 0.000000 3.5657816

$alpha.star
[1] 0.004166667

$bonferroni.sig
[,1] [,2] [,3] [,4]
[1,] "" "Signif" "Not" "Not"
[2,] "" "" "Signif" "Not"
[3,] "" "" "" "Signif"


Per comodità la funzione fornisce in output anche le varianze (tra gruppi ed entro gruppi), il risultato della statistica F di una ANOVA parametrica, e il suo corrispettivo p-value (per controllare se è utile eseguire i confronti multipli oppure no).
Quindi viene presentata la matrice triangolare con i valori t di Bonferroni calcolati come sopra. E' in pratica la stessa tabella che abbiamo scritto manualmente. Quindi viene riportato il valore alpha (0.05 / 12), ed infine una matrice triangolare contenente la significatività del test.
Risultano quindi significative (ossia le medie sono differenti) i confronti tra i gruppi 1-2, 2-3, 3-4. Osservate che il test HSD di Tukey (applicato in questo post) ha dato lo stesso risultato, ossia ha individuato come significative le stesse coppie.




Test di Scheffé per i confronti multipli

Il test implementato da Scheffé è molto simile al testo di Bonferroni. Per ogni coppia di dati si calcola la seguente quantità:



con = varianza entro gruppi.
Questo valore (che è lo stesso della procedura di Bonferroni) viene confrontato con il valore critico:



Se la quantità calcolata supera il valore critico, allora la differenza è significativa.
Il test è quindi molto simile al test di Bonferroni, eccetto il valore critico di confronto. Consideriamo il consueto dataset, e calcoliamo il valore critico manualmente:


a <- c(13.47, 10.21, 15.10, 14.65, 9.03, 15.14, 6.09, 3.43, 5.95, 10.72, 10.01, 8.17)
b <- c(4.02, 14.03, 3.09, 10.25, 6.25, 1.00, 7.50, 10.18, 6.07, 2.03, 4.17, 7.28)
c <- c(10.56, 14.61, 11.88, 11.43, 9.73, 12.37, 3.82, 13.04, 13.28, 15.28, 12.90, 10.98)
d <- c(7.74, 2.77, 10.29, 4.03, 10.23, 7.67, 7.93, 6.75, 5.60, 8.19, 2.02, 7.88)

dati <- list(g1=a, g2=b, g3=c, g4=d)

N <- length(a) + length(b) + length(c) + length(d)
alpha <- 1 - .05
F.cv <- sqrt( (length(dati)-1) * qf(alpha, (length(dati)-1), (N-length(dati))) )

> F.cv
[1] 2.906785


Proviamo ora a calcolare la quantità S per la coppia di gruppi a-b:


num <- abs( mean(a) - mean(b) )

dati.tot <- c(a,b,c,d)
gruppi = factor(rep(letters[1:4], each = 12))
within.var <- anova(lm(dati.tot~gruppi))[2,3]

den <- sqrt( within.var*(1/length(a)+1/length(b)) )

S <- num/den

> S
[1] 2.796572


Per la coppia a-b, S < S-critico, quindi la media di questi due gruppi è significativamente uguale.
Possiamo procedere in modo analogo per le altre coppie. Oppure utilizziamo la funzione sottostante, che è sostanzialmente uguale a quella del test di Bonferroni visto poco sopra, salvo le modifiche del test di Scheffé. Questo è il codice per eseguire il test di Scheffé in R:


scheffe.test = function(x, y)
{
# x is a list of vectors
# y is the alpha
num <- 0
denom <- 0
grandmean.num <- 0
sb2.num <- 0
nn <- numeric(length(x))
ss2 <- numeric(length(x))
for(i in 1:length(x)) {
nn[i] <- length(x[[i]])
ss2[i] <- (sum((x[[i]] - mean(x[[i]]))^2))/(nn[i] - 1
)
num <- ((nn[i] - 1) * ss2[i]) + num
denom <- nn[i] + denom
grandmean.num <- (nn[i] * mean(x[[i]])) +
grandmean.num
}
sw2 <- num/(denom - length(x))
grandmean <- grandmean.num/denom
for(j in 1:length(x)) {
sb2.num <- (nn[j] * ((mean(x[[j]]) - grandmean)^2)) +
sb2.num
}
sb2 <- sb2.num/(length(x) - 1)
FF <- sb2/sw2
p.val <- 1 - pf(FF, length(x) - 1, denom - length(x))
effort <- numeric(length(x) * (length(x) - 1))
effort1 <- character(length(x) * (length(x) - 1))
tt <- matrix(effort, ncol = length(x))
pp <- matrix(effort, ncol = length(x))
qq <- matrix(effort1, ncol = length(x))
alpha.star <- y/((length(x) * (length(x) - 1)))
deg.free <- denom - length(x)
for(k in 1:(length(x) - 1)) {
for(l in 2:length(x)) {
if(k >= l) {
tt[k, l] <- 0
}
else {
tt[k, l] <- abs((mean(x[[k]]) - mean(x[[l
]]))/sqrt(sw2 * ((1/nn[k]) + (1/nn[
l]))))
alpha <- 1 - y
f.tab <- sqrt( (length(x)-1) * qf(alpha, (length(x)-1), (denom-length(x))))
pp[k, k] <- 0
if(tt[k, l] == 0) {
qq[k, l] <- c("Zero")
}
else if(tt[k, l] < f.tab) {
qq[k, l] <- c("Not")
}
else if(tt[k, l] > f.tab) {
qq[k, l] <- c("Signif")
}
}
}
}
results <- list(grandmean, sw2, sb2, FF, p.val, tt,
alpha.star, qq)
results <- list(Within.var = sw2,
Between.var = sb2, F.value = FF, p.value = p.val,
Scheffé.S = tt, alpha = y, S.critical.value = f.tab,
Scheffé.sig = qq)
results
}


Adesso studiamo l'output della funzione:


a <- c(13.47, 10.21, 15.10, 14.65, 9.03, 15.14, 6.09, 3.43, 5.95, 10.72, 10.01, 8.17)
b <- c(4.02, 14.03, 3.09, 10.25, 6.25, 1.00, 7.50, 10.18, 6.07, 2.03, 4.17, 7.28)
c <- c(10.56, 14.61, 11.88, 11.43, 9.73, 12.37, 3.82, 13.04, 13.28, 15.28, 12.90, 10.98)
d <- c(7.74, 2.77, 10.29, 4.03, 10.23, 7.67, 7.93, 6.75, 5.60, 8.19, 2.02, 7.88)

dati <- list(g1=a, g2=b, g3=c, g4=d)

scheffe.test(dati, .05)

$Within.var
[1] 11.32240

$Between.var
[1] 81.22261

$F.value
[1] 7.173622

$p.value
[1] 0.0005046445

$Scheffé.S
[,1] [,2] [,3] [,4]
[1,] 0 2.796572 1.086478 2.4793041
[2,] 0 0.000000 3.883050 0.3172684
[3,] 0 0.000000 0.000000 3.5657816

$alpha
[1] 0.05

$F.critical.value
[1] 2.906785

$Scheffé.sig
[,1] [,2] [,3] [,4]
[1,] "" "Not" "Not" "Not"
[2,] "" "" "Signif" "Not"
[3,] "" "" "" "Signif"


Nell'ordine ci vengono forniti:
- la varianza entro gruppi
- la varianza tra gruppi
- il valore della statistica F dell'ANOVA e il suo p-value
- la quantità S calcolata per ogni coppia
- il valore alpha prescelto
- il valore critico di confronto
- la significativà (una coppia è significativa quando le medie sono differenti)

In base al test di Scheffé sono significative le coppie 2-3 e 3-4.

domenica 9 agosto 2009

ANOVA post-hoc tests #1

In questo post vediamo come applicare alcuni test per indagare quali gruppi hanno influito sulla significatività di una analisi della varianza ANOVA. Ricordo che una ANOVA si dice significativa quando p-value < 0.05, il che significa che tra i gruppi che si sono considerati, almeno una coppia di medie è significativamente differente. Il nostro obiettivo è quello di ricercare quali coppie. I test a disposizione sono numerosi; in questo e nei prossimi post ne vedremo alcuni

Consideriamo i seguenti 4 gruppi, ripresi da questo link. Immettiamo in R i nostri dati, e creiamo i vettori che ci serviranno per le funzioni che applicheremo.
La prima cosa da fare, è stabilire se questi seguono una distribuzione normale, al fine di applicare l'analisi della varianza parametrica. In questo post sono raccolti alcuni dei test a disposizione (qui utilizzerò solo lo Shapiro-Wilk normality test).
Quindi si verifica l'omogeneità delle varianze, così da poter procedere con una ANOVA omoschedastica. In questo post sono raccolti alcuni test che possiamo applicare (qui utilizzo solo il test di Bartlett).



a <- c(13.47, 10.21, 15.10, 14.65, 9.03, 15.14, 6.09, 3.43, 5.95, 10.72, 10.01, 8.17)
b <- c(4.02, 14.03, 3.09, 10.25, 6.25, 1.00, 7.50, 10.18, 6.07, 2.03, 4.17, 7.28)
c <- c(10.56, 14.61, 11.88, 11.43, 9.73, 12.37, 3.82, 13.04, 13.28, 15.28, 12.90, 10.98)
d <- c(7.74, 2.77, 10.29, 4.03, 10.23, 7.67, 7.93, 6.75, 5.60, 8.19, 2.02, 7.88)

dati <- c(a,b,c,d)
gruppi = factor(rep(letters[1:4], each = 12))

shapiro.test(a)

Shapiro-Wilk normality test

data: a
W = 0.939, p-value = 0.4855

shapiro.test(b)

Shapiro-Wilk normality test

data: b
W = 0.9619, p-value = 0.8103

shapiro.test(c)

Shapiro-Wilk normality test

data: c
W = 0.8519, p-value = 0.0388

shapiro.test(d)

Shapiro-Wilk normality test

data: d
W = 0.916, p-value = 0.2548

bartlett.test(dati, gruppi)

Bartlett test of homogeneity of variances

data: dati and gruppi
Bartlett's K-squared = 2.1573, df = 3, p-value = 0.5404


Il vettore c risulta non-normale. Teoricamente quindi non potremmo procedere con una ANOVA parametrica, ma dovremmo utilizzare il test di Kruskal-Wallis. Tuttavia, procederò con l'ANOVA parametrica ai fini dell'esercizio. Le varianze risultano omogenee.
A questo punto fittiamo il modello, e calcoliamo la tabella ANOVA:


model <- lm(dati ~ gruppi)
anova(model)

Analysis of Variance Table

Response: dati
Df Sum Sq Mean Sq F value Pr(>F)
gruppi 3 243.67 81.22 7.1736 0.0005046 ***
Residuals 44 498.19 11.32
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1


Il p-value è minore di 0.05, quindi l'ANOVA è significativa: non tutte le medie sono significativamente uguali.
Procediamo quindi con i test post-hoc. Quelli che qui esamineremo sono:

  • test LSD di Fisher

  • test HSD di Tukey






Procedura LSD di Fisher

La sigla LSD sta per "least significance difference". Disponendo di k medie, ognuna calcolata su n dati (numerosità campionaria di ciascun gruppo), si calcola la quantità LSD, secondo la formula:



Il valore t di Student è quello relativo al proprio livello di confidenza (qui useremo sempre alpha = 0.05), e i gradi di liberà della varianza residua (N-k = 44). In R tale valore si calcola così:


qt(0.975, 44)
[1] 2.015368


Questo t si moltiplica per la radice quadrata del rapporto tra la varianza residua (il cui valore è presente della tabella ANOVA: 11.32), fratto la numerosità campionaria del singolo gruppo (12). Il risultato è:


qt(0.975, 44) * sqrt(2*11.32/12)
[1] 2.768


A questo punto si calcolano le differenze tra le medie di tutti i gruppi. Quindi la media del gruppo a meno la media del gruppo b; media-a meno media-c, e così via, prese in valore assoluto. Questo risultato si confronta con il valore LSD. Se la differenza è maggiore di LSD allora quella coppia è significativa (le medie sono significativamente differenti); se è minore, allora le medie sono uguali.
Manualmente si può procedere così:


> abs(mean(a) - mean(b))
[1] 3.841667

> abs(mean(a) - mean(c))
[1] 1.4925

#e così via


Con molti gruppi, il lavoro può essere laborioso. Ad ogni modo nella libreria agricolae (da scaricare e installare), la procedura LSD è a disposizione. La funzione LSD.test() riceve in input:
- i dati
- i gruppi
- i gradi di libertà della varianza residua (che possiamo calcolare in R così: df <- df.residual(model) )
- la varianza residua (che possiamo calcolare in R così: MSerror <- deviance(model)/df )

Applichiamo subito la funzione ai nostri dati:


library(agricolae)
model <- lm(dati ~ gruppi)
df<-df.residual(model)
MSerror<-deviance(model)/df
comparison <- LSD.test(dati, gruppi, df, MSerror, group=TRUE)

Study:

LSD t Test for dati
......
Alpha 0.050000
Error Degrees of Freedom 44.000000
Error Mean Square 11.322399
Critical Value of t 2.015368

Treatment Means
gruppi dati std.err replication
1 a 10.164167 1.1196506 12
2 b 6.322500 1.0982832 12
3 c 11.656667 0.8498728 12
4 d 6.758333 0.7694191 12

Least Significant Difference 2.768521
Means with the same letter are not significantly different.

Groups, Treatments and means
a c 11.65667
a a 10.16417
b d 6.758333
b b 6.3225


Specificando group=TRUE, possiamo leggere il valore LSD calcolato (2.768). Se vogliamo confrontare tutte le differenze con LSD, dobbiamo specificare group=FALSE.


comparison <- LSD.test(dati, gruppi, df, MSerror, group=FALSE)

Study:

LSD t Test for dati
......
Alpha 0.050000
Error Degrees of Freedom 44.000000
Error Mean Square 11.322399
Critical Value of t 2.015368

Treatment Means
gruppi dati std.err replication
1 a 10.164167 1.1196506 12
2 b 6.322500 1.0982832 12
3 c 11.656667 0.8498728 12
4 d 6.758333 0.7694191 12

Comparison between treatments means

tr.i tr.j diff pvalue
1 1 2 3.8416667 0.0076
2 1 3 1.4925000 0.2832
3 1 4 3.4058333 0.0171
4 2 3 5.3341667 0.0003
5 2 4 0.4358333 0.7525
6 3 4 4.8983333 0.0009


L'ultima parte dell'output è quello che ci serve in questo momento. Sono state calcolate tutte le differenze (primo gruppo meno secondo gruppo; primo gruppo meno terzo gruppo, e così via), ed è stato calcolato il relativo p-value di confronto con LSD.
- p-value > 0.05: differenza non significativa (le medie dei due gruppi sono uguali)
- p-value < 0.05: differenza significativa (le medie dei due gruppi non sono uguali)
E così si può ricavare quali sono le coppie che hanno reso significativa la ANOVA.

Il test LSD modificato da Winer propone di utilizzare la correzione di Bonferroni per calcolare la probabilità alpha del comparisonwise (in pratica alpha viene diviso per il doppio del numero di confronti da effettuare simultaneamente; quindi cambia il valore del t di Student). Se scegliamo di tener conto di questa modifica, in R dobbiamo semplicemente richiedere di aggiustare il p-value secondo il metodo di Bonferroni:


comparison <- LSD.test(dati, gruppi, df, MSerror, group=FALSE, p.adj="bonferroni")

Study:

LSD t Test for dati
P value adjustment method: bonferroni
......
Alpha 0.050000
Error Degrees of Freedom 44.000000
Error Mean Square 11.322399
Critical Value of t 2.762815

Treatment Means
gruppi dati std.err replication
1 a 10.164167 1.1196506 12
2 b 6.322500 1.0982832 12
3 c 11.656667 0.8498728 12
4 d 6.758333 0.7694191 12

Comparison between treatments means

tr.i tr.j diff pvalue
1 1 2 3.8416667 0.0458
2 1 3 1.4925000 1.0000
3 1 4 3.4058333 0.1024
4 2 3 5.3341667 0.0021
5 2 4 0.4358333 1.0000
6 3 4 4.8983333 0.0053


Con la correzione di Bonferroni risultano significativamente differenti le coppie 1-2, 2-3, 3-4.




Test HSD di Tukey

L'acronimo HSD sta per "honestly significant difference". I principi generali visti per il test LSD vengono applicati anche qui. L'unica differenze è il calcolo della quantità LSD, che qui viene indicata con HSD:



Il valore Q (Q-studentizzato), dipende dal livello di confidenza alpha (0.05), dal numero di medie a confronto (k) e dai gradi di libertà della varianza d'errore (N-k). Si ricerca quindi in tavole tabulate il valore, che in questo caso è pari a 3.789. Quindi la quantità HSD ha valore 3.668. Le differenze tra le medie di tutti i gruppi vanno confrontate con il valore HSD (come per il test LSD).
Sempre nel pacchetto agricolae è già disponibile la funzione che svolge tutti i calcoli. L'input è uguale alla funzione per il test LSD


comparison <- HSD.test(dati, gruppi, df, MSerror, group=T)

##cancello parte dell'output, perchè ci serve solo
##per osservare il valore HSD:

Honestly Significant Difference 3.667801

##...

comparison <- HSD.test(dati, gruppi, df, MSerror, group=FALSE)

Study:

HSD Test for dati
......
Alpha 0.050000
Error Degrees of Freedom 44.000000
Error Mean Square 11.322399
Critical Value of Studentized Range 3.775958

Treatment Means
gruppi dati std.err replication
1 a 10.164167 1.1196506 12
2 b 6.322500 1.0982832 12
3 c 11.656667 0.8498728 12
4 d 6.758333 0.7694191 12

Comparison between treatments means

tr.i tr.j diff pvalue
1 1 2 3.8416667 0.0369
2 1 3 1.4925000 0.6995
3 1 4 3.4058333 0.0773
4 2 3 5.3341667 0.0019
5 2 4 0.4358333 0.9888
6 3 4 4.8983333 0.0048


Anche in questo caso sono significative (ossia significativamente diverse) le differenze che in valore assoluto sono superiori al valore HSD, e che nell'output del programma R presentano p-value < 0.05. Con il test HSD di Tukey risultano significativamente differenti le medie dei gruppi 1-2, 2-3, 3-4 (le stesse coppie del test LSD con la modifica di Winer).

In questo post vengono trattati il test t di Bonferroni e il test S di Scheffé

venerdì 17 luglio 2009

ANOVA a due vie

L'analisi della varianza ad una via è utile per verificare contemporaneamente se le medie di più gruppi sono uguali. Ma questa analisi può risultare poco utile, ai fini di problemi più complessi. Ad esempio può essere necessario prendere in considerazione due fattori di variabilità, per verificare se le medie tra i gruppi dipendono dal gruppo di classificazione ("zone") o dalla seconda variabile che si va a considerare ("blocco"). In questo caso si ricorre ad una analisi della varianza a due vie (ANOVA a due vie, two-way ANOVA).

Cominciamo subito con un esempio, così da rendere più facile la comprensione di questo metodo statistico. I dati raccolti vengono organizzati in tabelle a doppia entrata.

Il direttore di una società ha raccolto le entrate (in migliaia di dollari) per 5 anni e in base al mese. Si vuole verificare se le entrate dipendono dall'annata e/o dal mese, oppure se sono indipendenti da questi due fattori.

Concettualmente il problema potrebbe essere risolto con un'ANOVA orizzontale, ed un ANOVA verticale. Si verifica cioè se le entrate medie classificate per anno siano uguali, e se sono uguali le entrate medie classificate per mese. Ciò richiederebbe numerosi calcoli, e pertanto si ricorre all'analisi ANOVA a due vie che offre il risultato istantaneamente.
Questa è la tabella delle entrate classificate per anno e per mese:



Come per l'ANOVA a una via, anche qui l'obiettivo è quello di strutturare un test F di Fisher per valutare la significatività della variabile mesi e della variabile anno, e stabilire se in base a uno (o entrambi) di questi criteri di classificazione dipendono le entrate.
In R la sintassi per l'ANOVA a due vie è la seguente. Innanzitutto si crea un array contenente tutti i valori tabulati, trascritti per riga:

> entrate = c(15,18,22,23,24, 22,25,15,15,14, 18,22,15,19,21,
+ 23,15,14,17,18, 23,15,26,18,14, 12,15,11,10,8, 26,12,23,15,18,
+ 19,17,15,20,10, 15,14,18,19,20, 14,18,10,12,23, 14,22,19,17,11,
+ 21,23,11,18,14)
>


A questo punto si classificano i valori così inseriti. In base ai mesi si crea un fattore a 12 livelli (numero righe) con 5 ripetizioni (numero colonne) in questa maniera:
> mesi = gl(12,5)

In base agli anni si crea un fattore a 5 livelli (numero colonne) e 1 ripetizione; si esplicita il numero di variabili inserite (la lunghezza dell'array entrate):
> anni = gl(5, 1, 60)

A questo punto è possibile fittare il modello lineare e produrre la tabella ANOVA:

> fit = aov(entrate ~ mesi + anni)
>
> anova(fit)

Analysis of Variance Table

Response: entrate
          Df Sum Sq Mean Sq F value Pr(>F)
mesi      11 308.45   28.04  1.4998 0.1660
anni       4  44.17   11.04  0.5906 0.6712
Residuals 44 822.63   18.70


Interpretiamo adesso i risultati.
La significatività della differenza tra mesi è: F = 1.4998. Questo valore è inferiore al valore tabulato e difatti p-value > 0.05. Quindi si accetta l'ipotesi nulla<: le medie valutate in base ai mesi sono uguali; quindi la variabile "mesi" non influisce sulle entrate.

La significatività della differenza tra anni è: F = 0.5906. Questo valore è inferiore al valore tabulato e difatti p-value > 0.05. Quindi si accetta l'ipotesi nulla: le medie valutate in base agli anni sono uguali; quindi la variabile "anni" non influisce sulle entrate.

sabato 4 luglio 2009

Confronto tra più gruppi, metodo parametrico: analisi della varianza ANOVA

Analisi della varianza: ANOVA

Il metodo ANOVA permette di effettuare il confronto tra più gruppi, con metodo parametrico (supponendo cioè che i vari gruppi seguano una distribuzione gaussiana).
Procediamo con il seguente esempio:

Il dirigente di una catena di supermercati vuole verificare se il consumo in KiloWatt di 4 suoi negozi siano tra loro uguali. Raccoglie i dati alla fine di ogni mese, per 6 mesi. I risultati sono i seguenti:
Supermercato A: 65, 48, 66, 75, 70, 55
Supermercato B: 64, 44, 70, 70, 68, 59
Supermercato C: 60, 50, 65, 69, 69, 57
Supermercato D: 62, 46, 68, 72, 67, 56


Per procedere con la verifica ANOVA, occorre dapprima verificare l'omoschedasticità (ossia effettuare test per l'omogeneità delle varianze). Il software R mette a disposizione due test nel pacchetto standard: il test di Bartlett e il test di Fligner-Killeen; numerosi altri test per testare l'omogeneità delle varianze (test di Hartley, di Cochran, di Levene, eccetera) vengono discussi in questo post.




Cominciamo con il test di Bartlett.

Per prima cosa creiamo 4 array con i valori:


a = c(65, 48, 66, 75, 70, 55)
b = c(64, 44, 70, 70, 68, 59)
c = c(60, 50, 65, 69, 69, 57)
d = c(62, 46, 68, 72, 67, 56)


Adesso concateniamo i 4 valori in un unico vettore:


dati = c(a, b, c, d)


Ora su questo unico contenitore in cui sono salvati tutti i dati, creiamo i 4 livelli:


gruppi = factor(rep(letters[1:4], each = 6))


Possiamo osservare il contenuto del vettore gruppi semplicemente digitando gruppi + [invio].

A questo punto avviamo il test di Bartlett:


bartlett.test(dati, gruppi)

Bartlett test of homogeneity of variances

data: dati and gruppi
Bartlett's K-squared = 0.4822, df = 3, p-value = 0.9228


La funzione ci ha fornito il valore della statistica test, nonché il p-value. Possiamo affermare che le varianze sono omogenee essendo p-value > 0.05. In alternativa possiamo confrontare il Bartlett's K-squared con il valore del chi-quadro-tabulato; calcoliamo quest'ultimo valore, assegnando il valore di alpha e i gradi di libertà:


qchisq(0.950, 3)
[1] 7.814728


Come vedete, chi-quadro-tabulato è maggiore di Bartlett's K-squared, quindi l'ipotesi di omoschedasticità è vera.




Proviamo a verificare l'omoschedasticità con il test di Fligner-Killeen.
La sintassi è del tutto analoga, quindi procediamo velocemente.


a = c(65, 48, 66, 75, 70, 55)
b = c(64, 44, 70, 70, 68, 59)
c = c(60, 50, 65, 69, 69, 57)
d = c(62, 46, 68, 72, 67, 56)

dati = c(a, b, c, d)

gruppi = factor(rep(letters[1:4], each = 6))

fligner.test(dati, gruppi)

Fligner-Killeen test of homogeneity of variances

data: dati and gruppi
Fligner-Killeen:med chi-squared = 0.1316, df = 3, p-value = 0.9878


Le conclusioni sono analoghe a quelle viste per il test di Bartlett.




Verificata l'omoschedasticità dei 4 gruppi, possiamo procedere con il metodo ANOVA.

Anzitutto organizziamo i valori:


modello = lm(formula = dati ~ gruppi)


Quindi analizziamo col metodo ANOVA:


anova (modello)

Analysis of Variance Table

Response: dati
Df Sum Sq Mean Sq F value Pr(>F)
gruppi 3 8.46 2.82 0.0327 0.9918
Residuals 20 1726.50 86.33


L'output della funzione è la classica tabella ANOVA con i seguenti dati:
Df = gradi di libertà
Sum Sq = devianza (entro gruppi, e residua)
Mean Sq = varianza (entro gruppi, e residua)
F value = è il valore della statistica test, calcolato come (varianza entro gruppi) / (varianza residua)
Pr(>F) = è il p-value

Essendo p-value > 0.05, accettiamo l'ipotesi H0: le medie dei valori sono statisticamente uguali. Per sicurezza possiamo confrontare F-value con il valore F-tabulato (alpha, gradi di libertà del numeratore, gradi di libertà del denominatore):


qf(0.950, 20, 3)
[1] 8.66019


Poiché F-tabulato > F-value-calcolato, posso concludere accettando l'ipotesi H0: le medie sono statisticamente uguali.

Se l'ANOVA fosse risultata significativa (ossia se p-value < 0.05, allora vuol dire che non tutte le medie dei k gruppi considerati sono tra loro uguali. Occorre andare a ricercare quale o quali coppie di gruppi rendono significativa l'ANOVA, ossia si deve cercare quali coppie di medie sono significativamente differenti. Per far questo sono a disposizione gli ANOVA post hoc-tests, di cui si parla in questo post.