rolisz's site

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, de­lim­i­ta­torii dintre coloane sunt "t". Un exemplu de asemenea fișier cu 50 de rânduri se găsește aici.

Vrem să obținem ur­mă­toarele date pentru fiecare examen:

Î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:

  1. 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
  2. Ne folosim de un parametru al funcției mean pentru a face filtrarea „automat”: Dacă na.rmeste 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 pro­cen­ta­jul 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 in­tere­sează 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 con­cate­nează parametrii ei și returnează un string. Restul para­metrilor 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 dis­poz­i­tivul ș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 para­metrilor 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 „sub­scripts 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 trans­for­mă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 sta­tis­ti­cile, 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.