6 Paketi, ugrađene funkcije i okoline


6.1 Rad s paketima

Standardna R distribucija dolazi sa dvije kolekcije paketa (nazvanih r-base i r-recommended) koje sadrže svojevrsnu “jezgru” jezika R - skup elemenata dostatnih za provođenje standardnih tipova podatkovnih analiza uz pomoć programskog jezika R. Uz to, CRAN (Comprehensive R Archive Network) predstavlja bogati repozitorij dodatnih paketa za najrazličitije primjene, od “popravljanja” osnovnih elemenata jezika R do strogo specijaliziranih paketa za posebne tipove analiza.

Kao što je uobičajena praksa u drugim programskim jezicima, R koristi sustav “paketa” ili “biblioteka” (engl. package ili library) kako bi logički organizirao već “isprogramirane” kolekcije podataka, skupova i prevedenog programskog koda.

Kod podizanja R okoline automatski se učitavaju određeni paketi u radnu memoriju čime njihovi elementi postaju dostupni za korištenje. Popis učitanih paketa možemo dobiti korištenjem funkcije search.


Zadatak 6.1 - staza pretrage

##  [1] ".GlobalEnv"        "package:kernlab"   "package:ranger"   
##  [4] "package:caret"     "package:e1071"     "package:corrplot" 
##  [7] "package:broom"     "package:car"       "package:carData"  
## [10] "package:Hmisc"     "package:Formula"   "package:survival" 
## [13] "package:lattice"   "package:sn"        "package:stats4"   
## [16] "package:gridExtra" "package:RSQLite"   "package:hflights" 
## [19] "package:lubridate" "package:GGally"    "package:forcats"  
## [22] "package:stringr"   "package:dplyr"     "package:purrr"    
## [25] "package:readr"     "package:tidyr"     "package:tibble"   
## [28] "package:ggplot2"   "package:tidyverse" "package:MASS"     
## [31] "package:stats"     "package:graphics"  "package:grDevices"
## [34] "package:utils"     "package:datasets"  "package:methods"  
## [37] "Autoloads"         "package:base"

Vidimo da većina paketa ima svoju stavku oblika "package:ime_paketa". Raspored paketa predstavlja i njihov “prioritet” glede pretrage imenskog područja, o čemu će više riječi biti kasnije.

Ukoliko želimo učitati novi paket u našu okolinu, to možemo izvesti uz pomoć funkcije library kojoj dajemo ime paketa (bez navodnika).


Zadatak 6.2 - učitavanje paketa u radnu okolinu


Naredba iz prethodnog primjera može imati dva ishoda: ukoliko paket postoji na lokalnom računalu (u mapi predodređenoj za dodatne pakete), on će biti učitan u radnu okolinu. Učitavanje paketa može biti popraćeno porukama o objektima koji su nakon učitavanja “maskirani”. To konkretno znači da je novi paket privremeno uskratio dostupnost pojedinim elementima iz ranije učitanih paketa iz razloga što im se podudaraju imena. Ovo često ne predstavlja nikakav problem, ali ukoliko korisnik ima potrebu za pristupom maskiranim elementima morati će koristiti njihovo “puno” ime tj. morati će navesti i ime paketa gdje se nalaze. Na primjer, ako je funkcija filter iz paketa stats maskirana nakon učitavanja novog paketa, ona je i dalje dostupna preko punog imena stats::filter ali ne direktno preko filter, budući da će to pozivati funkciju iz najnovije učitanog paketa. Više detalja o tome kako R razrješava nazive varijabli i funkcija biti će dano u nastavku ove lekcije.

Ukoliko na lokalnom računalu nemamo navedeni paket dobivamo poruku o grešci da taj paket ne postoji. U tom slučaju potrebno je prvo dohvatiti paket iz CRAN repozitorija uz pomoć funkcije install.packages kojoj dajemo naziv jednog ili više paketa (s navodnicima!) kao parametre. Navedena funkcija pretpostavlja da R okolina ima definiran CRAN mirror tj. konkretnu adresu CRAN repozitorija odakle će se paket dohvatiti. Veliki broj država ima svoju “kopiju” CRAN repozitorija, no nažalost Republika Hrvatska iz nejasnih razloga više nema svoj CRAN repozitorij te u trenutku pisanja ove bilježnice nema pokazatelja da će se isti uspostaviti. Ako radimo u sučelju RStudio, CRAN repozitorij smo vrlo vjerojatno postavili kod prvog pokretanja, (tj. odabrali smo opciju Global koja naše zahtjeve za instaliranjem paketa automatski prosljeđuje najbližem CRAN repozitoriju) no ako to nismo obavili ili radimo u nekom drugom razvojnom sučelju onda uz pomoć dokumentacije moramo potražiti način postavljanja CRAN repozitorija ako želimo učitavati dodatne pakete.


Zadatak 6.3 - instalacija paketa sa CRAN repozitorija

##  [1] ".GlobalEnv"        "package:kernlab"   "package:ranger"   
##  [4] "package:caret"     "package:e1071"     "package:corrplot" 
##  [7] "package:broom"     "package:car"       "package:carData"  
## [10] "package:Hmisc"     "package:Formula"   "package:survival" 
## [13] "package:lattice"   "package:sn"        "package:stats4"   
## [16] "package:gridExtra" "package:RSQLite"   "package:hflights" 
## [19] "package:lubridate" "package:GGally"    "package:forcats"  
## [22] "package:stringr"   "package:dplyr"     "package:purrr"    
## [25] "package:readr"     "package:tidyr"     "package:tibble"   
## [28] "package:ggplot2"   "package:tidyverse" "package:MASS"     
## [31] "package:stats"     "package:graphics"  "package:grDevices"
## [34] "package:utils"     "package:datasets"  "package:methods"  
## [37] "Autoloads"         "package:base"

Napomena: u pravilu pakete instaliramo samo jednom i to preko konzole tako da nikad nema potrebe naredbe za instalaciju paketa ugrađivati u R Markdown dokumente; također, zbog lakše organizacije izvještaja, učitavanje svih potrebnih paketa se po konvenciji obavlja na početku dokumenta, u isječku koda nazvanom setup.


Uočimo da će instalacija i učitavanje paketa automatski sa sobom povući i učitavanje svih paketa koji su preduvjeti za korištenje traženog paketa, što uvelike olakšava rad korisniku koji se ne mora brinuti o tome što “dodatno” treba instalirati kako bi mogao koristiti elemente paketa.

Ako želimo saznati više informacija o nekom paketu, to također možemo izvesti uz pomoć funkcije library uz parametar help postavljen na ime paketa.

Još jedan prilično popularan način dokumentiranja paketa je uz pomoć tzv. “vinjeta” (engl. vignettes). Vinjete su zapravo “mini-tutorial” nekog paketa u HTML obliku koji služi za predstavljanje funkcionalnosti paketa na pristupačan, čitljiv način uz pomoć detaljnih objašnjenja i pripadajućeg programskog koda. Možemo pogledati koje vinjete su instalirane na sustav pozivom funkcije browseVignettes() bez parametara (ili opcionalno dodati kao parametar imena paketa ako nas zanimaju samo njegove vinjete). Ako paket ima samo jednu vinjetu (npr. paket stringr). Možemo također odmah otvoriti vinjetu uz pomoć funkcije vignette(ime_paketa).


6.2 Ugrađene funkcije

U prethodnim poglavljima već smo upoznali neke od gotovih funkcija koje dobijamo zajedno sa našom R distribucijom. To su npr. numeričke funkcije (log, abs, sqrt, round i sl.), funkcije za stvaranje vektora (rep, seq i sl.), funkcije za rad s paketima (install.packages, library i sl.) i tako dalje.

U R-u se rijetko govori o “ugrađenim” funkcijama budući da - kao što je već prikazano - R okolina automatski učitava neke često korištene pakete čiji su elementi odmah dostupni za korištenje, bez nužnog navođenja imena paketa u kojem se nalaze. Npr. paket stats sadrži bogati skup funkcija vezanih uz statističke obrade. Jedna od tih funkcija je i rnorm koja vraća numerički vektor željene duljine čiji su elementi nasumično odabrani iz normalne distribucije sa aritmetičkom sredinom 0 i standardnom devijacijom 1 (ove vrijednosti možemo i promijeniti uz pomoć parametara mean i sd). Ukoliko želimo, ovu funkciju možemo pozvati uz pomoć sintakse ime_paketa::ime_funkcije(parametri).


Zadatak 6.4 - poziv funkcije iz definiranog paketa


Iako je ovo sintaksno korektan način pozivanja funkcije, R nam omogućuje da izuzmemo nazive paketa i jednostavno navedemo samo naziv funkcije.


Zadatak 6.5 - poziv funkcije bez imena paketa


Možemo se zapitati - kako R zna gdje se nalazi funkcija koju želimo pozvati, ako nismo eksplicitno učitali niti naveli paket koji sadrži tu funkciju? Točan razlog objasniti ćemo u idućem poglavlju koje se bavi okolinama i već viđenom “stazom pretrage”.

Popisivanje svih dostupnih funkcija, pa čak i onih češće korištenih, bilo bi redundantno budući da se R jezik najbolje uči uz konkretnu primjenu, pri čemu nakon određenog vremena korištenja korisnik polako oformi “vlastiti” skup funkcija koje predstavljaju njegov uobičajeni “alat” za analize. U svakom slučaju, svakako se preporučuje odabir nekog od javno dostupnih R podsjetnika (reference card ili cheat sheet) koji će onda uvijek biti pri ruci za vrijeme R programiranja.

Pojedini R podsjetnici dostupni su i na samom CRAN-u (dovoljno je upisati CRAN reference card u tražilicu i pregledati rezultate), no ovdje ćemo iskoristiti priliku i preporučiti izvrsne “šalabahtere” na stranicama sučelja RStudio dostupne na sljedećoj poveznici: https://www.rstudio.com/resources/cheatsheets/ (alternativno, upišite RStudio cheat sheets u tražilicu). Ovi podsjetnici sadržajno pokrivaju većinu korisnih stvari vezanih uz općenite elemente jezika R (Base R, Advanced R) ali i konkretne pakete koje ćemo kasnije upoznati (dplyr, ggplot2) te predstavljaju vrlo vrijedne resurse kako za učenje R-a, tako i za dugoročno korištenje. Za lakše praćenje lekcija koje slijede, preporučujemo ispis navedenih podsjetnika i upoznavanje sa elementima koje sadržavaju, budući da se predstavlja o dugoročno korisnom pomagalu za programiranje u jeziku R.

Za kraj, spomenimo da nam R omogućuje brzo dohvaćanje pomoći o funkciji jednostavnim pozivom ?ime_funkcije ili help(ime_funkcije) te da možemo dobiti primjere korištenja funkcije kroz example(ime_funkcije). Ove pozive bismo trebali vrlo često koristiti čak i ako smatramo da smo dobro upoznati sa funkcijom koju pozivamo - lako je moguće da postoji neki dodatni parametar (ili srodna funkcija koje se također često navode u dokumentaciji) a koji će nam dodatno pomoći u obavljanju zadatka zbog kojeg funkciju i koristimo.


6.3 Okoline

Kao što je već rečeno, rad u R-u svodi se na upravljanje različitim objektima. Kako bismo uopće mogli upravljati tim objektima, potrebni su nam mehanizmi uz pomoć kojih referenciramo dotične objekte. U R-u se to zove “povezivanje” (engl. binding). Kada stvorimo varijablu x numeričkog tipa i njoj pridružimo broj 5, mi smo zapravo stvorili (jednoelementni) numerički vektor i “povezali” taj podatak sa nizom znakova "x" kojeg potom možemo koristiti za daljnje referenciranje tog podatka (ili tih podataka).

Stoga, kada želimo pristupiti nekoj varijabli, R mora pretražiti svoj interni “zapisnik” koje varijable trenutno postoje, kojeg su tipa te kako im pristupiti. Kako bi R pronašao varijablu, on koristi mehanizam zvan “leksičko uokviravanje” (engl. lexical scoping) koji se temelji na konceptu zvanom okoline (engl. environments).

“Okolina” se često naziva “vrećom za nazive” (engl. bag of names). Ona nam pomaže da logički grupiramo nazive objekata koje koristimo i da pomognemo R-u naći naziv u drugim okolinama ako isti ne postoji u trenutnoj okolini. Ovo potonje je omogućeno na način da (gotovo) svaka okolina ima poveznicu na svoju okolinu-roditelja (engl. parent environment). Ovakav sustav poveznica na okoline-roditelje stvara svojevrsnu “hijerarhiju okolina” koja se često naziva i “staza pretrage” (engl. search path); R, tražeći zadano ime varijable, pretražuje okoline “uzlazno” sve dok ne pronađe prvu pojavu traženog naziva ili naleti na krajnju okolinu bez roditelja (tzv. prazna okolina - engl. empty environment). Ono što je interesantno jest činjenica da je i sama okolina objekt - možemo stvoriti referencu na nju, slati ju u funkcije i sl.

“Osnovna” okolina u kojoj radimo i u kojoj stvaramo nove varijable je tzv. globalna okolina, ili .GlobalEnv (paziti na točku!). Ona je na dnu hijerarhije okolina. Možemo dohvatiti referencu na nju preko istoimene varijable, ili se poslužiti funkcijom globalenv().


Zadatak 6.6 - globalna okolina


Iz zadnjeg primjera možemo vidjeti da okolina bez problema može čuvati i referencu na samu sebe obliku varijable e, tako da je ovo zapravo potpuno ispravna (iako nepotrebno komplicirana) sintaksa za ispis varijable x:

Okoline su u izvjesnoj mjeri slične listama, koje su isto zapravo svojevrsni način “enkapsulacije” niza objekata u jedinstvenu strukturu. Najbitnije razlike između okoline i liste su:

  • poredak elemenata u okolini je nebitan
  • okolina (u pravilu) ima poveznicu na okolinu roditelja

Pogledajmo tko je okolina-roditelj globalnoj okolini uz pomoć funkcije parent.env.


Zadatak 6.7 - okoline roditelji

## <environment: package:kernlab>
## attr(,"name")
## [1] "package:kernlab"
## attr(,"path")
## [1] "C:/R/R-3.5.1/library/kernlab"

Pomalo neočekivano, roditelj globalne okoline jest zadnje učitani paket! Ovo zapravo nije neobično - globalna okolina ima “prioritet” kod referenciranja varijable, ali prioritetno odmah “ispod” nje su oni objekti i funkcije koje smo zadnje učitali u okolinu (što nam odgovara budući da je pretpostavka da je “najsvježije” učitani paket onaj kojeg namjeravamo odmah koristiti). Drugim riječima, učitavanjem paketa novi paket se uvijek “namjesti” između globalne okoline i paketa koji je prije njega bio zadnji učitan. Kada smo pozivali funkciju search, zapravo smo dobili hijerarhiju okolina koje predstavljaju učitane pakete. Ova hijerarhija okolina ujedno predstavlja i već spominjanu “stazu pretrage”.

Uz pomoć funkcije parent.env možemo sami odrediti koju okolinu će neka okolina smatrati roditeljem. Na ovaj način možemo napraviti vlastitu hijerarhiju okolina. Nadalje varijable koje stvaramo ne moraju koristiti reference iz globalne okoline (što je zapravo i osnovna funkcija operatora <-), mi ih možemo pohraniti u bilo koju okolinu koju želimo, no za to se moramo koristiti funkcijama assign i get ili kombinacijom operatora $ i <-.



Zašto bi koristili okoline u praksi? Okolina predstavlja zgodan način “omatanja” skupa varijabli koje onda zajedno možemo slati u neku funkciju - što je pogotovo zgodno ako dotične varijable referenciraju neke velike skupove podataka. Kao što ćemo vidjeti u sljedećoj lekciji, R ne podržava tzv. call-by-reference princip već kod slanja objekata u funkciju R koristi tzv. copy-on-modify mehanizam. Ovo znači da će funkcija koristiti referencu na originalni objekt poslan u nju kao parametar sve do naredbe koja taj objekt odluči mijenjati; u tom trenutku stvara se kopija tog objekta i tek onda se provode izmjene. Ovo može dovesti do značajnih usporavanja programa kod programera koji nisu upoznati s ovom činjenicom a koji npr. programiraju funkciju koja transformira podatkovni okvir. Ako funkciji umjesto reference na podatkovni okvir pošaljemo referencu na okolinu u koju je “zamotan” okvir, onda neće doći do kopiranja varijable jer je okolina jedini objekt za kojeg copy-on-modify ne vrijedi. Uočimo da je specijalni slučaj ove metode “slanje” globalne okoline u funkciju, što se zapravo svodi na korištenje “globalne varijable” - nečega što se u drugim programskim jezicima često izbjegava, ali u R-u nije pretjerano rijetka pojava upravo zbog činjenice da nas znatno usporavanje programa puno više smeta od potencijalnih problema koje globalne varijable sa sobom donose.

Za kraj demonstrirajmo rad funkcije attach koju analitičari često koriste kako bi ubrzali postupak analize ali koja može uzrokovati probleme ako nismo pažljivi sa njenim korištenjem. Ova funkcija ubaciti će podatkovni okvir direktno u stazu pretrage kako bi nam omogućila “lakši” pristup varijablama, ni uz potencijalne nezgodne nuspojave. Pogledajmo ovo na primjeru.


Zadatak 6.8 - funkcija attach

##  [1] ".GlobalEnv"        "mjesto"            "package:kernlab"  
##  [4] "package:ranger"    "package:caret"     "package:e1071"    
##  [7] "package:corrplot"  "package:broom"     "package:car"      
## [10] "package:carData"   "package:Hmisc"     "package:Formula"  
## [13] "package:survival"  "package:lattice"   "package:sn"       
## [16] "package:stats4"    "package:gridExtra" "package:RSQLite"  
## [19] "package:hflights"  "package:lubridate" "package:GGally"   
## [22] "package:forcats"   "package:stringr"   "package:dplyr"    
## [25] "package:purrr"     "package:readr"     "package:tidyr"    
## [28] "package:tibble"    "package:ggplot2"   "package:tidyverse"
## [31] "package:MASS"      "package:stats"     "package:graphics" 
## [34] "package:grDevices" "package:utils"     "package:datasets" 
## [37] "package:methods"   "Autoloads"         "package:base"     
## -------------------------
## [1] 10000 51000 21000 31000  2000
## -------------------------
##     pbr nazivMjesta prosjPlacaKn brojStanovnika prirez
## 1 10000      Zagreb         6359         790017     18
## 2 51000      Rijeka         5418         128384     15
## 3 21000       Split         5170         167121     10
## 4 31000      Osijek         4892          84104     13
## 5  2000   Dubrovnik         5348          28434     10
## -------------------------

Objasnimo što se dogodilo u gornjem primjeru. Uz pomoć funkcije attach podatkovni okvir mjesto postao je “mini-okolina”, tj. njegovi stupci postali su dostupni unutar staze pretrage. Očiti benefit ovoga jest to da ih možemo referencirati direktno, bez reference na originalni podatkovni okvir i operatora $. No ovaj naočigled praktičan trik ima skrivene zamke - prvo, ako se imena stupaca podudaraju sa postojećim varijablama globalne okoline, onda ti stupci neće biti vidljivi (o ovom ćemo biti obaviješteni adekvatnim upozorenjem). Drugo - i puno problematičnije - ako pokušamo mijenjati stupac okvira direktnim referenciranjem, R će to spriječiti i potiho će primijeniti copy-on-modify princip stvaranjem nove, globalne varijable koja će biti kopija referenciranog stupca. Neopreznom analitičaru tako može promaknuti činjenica da se promjene koje naizgled unosi uopće ne odražavaju na samom podatkovnom okviru, što može imati dalekosežne posljedice.

Ovi potencijalni problemi su vrlo rašireni među početnicima u jeziku R tako da se u literaturi često može naći preporuka da se funkcija attach ne koristi ukoliko to iz nekog razloga nije nužno. Npr. Google-ov stilski vodič za R kaže “mogućnosti greške kod korištenja funkcije attach su brojne, zato ju izbjegavajte”.

Još jedna mogućnost koju imamo jest korištenje funkcije with kojoj prvo dajemo referencu na okolinu, a potom izraz koji koristi varijable iz te okoline:

no u većini slučajeva korištenje ove funkcije neće rezultirati čitljivom sintaksom već upravo naprotiv - programski kod se dodatno komplicira. Ukoliko želimo izbjeći stalno referenciranje podatkovnog okvira u izrazima, za to postoje novi paketi upravo namjenjenih lakom upravljanju podatkovnih okvirima uz pomoć čiste i pregledne sintakse, kao npr. dplyr ili tidyr. Ove pakete upoznati ćemo u jednoj od nastupajućih lekcija.


Zadaci za vježbu

  1. Učitajte sljedeće pakete u radnu okolinu: magrittr, dplyr, tidyr, ggplot2. Ispišite stazu pretrage i provjerite gdje se nalaze učitani paketi.

  2. Sljedeća naredba stvoriti će vektor od 20 nasumično odabranih prirodnih brojeva od 1 do 100. Uz pomoć podsjetnika i/ili službene dokumentacije pronađite ugrađene funkcije koje izvršavaju zadane zadatke.

ispišite:

  • vektor a
  • vrijednosti vektora a poredane obrnutim redoslijedom
  • jedinstvene vrijednosti iz vektora a
  • vrijednosti vektora a poredane uzlazno
  1. Spomenuli smo da su učitani paketi zapravo “okoline”. Ukoliko želimo dobiti direktnu referencu na njih, moramo se poslužiti funkcijom as.environment i imenom paketa. Pokušajte dobiti referencu na paket package:magrittr u obliku okoline te uz pomoć funkcije ls provjerite koje nazive ona sadrži u sebi.

Creative Commons License
Programirajmo u R-u by Damir Pintar is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
Based on a work at https://ratnip.github.io/FER_OPJR/