Statistici si grafice în R
Să presupunem că avem un fișier text care conține notele unor elevi de la 3 examene. Fișierul are următoarea structură:
Grupa | 1 | 2 | 2 |
---|---|---|---|
Nota1 | 4 | 9 | 4 |
Nota2 | 8 | 7 | 7 |
Nota3 | 5 | 7 | 6 |
Unii elevi nu au fost la toate examenele, în locul acelor note este trecut NA (Not Available). Desigur, fișierul fiind text, delimitatorii dintre coloane sunt "t
". Un exemplu de asemenea fișier cu 50 de rânduri se găsește aici.
Vrem să obținem următoarele date pentru fiecare examen:
- numărul de picați pe an și pe fiecare grupă
- procentajul celor picați pe an și pe grupă
- media notelor pe an și pe grupă
- un grafic cu linii al frecvenței notelor, care să prezinte grupa vs an
- un barchart al numărului de note pe grupe
- media fiecărui elev
În primul rând trebuie să importăm datele în R:
> note note
Grupa Nota1 Nota2 Nota3
1 1 4 NA 6
2 1 4 NA 6
3 1 6 8 8
4 1 4 2 5
5 1 9 9 9
6 1 NA 3 5
...
R are un obiect special numit data frame, care este o listă de variabile cu același număr de linii, aka: un tabel. Nu o să intru în detalii prea multe despre el, doar câteva chestii de bază: data frames pot avea un header (Grupa, Nota1, Nota2, Nota3 în cazul nostru) cu care pot fi accesate coloanele sale. Putem lucra cu datele dintr-o anumită coloană selectând-o cu $: note$Grupa
sau note$Nota1
rezultă într-un vector cu valorile coloanelor. Cu acești vector se lucrează apoi în mod obișnuit, doar având grijă că dacă le atribuim valori noi, acestea trebuie să aibă atâtea rânduri câte are data frameul.
Atenție la citire, se pune / în loc de în pathul fișierului!
Pentru că avem și date lipsă, nu putem să aplicăm pur și simplu funcția mean
asupra coloanelor ca să obținem media, ci trebuie să filtrăm cumva valorile cu NA. Avem două modalități:
Să filtrăm cu funcția is.na()
coloana și acestui vector să aplicăm mean
> mean(note$Nota1[!is.na(note$Nota1)])
[1] 5.525
Ne folosim de un parametru al funcției mean
pentru a face filtrarea „automat”: Dacă na.rm
este TRUE atunci va sări automat peste valorile de NA.
> mean(note$Nota1,na.rm=TRUE)
[1] 5.525
Pentru simplitate (și pentru că un pic mai încolo avem nevoie de ea :D), definim o funcție meanRM, care face această chestie automat:
> meanRM <- function(values)
+ {
+ mean(values,na.rm=TRUE)
+ }
R returnează automat valoarea returnată de ultima linie din funcție.
Ca să calculăm mediile pe grupe, folosim funcția factor, care permite gruparea datelor în funcție de anumite criterii, în acest caz după grupă, și apoi putem ușor aplica funcții tuturor grupelor.
> grupe grupe
[1] 1 1 1 1 1 1 1 1 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7
[46] 7 7 7 7 7
Levels: 1 2 3 4 5 6 7
Acel levels semnifică valorile distincte după care a creat grupele.
Aplicarea unei funcții unei funcții se face cu tapply
, care are ca primul parametru vector cu care vrem să lucrăm, al doilea este lista factorilor, iar al treilea este funcția pe care vrem să o aplicăm (aici ne trebuie meanRM
).
> tapply(note$Nota1,grupe,meanRM)
1 2 3 4 5 6 7
5.0 5.5 4.0 5.5 4.8 8.4 5.5
Ca să obținem numărul celor picați, va trebui să filtrăm notele după valoarea notelor, dacă este mai mică sau egală cu 4 sau dacă ea este NA (și cei care nu s-au prezentat la examen eu picat). Definim o funcția care returnează lungimea vectorului după filtrare și apoi chemăm funcția o dată cu toate notele și o dată cu tapply
, pentru a obține rezultatul pe grupe.
> picati picati(note$Nota1)
[1] 31
> tapply(note$Nota1,grupe,picati)
1 2 3 4 5 6 7
6 3 5 6 7 0 4
Pentru procentajul celor picați, procedăm fix la fel, doar că mai împărțim la lungimea vectorului:
> picatiProc picatiProc(note$Nota1)
[1] 62
> tapply(note$Nota1,grupe,picatiProc)
1 2 3 4 5 6 7
75.00000 75.00000 100.00000 66.66667 63.63636 0.00000 50.00000
Și acum să trecem la chestiile mai fun: grafice. Graficele se trimit la un dispozitiv de vizualizare. Pe noi deocamdată ne interesează două din acestea: ecranul și un fișier png. Pentru a face grafice folosim funcția plot
, care începe un grafic nou. Cum un grafic are două axe, avem nevoie de două seturi de date. Unul este dat de valoarile pe care le pot lua notele (1-10), iar celălalt este dat de frecvențele notelor (0 - 1). Numărul de note este dat de funcția table
, care returnează un tabel care conține pentru fiecare valoare de câte ori apare ea în setul de date. Ca să obținem frecvența notelor, trebuie să mai împărțim la numărul total de note. Dacă vrem să mai adăugăm un set de date unui grafic, trebuie să folosim funcția lines
, care cam aceleași chestii le face ca și plot
, doar că nu începe un grafic nou.
> plot(table(note$Nota1)/length(note$Nota1),type="o",xlab="Frecvente",ylab="Note")
Asta duce la desenarea pe ecran a graficului. Type="o" face ca să fie linii desenate (RTM pentru restul tipurilor), iar ceilalți doi parametrii îs kinda obvious.
Cum facem să desenăm într-un fișier?
> png(filename=paste("E:/Blog/Grupa ",i,".png",sep=""),width = 600,height=500,bg="white")
> #draw
> box()
> dev.off()
Funcția paste
concatenează parametrii ei și returnează un string. Restul parametrilor funcției png
se explică singuri. Cu funcția box()
desenăm un pătrat în jurul graficului (să arate un pic mai profi), iar dev.off()
oprește dispozitivul și implicit duce la salvarea fișierului.
Punând toate la un loc, obținem codul pentru a genera grafice într-un fișier. Ca să mai variem un pic lucrurile, acuma vom folosi un for loop pentru a genera graficele pentru fiecare grupă.
> for (i in 1:7) {
+ png(filename=paste("E:/Blog/Grupa ",i,".png",sep=""),width = 600,height=500,bg="white")
+ plot(table(note$Nota1)/length(note$Nota1),type="o",xlab="Frecvente",ylab="Note")
+ lines(table(note$Nota1[note$Grupa == i])/length(note$Nota1[note$Grupa == i]),type="o",col="red")
+ box()
+ dev.off()
+ }
Pentru a face graficele cu bare pentru note, folosim funcția barplot
(shock and awe):
> barplot(table(note$Nota1))
Restul parametrilor sunt identici cu cei de la plot
.
Pentru a calcula mediile fiecărui elev, vom adăuga o coloană nouă în care vom pune automat media. Aceasta se poate face cu un one-liner:
> note$medii
apply
este similar cu tapply
de mai sus, doar că nu lucrează pe levels, ci pe „subscripts of the matrix” (pe rânduri sau/și pe coloane). Primul parametru reprezintă datele pe care va lucra, al doilea reprezintă că va lucra pe rânduri, iar al treilea este funcția care se aplică. Aceasta primește ca parametru rândul pe care se aplică și returnează media celorlalte coloane cu note.
Dacă după aceste transformări vrem să scriem înapoi în fișier datele noastre, folosim write.table
, simetricul funcției cu care am citit.
> write.table(note,"E:/Blog/sample.txt",row.names=FALSE,sep="t")
Parametrul row.names
setează scrierea indicilor rândurilor (care în acest caz sunt destul de arbitrari așa că nu îi scriem).
Și cu asta am făcut o prelucrare simplă a notelor unor examene fictive.
P.S: Ar fi trebuit să scriu tutorialul acesta înainte să fac statisticile, că am învățat multe scriindu-l =)))
P.P2: Dacă numele tău nu este Cătălin și ai citit până aici, I'm impressed.