NMSA230 - Úvod do programování v R

Zimný semester 2021/2022 | Cvičenie 2 | St 20/10/21



II. ‘Reálne’ data a základné popisné charakteristiky v R

Program R (dostupný pod GNU GPL licenciou) je k dispozícii k stiahnutiu (free of charge) na adrese

https://www.r-project.org

K dispozícii sú distribúcie s priamou podporou pre OS Windows, Linux aj Macintosh.

Základnú inštaláciu programu R je možne jednoducho rozšíriť pomocou dodatočných knižníc (balíčkov), ktoré sú k dispozícii na rôznych online repozitároch (zoznam hlavných repozitárov je na adrese https://cran.r-project.org/mirrors.html). Jednotlivé R knižnice sú tvorené samotnými užívateľmi softwaru R a ich správne fungovanie nie je garantované - je preto namieste určitá opatrnosť a hlavne aktívne premýšľanie pri ich používaní.

Pre užívateľov programu R sú k dispozícii aj rôzne grafické rozhrania, ktoré je možne dodatočne nainštalovať a umožňujú (v určitých smeroch) jednoduchšiu a prehľadnejšiu prácu. Najznámejší a pravdepodobne aj jeden z najlepších R interfacov je RStudio.

Užitočné materiály pre prácu so štatistickým softwarom R

  • Bína, V., Komárek, A. a Komárková, L.: Jak na jazyk R. (PDF súbor)
  • Komárek, A.: Základy práce s R. (PDF súbor)
  • Kulich, M.: Velmi stručný úvod do R. (PDF súbor)
  • De Vries, A. a Meys, J.: R for Dummies. (ISBN-13: 978-1119055808)



1. Práca s datovými súbormi

V programe R je štandardne k dispozícii množstvo vzorových datových súborov rôzných formátov a typov. Zoznam týchto datových suborov sa získa zavolaním príkazu data(). Na konkrétny datový súbor sa následne odkazuje pomocou mena príslušného datového súboru.

rm(list = ls())
Orange

Ďalšie vzorové data lze získať inštaláciou dodatočných knižnic. Pre zoznam datových súborov z konkrétnej knižnice slúži dodatočný parameter package = NULL. Napríklad, pre zobrazenie zoznamu dat v knižnici lattice slúži nasledujúci príkaz:

data(package = "lattice") 

Reálne datové súbory bývajú prevažne reprezentované prostredníctvom tabuliek (tzv. datových matíc) s množstvom ríadkov (väčšinou jednotlivé pozorovania) a stĺpcov (väčšinou sledované premenné). Nie vždy je možné celkový datový súbor zobraziť na obrazovke, preto je užitočné poznať niektoré príkazy, prostredníctvom ktorých sa o datach dozvieme tie podstatné informácie. V praxi sa ale bežne stane, že samotné data je potrebné výrazne upraviť/modifikovať/prispôsobiť tak, aby bolo možné s nimi efektívne pracovať. Príprava datového súboru je prvý a nutný krok pred samotnou štatistickou analýzou.

test.data <- read.table("http://www.karlin.mff.cuni.cz/~maciak/NMSA407/NMSA407-1617-HW1.txt", header=T)
dim(test.data) ### velkost datoveho suboru: pocet pozorovani a pocet sledovanych premennych
str(test.data) ### struktura a typ jednotlivych premennych
head(test.data) ### niekolko prvych pozorovani
summary(test.data) ### zakladne popisne charakteristiky polohy

V prípade, že pracujeme pouze s jednym datovým súborom, je možne odkazovať sa na jednotlivé premenné aj pomocou príslušného názvu premenných. Potrebný je príkaz attach() (záleží tiež od konkrétnej verzie programu R), ktorý volanie prostredníctvom názvu premenných umožní. Porovnajte výstup dvoch nasledujúcich časti zdrojového kódu:

table(hitchhikers)
table(test.data$hitchhikers)

a

attach(test.data)
table(hitchhikers)

S jednotlivými premennými môžeme v Rku následne jednoducho pracovať a podľa potreby si ich upravovať, prípadne definovať nové premenné (resp. rôzne datové podsúbory). Užitočný príkaz pre manipuláciu s premennými a definíciu nových premenných je príkaz transform().

test.data <- transform(test.data, driverCZ = factor(1*(driverGender == "male") + 2*(driverGender == "female"), labels = c("M", "Z")))

test.data <- transform(test.data, distance01 = factor(1*(travelDistance != "> 500km") + 2*(travelDistance == "> 500km"), labels = c("short_trip", "long_trip")))

test.data <- transform(test.data, waitingTimeHours = ceiling(waitingTime/60))
head(test.data)
##   hitchhikers travelDistance driverGender country waitingTime carNumber
## 1    two guys         < 50km         male      CZ          10        NA
## 2    two guys      100 - 500         male   other          34        NA
## 3    two guys      100 - 500       female       D          30        NA
## 4    two guys      100 - 500         male       D          45        NA
## 5    two guys         < 50km         male       D          30        NA
## 6    two guys         < 50km         male       D          45        NA
##   dayNight driverCZ distance01 waitingTimeHours
## 1      day        M short_trip                1
## 2      day        M short_trip                1
## 3    night        Z short_trip                1
## 4      day        M short_trip                1
## 5      day        M short_trip                1
## 6      day        M short_trip                1
table(test.data$driverCZ, test.data$driverGender)
##    
##     female male
##   M      0  157
##   Z     23    0
table(test.data$distance01, test.data$travelDistance)
##             
##              < 50km > 500km 100 - 500 50 - 100
##   short_trip     98       0        49       25
##   long_trip       0       8         0        0
table(test.data$waitingTimeHours, test.data$waitingTime)[,1:20]
##     
##       0  1  2  3  4  5  6  7  8 10 12 13 14 15 16 18 19 20 22 25
##   0   1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   1   0  5  4  3  1 17  2  3  1 13  2  2  1  8  1  2  1  9  2  8
##   2   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   3   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   4   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   5   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   6   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   12  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

Následujúci výstup ale nepôsobí najlepším dojmom. Intuitívne by sme asi očakávali, že zoradenie jednotlivých kategórii bude logické, keďže sa jedná o tzv. ordinálny faktor (premennú s diskrétnymi, ale ordinalnými hodnotami).

table(test.data$distance01, test.data$travelDistance)
##             
##              < 50km > 500km 100 - 500 50 - 100
##   short_trip     98       0        49       25
##   long_trip       0       8         0        0

V programe R funguje automatick zoradenie faktorových úrovni podľa abecedy (vysvetlené a vyjadrené jednoducho). Rozumnejšie usporiadanie tabuľky získame jednoduchým príkazom:

test.data$travelDistance <-  ordered(test.data$travelDistance, levels = c("< 50km", "50 - 100", "100 - 500", "> 500km"))
table(test.data$distance01, test.data$travelDistance)
##             
##              < 50km 50 - 100 100 - 500 > 500km
##   short_trip     98       25        49       0
##   long_trip       0        0         0       8

Všimnite si, že na pôvodné premenné je naďalej možné odkazovať sa pomocou ich názvu, ale nové premenné týmto spôsobom sprístupnené nie sú. Z praktického hľadiska je vždy výhodnejšie odkazovať sa na premenné pomocou odkazu na konkrétny datov súbor - väčšinou totiž pracujeme s viacerými datovými súbormi naraz a zabráni sa takto nechcenej zámene, ktorú často ani nemusíme zaregistrovať. V prípade, že pracujeme pouze s jedným datovým súborom, postači opätovne zavolať príkaz

attach(test.data)

V programe R môžeme pracovať s datovými súbormi rôznych formátov a rôznych typov. Najčastejšie používané príkazy pre načítanie datového súboru z disku sú read.table() a read.csv(). K načítaniu datového súboru, ktorý je typický pre program R (súbor typu .RData) slúži príkaz load(). Hlavne prvé dva príkazy používajú množstvo rôznych parametrov, ktorými sa da presne špecifikovať konkrétny spôsob, ako data načítať (často používané parametre sú napr. header = , sep = , dec = , na.string = , as.is = , row.names = , col.names = a mnoho ďalších).

Porovnajte napr. nasledujúce dva príkazy:

tempData <- read.table("testData.txt", header = T, sep = ";", dec = ".")
tempData <- read.table("testData.txt", header = F, sep = ".", dec = ";", skip = 1)



Užitočné


  • Pre správne načítanie datového súboru je nutné poznať data a vedieť, ako sú uložené a akým spôsobom sú reprezentované (napr. ako sú označené chýbajúce pozorovania, aký znak sa používa na oddelenie desatinných miest, a pod.);
  • Užitočné príkazy pre prácu sa datami a manipuláciu s premennýmu sú aj class(), factor(), as.factor(), as.character(), as.numeric() a ďalšie. Pužijte help k jednotlivým príkazom a podívajte sa spôsob ich implemenácie, ilustračné príklady aplikácie a dotatočne podobné funkcie v referenciách;
  • Pomocou dodatočných knižníc je môžne načítať aj súbory z Excelu (príkaz read_excel() z knižnice library("readxl")), z SQL databázy (knižnica library(dbplyr)) mnoho ďalších typov súborov, vrátane obrázkov, videí, alebo mp3 súborov;
  • Pre program R množstvo niekoľko dodatočných knižníc, ktoré umožňujú načítať rôzne iné súbpry - napr. Excel tabuľku z xls alebo xlsx súborov, data z SQL databázy, ale aj obrázkove, filmové, či zvukové súbory.


2. Regular expressions in R

Ako bolo uvedené už pri prvom seminári, program R umožňuje prácu s tzv. “regular expressions” (regulárnymi výrazmi). Regulárne výrazy sú výstižným a flexibilným nástrojom na popis rôznych vzorov (patterns) v reťazcoch znakov. Práca s regulárnymi výrazmi často ulahčuje a zefektivňuje prácu s datami - hlavne pri príprave dat k analýze (vid help ?regex).

Zaujímavé ukážky a ilustračné príklady napríklad tu: https://cran.r-project.org/web/packages/stringr/vignettes/regular-expressions.html

Užitočné


  • Základné príkazy: grep(), grepl(), sub(), gsub(), match(), pmatch(), regexpr(), gregexpr() a mnoho ďalších;
  • Vyhľadávanie v texte: str_detect(), str_extract(), str_match(), str_subset();
  • Dátum a čas: strptime(), date(), as.Date(), as.POSIXlt(), POSIXct();
  • Vypisovanie: paste(), strsplit(), cat();


3. Základné popisné charakteristiky / exploratívna analýza

Na jednotlivé premenné sa môžeme odkazovať pomocou ich názvu. Môžeme takto spočítať základne emirické charakteristiky pre jednotlivé premenné. V programe R je k tomu celá rada rôznych (intuitivne implementovaných) funkcii: mean(), median(), min(), max(), sd(), var(), quantile(), a mnoho ďalších. Pomocou helpu k jednotlivých príkazom naštudujte, k čomu jednotlivé funkcie slúžia, ako sú v R implementované a nájdite ďalšie užitočné funkcie, ktoré sú vhodné pre základnú štatisticku analýzu dat.

mean(test.data$waitingTime)

Užitočný je aj samotný príkaz summary(), avšak ten poskytuje pouze popisné charakteristiky pre polohu (t.j., výberový priemer, výberový médian, výberové kvartily) a neposkytuje žiadnu informáciu o variabilite dat (napr. výberový rozptyl, výberová směrodatná odchýlka, výberové mezikvartilové rozpětí a pod.). Z tohto dôvodu nie je použitie príkazu summary() postačujúce a je nutné doplniť ho o aspoň nejakú informáciu o volatilite.

Analogicky, ako v prípade vektorov a matíc, môžeme odkazovať len na určitú časť datového súboru a spočítať empirické charakteristiky len pre konkrétnu (pod)skupinu:

mean(test.data$waitingTime[test.data$driverGender == "male"])
mean(test.data$waitingTime[test.data$driverGender == "female"])

Popisné charakteristiky je možné štrukturovať v závislosti na príslušnosti k nejakej skupine (konkrétna úroveň nejakéj faktorovej premennej).

with(test.data, tapply(waitingTime, driverCZ, summary))
with(test.data, tapply(waitingTime, country, summary))

alebo dokonca vytvárať vlastné špecifické výstupy pomocou vlastných pred-definovaných funkcii:

F1 <- function(x){
  temp <- round(c(min(x), mean(x), max(x), var(x), quantile(x, prob = 0.9)), 3)
  names(temp) <- c("Minimum", "Priemer", "Maximum", "Rozptyl", "90% kvantil")
  return(temp)
}

with(test.data, tapply(waitingTime, driverCZ, F1))
## $M
##     Minimum     Priemer     Maximum     Rozptyl 90% kvantil 
##       0.000      49.223     360.000    3851.200     120.000 
## 
## $Z
##     Minimum     Priemer     Maximum     Rozptyl 90% kvantil 
##       1.000      67.696     720.000   22651.676     110.000

U kategorických (resp. faktorových) premenných nie je štandardné uvádzať klasické výberové popisné charakteristiky. Namiesto toho je vhodne uviesť absolútne a relatívne počty v jednotlivých kategóriach.

table(test.data$country) ## absolutne pocty
## 
##    CZ     D    ES   EST     F   FIN other     P    PL   RUS 
##    63    11    11     9    32    16    14    10     6     8
table(test.data$country)/length(test.data$country) ## relativne pocty
## 
##         CZ          D         ES        EST          F        FIN 
## 0.35000000 0.06111111 0.06111111 0.05000000 0.17777778 0.08888889 
##      other          P         PL        RUS 
## 0.07777778 0.05555556 0.03333333 0.04444444

Prehľadnejšia forma zápisu:

round(table(test.data$country)/length(test.data$country), 2)

Prípadne viac štrukturované tabuľky vzhľadom k viacerým kategorickým premenným:

with(test.data, tapply(driverCZ, hitchhikers, table))

prrípadne relatívne počty, ktoré bývajú na prvý pohľad viac výpovedné:

F2 <- function(x){
  temp <- format(round(table(x)/length(x), 2), nsmall = 2)
  return(temp)
}

with(test.data, tapply(driverCZ, hitchhikers, F2))
## $`single guy`
## x
##      M      Z 
## "0.90" "0.10" 
## 
## $`two guys`
## x
##      M      Z 
## "0.78" "0.22"



Užitočné


  • Pomocou helpu sa podívajte na príkaz subset() a vyskúšajte, ako funguje;
  • Pomocou helpu naštudujte, k čomu slúžia funcie apply(), tapply(), lapply(), aggregate() a by();
  • Užitočné príkazy pre prácu s datami sú aj order() a sort();
  • Použijte tieto funkcie v súvislosti s datovým súborom ‘test.data’ a pomocou nich spočítajte niektoré empirické charakteristiky;
  • V pripade, že pracujeme v programe R len s jednym datovým súborom, je možné prácu (resp. implementáciu príkazov) mierne zjednodušiť - pomocou príkazu attach(test.data). V prípade, že takto zprístupnene premenné už neplánujeme využívať, je dobre využiť komplementárny príkaz detach().
  • Je dôležité správne pochopiť fungovanie Rka a odkazovanie na premenné po použití príkazu attach(). Program R totiž zprístupní premenné v stave, v akom sa v momente volania príkazu attach() aktuálne nachádzaju v data.frame. Akýkoľvek následný zásah do data.framu a zmena hodnôt v ňom, nebude reflektovaná ak sa na premennú odkážeme pomocou jej názvu a nie priamo cez odkaz na data.frame;


4. Príprava a programovanie vlastných skriptov

Štatistický program R slúži aj ako samostatný programovací jazyk: pomocou tohto programu je možné naprogramovať takmer akúkoľvek úlohu. Je pri tom užitočné poznať rôzne nástroje a konkrétne funkcie, ktoré k tomuto účelu môžu poslúžiť (napr. implementácia cyklov for a while, alebo overovanie podmienok pomocou if).

  • for cyklus:

    for (i in 1:10){
      print(1:i)
    }
  • while cyklus:

    i <- 1
    while (i < 10){
      print(1:i)
      i <- i + 1
    }
  • overovanie podmienky pomocou if

    N <- rpoiss(1)
    if (N %% 2 == 0){
      print("Sude cislo")
    } else {
      print("Liche cislo")
    }
  • rozlíšovanie prípadov pomocou else if

    if (x < 0) {
    print("Zaporne cislo")
    } else if (x > 0) {
    print("Kladne cislo")
    } else
    print("Nula")

Pri používaní cyklov je dôležité dôsledne skontrolovať skript - môže sa totiž veľmi jednoducho stáť, že program R neúmyselne zacyklíme. V takom prípade je dobré poznať klávesovú skratku CTR + C (v konzole programu R), prípadne ESC v interface RStudio.



Uvedené cykly môžu byť užitočné napr. pri skúmaní dat, počítaní popisných charakteristík pre jednotlivé premenné a podobne.

vars <- names(test.data) ### premenne v datach
Nvars <- vars[c(5,6)]

for (var in Nvars){
  print(paste("Popisne charakteristiky pre promennu", var, sep = " "))
  print(F1(test.data[!is.na(test.data[, var]), var]))
  print("-------------------------------------------------------")
}

Jednotlivé výstupy ale dostaneme aj samostatne pomocou dvojice príkazov:

F1(test.data[, c("waitingTime")])
F1(test.data[!is.na(test.data[, "carNumber"]), "carNumber"])

Vo väčšine prípadov je možné požadované riešenie (požadovaný výstup) získať aj bez pomoci cyklov, pouze s využitím štandardne implementovaných R-kových príkazov a funkcii.



Užitočné


  • Jednotlivé príkazy je možné v programe R vzájomne kombinovať a vytvárať z nich komplexné Rkové skripty. K dispozícii je samozrejme mnoho ďalších nástrojov určených k pokročilému programovaniu (viď napr. help a rôzne tutoriály).


Domáca úloha

(Deadline: 3. cvičenie | St: 03.11.2021)

Použijte datový súbor, ktorý ste si pripravili v predchádzajúcej domácej úlohe a urobte nasledujúce:

  • Pomocou príkazu transform() vytvorte dve ďalšie premenné.
  • Vytvorte si vlastnú funkciu a pomocou tejto funkcie spočítajte niektoré základné popisné charakteristiky k aspoň dvom premenným.
  • Využijte príkaz with() a urobte stejné popisné charakteristiky, ale rozdelené do skupín príslušných niektorej faktorovej premennej.