Grafiikka, satunnaislukuja, jakaumia ja yleistettyjä lineaarisia malleja Jari Oksanen Tiistai 13. syyskuuta 2005 Tiivistelmä Aluksi laajennettiin ja kerrattiin edellisen päivän grafiikan virittelyä kosekavaa osiota. Janiika Aaltosen kysymys vei meidät satunnaislukujen ihmeelliseen maailmaan ja siitä edelleen jakaumien tarkasteluun. Tämän jälkeen oli vuorossa päivän suunniteltu pääaihe, eli yleistetyt lineaariset mallit, jotka tosin jäivät melko lailla puolitiehen. Sisältö 1 Graafisen ympäristön virittäminen 1 2 Satunnaisluvut ja jakaumat 2 2.1 Jakaumien graafinen tarkastelu................... 3 2.2 Keskeinen raja-arvolause....................... 4 3 Yleistetty lineaarinen malli 5 1 Graafisen ympäristön virittäminen R:n perusgrafiikka on siistiä, karua. Se on suuniteltu printattavaksi tai artikkeliin liitettäväksi. Verkkosivuja tai esitelmiä varten voi olla tarvetta muuttaa grafiikan yleisilmettä. Tämä tapahtuu asettamalla graafisia parametreja. Näitä parametreja on erittäin paljon ja varmaankin on tarve katsoa manuaalisivuilta (?par) mitä kaikkea voi tai pitäisi muutttaa. Tässäkään osiossa ei ole kuvia: ne voi toteuttaa itse omalla koneellaan. Ehdottomasti ensimmäiseksi on tallennettava nykyisen istunnon parametrit, sillä toipuminen seikkailusta voi muuten olla liian työlästä: > oletuspar = par(no.readonly = T) Tämän jälkeen voi ruveta muuttamaan yksi kerrallaan grafiikan ilmettä: > par(bg = "darkblue") > par(fg = "white") > par(col.axis = "yellow") > par(col.lab = "yellow") > par(col.main = "hotpink") 1
> par(cex.lab = 1.3) > par(cex = 1.2) > par(lwd = 2) Tämän jälkeen voimme katsoa miltä kuvan uusi ilme näyttää: > load(url("http://cc.oulu.fi/~jarioksa/piikurssi.rda")) > attach(kurssi) > plot(ph, spno, main = "Otsikko") Mikäli kuva miellytti, voimme tallettaa tämänhetkiset asetukset samalla tavalla kuin oletusasetukset aikaisemmin ja tarpeen mukaan vaihtaa oletusasetusten ja uusien asetusten välillä > slaidi = par(no.readonly = T) > plot(ph, spno) > par(oletuspar) > plot(ph, spno, main = "Huuhaa!") Asetukset säilyvät työtilassa ja ne voi tallettaa istunnon loputtua. Halutessaan ne voi dumpata erilliseen tiedostoon ja myöhemmin sourcella ottaa jälleen istuntoonsa. Viritykset saa taas käyttönsä sanomalla > par(slaidi) 2 Satunnaisluvut ja jakaumat R:ssä voi tuottaa satunnaislukuja hyvin monesta erilaisesta jakaumasta. Yhden funktion olemme jo nähneet: sample poimii satunnaislukuja annetusta vektorista tai kokonaisluvuista. Seuraava noppa-funktio arpoo kolme numeroa luvuista 1... 6 niin että kukin luku voi esiintyä otoksessa monta kertaa: > sample(6, 3, rep = T) [1] 4 5 5 Ilman rep=t määrettä kukin luku voisi esiintyä otoksessa vain kerran. Mikäli taas otoskokoa ei anneta, oletustoiminta on poimia kaikki luvut kerran otokseen eli järjestää vektori satunnaiseen järjestykseen, kuten teimme ristivalidoinnissa eilen: > sample(6) [1] 4 1 5 2 6 3 Funktio voi saada vielä yhden määritteen: probs, joka antaa kunkin poimittavan alkion otostustodennäköisyyden. Edellä simuloimme reilua noppaa, mutta painotetun nopan saa aikaiseksi antamalle arvolle 6 suurempi paino: > sample(6, 20, rep = T, prob = c(1, 1, 1, 1, 1, 6)) [1] 3 6 6 5 3 6 4 6 2 6 6 4 6 6 1 6 6 3 6 6 Funktio voi poimia havaintoja myös muista kuin numerovektoreista: 2
> sample(c("orvokki", "vuokko", "leinikki", "kastikka"), 5, rep = T) [1] "leinikki" "leinikki" "kastikka" "vuokko" "kastikka" R tuntee tärkeimmät tilastolliset jakaumat ja pystyy yleensä tuottamaan satunnaislukuja näistä jakaumista. Tällaisen satunnaislukufunktion nimi on tyyppiä rjakauma, ja se on yleensä dokumentoitu yhdessä muiden jakaumafunktioiden kanssa: esim. djakauma antaa todennäköisyystiheyden. Useimmin käytettyjä satunnaislukufunktioita on tasainen jakauma. Seuraavassa tuotetaan viisi satunnaislukua tasaisesta jakaumasta välillä 0... 1 ja sitten välillä 1... 3: > runif(5) [1] 0.49645 0.31747 0.95022 0.06872 0.24272 > runif(5, -1, 3) [1] 1.3190 0.6973-0.8852 1.5652 1.6635 Normaalisti jakautuneita lukuja voidaan tuottaa näin: > rnorm(8) [1] -1.39512 0.89259-1.13127 0.03491 1.46125-1.13530 0.15106 0.72492 > rnorm(8, mean = 5, sd = 2) [1] 6.476 3.115 7.644 5.865 3.435 5.830 4.147 5.741 Muita funktioita ovat esim. rbinom, rpois, rgamma, rt, rcauchy, rexp... Nämä on dokumentoitu oman jakaumafunktionsa kanssa yhdessä, ja kukin odottaa omaa jakaumasta riippuvaista parametriaan. 2.1 Jakaumien graafinen tarkastelu Jakauman muotoa voi tarkastella sovitetun tiheysfunktion avulla. Tätä varten on R:ssä funktio density, jonka tuloksen voi suoraan piirtää. Funktio valitse tasoitusikkunan ( bandwidth ) leveyden automaattisesti. Kuinka normaaleilta näyttävät normaalisti jakautuneet satunnaisluvut: > x = rnorm(100, mean = 5, sd = 2) > den = density(x) > plot(den) Samaa funktiota voi käyttää myös havaittujen muuttujien jakauman tarkasteluun. Tämä ei ole kovin normaali: > plot(density(alkal)) Muita jakaumien tarkateluun sopivia funktioita ovat boxplot ja kvantiili-kvantiili-kuviot: > boxplot(alkal) > qqnorm(alkal) > qqline(alkal) 3
Jos muuttuja on jakautunut normaalisti, pisteiden pitäisi muodostaa suora viiva kvantiiliplotissa. Tällä kertaa jakauma on hyvinkin vino. Funktio qqline helpottaa suoruuden arviontia. Meidän ei kuitenkaan pidä odottaa, että pisteet ovat täysin suoralla viivalla. Etenkin pienissä aineistoissa sattuman merkitys on suuri: > qqnorm(rnorm(30)) Sen sijaan suurissa aineistoissa normaalisti jakautuneet pisteet myös näyttävät normaalisti jakautuneilta: > qqnorm(rnorm(3000)) 2.2 Keskeinen raja-arvolause Edellä esitety graafiset tarkastelut ovat yleensä riittäviä arvioimaan muuttujien jakauman normaalisuutta. Normaalisuustestit ovat vaarallisia: pienissä aineistoissa niiden voima ei riitä poikkeamien havaitsemiseen ja suurissa aineistoissa ne taas ovat herkkiä havaitsemaan mitättömiäkin eroja. Kuitenkin jakaumaoletuksilla on merkitystä nimenomaan pienissä aineistoissa ja suurissa meitä suojelee keskeinen raja-arvolause. Keskeinen raja-arvolause sanoo: summan jakauma lähestyy normaalijakaumaa summattavien määrän kasvaessa riippumatta alkuperäisten havaintojen jakaumasta. Keskiarvo pohjautuu summaan (summa jaettuna havaintomäärällä), joten myös keskiarvon jakauma lähestyy normaalijakaumaa kun aineiston koko kasvaa. Näin ollen voimme käyttää monia parametrisia testejä jakaumaoletuksia rikkomatta. Keskeinen raja-arvolause on varma äärettömille aineistoille, mutta onneksi normaalistuminen tapahtuu melko nopeasti jopa erittäin epänormaaleilla havainnoilla. Seuraavassa esimerkissä tarkastellan kurssi-aineistosta piilevälajia, jonka alkuperäiset jakaumat ovat hyvin vinot: > par(mfrow = c(2, 2)) > plot(density(asteform)) Tarkastelemme kuinka tästä lajista poimittujen otosten keskiarvojen jakaumaa. Ensin teemme vektorin, jossa on tilaa sadalle otoskeskiarvolle: > samp = rep(0, 100) Sitten käytämmä uudelleen otostuksesta tuttua toistoa (for) ja otostamme takaisinpanolla (sample) erikokoisia otoksia, laskemme niistä keskiarvon (mean), talletamme sen havainnoksi i ja sadan kierroksen jälkeen piirrämme jakauman tiheyskuvaajan: > for (i in 1:100) samp[i] = mean(sample(asteform, 4, repl = T)) > plot(density(samp), main = "N = 4") > for (i in 1:100) samp[i] = mean(sample(asteform, 16, repl = T)) > plot(density(samp), main = "N = 16") > for (i in 1:100) samp[i] = mean(sample(asteform, 64, repl = T)) > plot(density(samp), main = "N = 64") Normaalistuminen on häkellyttävn selvä. 4
3 Yleistetty lineaarinen malli Tässä kappaleessa tarkastellaan piileväaineistosta (kurssi) Asterionella formasan esiintymistodennäköisyyden enneustamista veden happamuuden perustteella. Kohdemuuttujamme on kaksiarvoinen: laji joko esiintyy tai puuttuu. Minkäänlainen muunnos ei voi normalisoida kaksiarvoista muuttujaa, joten meidän on valittava menetelmä, joka pystyy tällaisia muuttujia käsittelemään. Yleistetyt lineaariset mallit suoriutuvat helposti ja luonnollisella tavalla tällaisistakin muuttujista (yksityiskohtia seuraa myöhemmin). Alkuperäiset havainnot ovat lukumäärämuuttujia, joista suuri osa on nollia: > sort(asteform) [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [19] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [37] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [55] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 [73] 1 2 2 2 2 2 3 6 6 7 9 9 9 9 10 10 10 11 [91] 11 11 13 13 15 23 24 25 25 27 31 33 33 33 35 50 63 68 [109] 78 127 Lukumäärämuuttujan saa muutettua kaksiarvoiseksi loogisella vertailuoperaatiolla x > 0: > Asteform > 0 [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE [13] FALSE FALSE FALSE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE [25] FALSE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE TRUE FALSE TRUE [37] FALSE FALSE FALSE FALSE TRUE FALSE TRUE TRUE TRUE TRUE TRUE TRUE [49] TRUE TRUE FALSE TRUE TRUE TRUE FALSE FALSE TRUE FALSE FALSE FALSE [61] FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE [73] FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE [85] FALSE FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE FALSE FALSE FALSE [97] FALSE TRUE FALSE FALSE FALSE TRUE TRUE FALSE TRUE TRUE TRUE FALSE [109] FALSE FALSE > par(mfrow = c(1, 1)) > plot(ph, Asteform > 0) Voimme antaa tämän vertailun suoraan mallilausekkeessa. Seuraava komento sovittaa mallin kaksiarvoiselle muuttujalle, joka on TRUE silloin kun laji esiintyy järvessä: > tulos = glm(asteform > 0 ~ ph, data = kurssi, family = binomial) > tulos Call: glm(formula = Asteform > 0 ~ ph, family = binomial, data = kurssi) Coefficients: (Intercept) ph -7.56 1.24 5
Degrees of Freedom: 109 Total (i.e. Null); Null Deviance: 148 Residual Deviance: 135 AIC: 139 108 Residual Komento on hyvin samantapainen kuin aiemmin käytetty lineaarinen malli lm: olemme vain lisänneet g-kirjaimen komentoon ja yhden uuden argumentin family = binomial. Yleistetty lineaarinen malli onkin yleistetty juuri siten, että sen virhejakauma voi olla muukin kuin normaali. Ekologit ja biologit tarvitsevat usein myös Ṕoisson- ja Gamma-jakaumia, ja muitakin on tarjolla. Lisää tietoja löytyy menetelmien ohjesivuilta (?glm,?family sekä R:n mukana tulevista oppaista ( An Introduction... ). Funktion tulostus on hyvin samantapainen kuin lm-malleissa: summary(tulos) > summary(tulos) Call: glm(formula = Asteform > 0 ~ ph, family = binomial, data = kurssi) Deviance Residuals: Min 1Q Median 3Q Max -1.602-0.960-0.658 1.116 2.001 Coefficients: Estimate Std. Error z value Pr(> z ) (Intercept) -7.57 2.17-3.49 0.00048 *** ph 1.24 0.37 3.34 0.00084 *** --- Signif. codes: 0 *** 0.001 ** 0.01 * 0.05. 0.1 1 (Dispersion parameter for binomial family taken to be 1) Null deviance: 148.06 on 109 degrees of freedom Residual deviance: 135.25 on 108 degrees of freedom AIC: 139.3 Number of Fisher Scoring iterations: 4 Oleellisimpia eroja ovat: ˆ Kerrointaulukon kolmas sarake on nimeltään z eikä t. Sarake on laskettu samalla tavalla (kerroin / keskivirhe), mutta nyt se ei ole t-jakautunut vaan asymptoottisesti likipitäen normaalijakautunut. Myös P -arvot ovat normaalijakaumasta. ˆ Neliösumman sijaan raportoidaan devianssi. Devianssi on epänormaalille mallille sama kuin jäännösneliösumma normaalimallille. Itse asiassa normaalimallin devianssi on jäännösneliösumma. Mallin ennusteet sijoitetaan kuvaan normaaliin tapaan. Jotta pisteet yhdistettäisiin oikeassa järjestyksessä, talletamme jälleen muuttujan ph järjestykseen vektoriin i ja käytämme tätä järjestystä kuvan piirtämisessä: 6
> i = order(ph) > lines(ph[i], fitted(tulos)[i], col = "hotpink") Kuvassa oleva regressiosuora onkin käyrä! Tämä on toinen glm:n yleistys virhevaihtelun lisäksi: regressiosuoraa voidaan muuntaa ns. linkkifunktiolla sen sijaan että muunnettaisiin aineistoa. Ohjesivut (?family) kertovat, että oletus linkkifunktio binomimalleissa on logit eli vedonlyöntisuhteen logaritmi. Logitfunktion avulla alkujaan suora viiva muuttuu sigmoidiksi käyräksi välille 0... 1. Ennuste antaa meille suoraan todennäköisyyden vaikka regressio näyttää määrittelevän suoran. Logit-muunnos määrittää logistisen käyrän, minkä takia usein puhutaan logistisesta regressiosta. Regressiokertoimista suoraan määritettyä ennustetta sanotaan lineaariseksi prediktoriksi. Lineaarisen prediktorin η ja responssin µ suhde on µ = g 1 (η), missä g on linkkifunktio. Logistisen käyrän käänteisen linkkifunktion tarjoaa R:ssä funktio plogis. Seuraavassa näemme, kuinka mallin kertoimista saadaan lineaarinen prediktori ja responssi ph-arvoon 7.5: > tulos Call: glm(formula = Asteform > 0 ~ ph, family = binomial, data = kurssi) Coefficients: (Intercept) ph -7.56 1.24 Degrees of Freedom: 109 Total (i.e. Null); Null Deviance: 148 Residual Deviance: 135 AIC: 139 108 Residual > -7.56 + 1.24 * 7.5 [1] 1.74 > plogis(-7.56 + 1.24 * 7.5) [1] 0.8507 Aiemmin lineaarista malleista oppimamme funktiot toimivat täälläkin, mutta joissain on lisäargumentteja tai hieman erilaisia oletuksia. Niinpä varianssianalyysia varten meidän on myös annettava testin nimi, tai saamme vain taulukon deviansseista ja vapausasteista: > anova(tulos, test = "Chi") Analysis of Deviance Table Model: binomial, link: logit Response: Asteform > 0 Terms added sequentially (first to last) 7
Df Deviance Resid. Df Resid. Dev P(> Chi ) NULL 109 148.1 ph 1 12.8 108 135.3 0.00034 Silloin kun sovitetun mallin dispersio on 1 (katso summary(tulos)), devianssi on jakautunut likipitäen Khi-neliö-jakauman mukaan, joten pyydämme tätä testiä. Funktio fitted tuottaa ennustetut arvot. Funktiossa predict sen sijaan on muitakin vaihtoehtoja: funktiossa on argumentti type, joka voi olla "link" tai "response". Jälkimmäinen on responssi ja edellinen lineaarinen prediktori. Luottamusvälejä ei saa automaattisesti vaan meidän on itse laskettava ne keskivirheistä. Piirrämme kuvan uudelleen, mutta lisäämme nyt käyrään arvion 95 % luottamusväleistä. Teemme myös uuden aineiston, jota käytämme ennustuksessa (tämä kaikki on periaattessa tuttua lineaarisista malleista viime viikolta). > range(ph) [1] 4.52 6.90 > df = data.frame(ph = seq(4.52, 6.9, len = 21)) > tmp <- seq(4.52, 6.9, len = 21) > df = data.frame(ph = tmp) > pre = predict(tulos, newdata = df, se = TRUE, type = "link") > str(pre) List of 3 $ fit : Named num [1:21] -1.98-1.83-1.69-1.54-1.39.....- attr(*, "names")= chr [1:21] "1" "2" "3" "4"... $ se.fit : Named num [1:21] 0.528 0.488 0.448 0.410 0.372.....- attr(*, "names")= chr [1:21] "1" "2" "3" "4"... $ residual.scale: num 1 Tuloksena on nyt luettelo, jossa on erikseen ennustettujen arvojen vektori sekä keskiarvojen vektori. Meidän on laskettava luottamusvälit käsin ja tämä on ensin tehtävä lineaarisella prediktorilla ja sitten muutettava arvot responssin skaalalle käänteisellä linkkifunktiolla: > fv = pre$fit > lwr = pre$fit - 2 * pre$se > upr = pre$fit + 2 * pre$se > par(mfrow = c(1, 1)) > plot(ph, Asteform > 0, main = "Asterionella formosa") > lines(tmp, plogis(fv), lwd = 3) > lines(tmp, plogis(upr), lwd = 1, col = "peachpuff") > lines(tmp, plogis(lwr), lwd = 1, col = "peachpuff") Tämä vaati hieman enemmän käsityötä, mutta oli muuten melko yksinkertaista. Valitettavasti emme ole vieä ehdyttäneet yleistettyjä lineaarisia malleja, vaan jatkamme niistä ensi kerralla... 8