79
Osnove programiranja u R-u S760

Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

  • Upload
    others

  • View
    7

  • Download
    1

Embed Size (px)

Citation preview

Page 1: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u

S760

Page 2: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Ovu inačicu priručnika izradio je autorski tim Srca u sastavu:

Autori: dr. sc. Damir Pintar i dr. sc. Mihaela Vranić

Recenzentica: mr. sc. Melita Perčec Tadić

Urednica: Sabina Rako

Lektorica: Mia Kožul

Sveučilište u Zagrebu

Sveučilišni računski centar

Josipa Marohnića 5, 10000 Zagreb

[email protected]

ISBN 978-953-8172-45-8 (meki uvez) ISBN 978-953-8172-46-5 (PDF)

Verzija priručnika: 20200205

Ovo djelo dano je na korištenje pod licencom Creative Commons Imenovanje-Nekomercijalno-Dijeli pod istim uvjetima 4.0 međunarodna. Licenca je dostupna na stranici: http://creativecommons.org/licenses/by-nc-sa/4.0/.

Page 3: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Sadržaj

1. Uvod .................................................................................................................................... 1

1.1. Osnovne informacije ...................................................................................................... 1

1.2. Osnovno o jeziku R ....................................................................................................... 2

1.2.1. R – programsko okruženje za statističke metode, vizualizaciju, eksploratornu i

dubinsku analizu podataka ................................................................................................... 2

1.2.2. Načini učenja R-a ................................................................................................... 2

1.2.3. Pregled sadržaja tečaja .......................................................................................... 3

2. Osnovni tipovi podataka i podatkovne strukture ............................................................. 4

2.1. Osnovni tipovi podataka ................................................................................................ 4

2.1.1. Pregled osnovnih tipova ......................................................................................... 4

2.1.2. Pojam varijable ....................................................................................................... 4

2.1.3. Provjera tipa podatka ............................................................................................. 5

2.2. Složene podatkovne strukture ....................................................................................... 6

2.2.1. Vektor .................................................................................................................... 6

2.2.2. Operator [ ............................................................................................................... 7

2.2.3. Matrice i polja ......................................................................................................... 8

2.2.4. Liste ..................................................................................................................... 10

2.2.5. Podatkovni okviri (Data frames) ........................................................................... 11

3. Vektorizacija i indeksni vektori ....................................................................................... 14

3.1. Principi vektorizacije i recikliranja ................................................................................ 14

3.1.1. “Sve je vektor” ...................................................................................................... 14

3.1.2. Matrica kao vektor ................................................................................................ 14

3.1.3. Lista kao vektor .................................................................................................... 15

3.1.4. Podatkovni okvir kao vektor .................................................................................. 16

3.1.5. Vektorizirane operacije i princip recikliranja .......................................................... 17

3.2. Indeksni vektori ........................................................................................................... 19

3.2.1. Definicija indeksnoga vektora ............................................................................... 19

3.2.2. Lokacijsko indeksiranje ........................................................................................ 20

3.2.3. Uvjetno indeksiranje ............................................................................................. 22

3.2.4. Imensko indeksiranje ............................................................................................ 23

3.2.5. Indeksni vektori i matrice ...................................................................................... 24

3.2.6. Indeksni vektori i podatkovni okviri ....................................................................... 24

3.3. Dodatni zadaci za vježbu............................................................................................. 26

Page 4: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

4. R i programske paradigme .............................................................................................. 27

4.1. Što su programske paradigme?................................................................................... 27

4.1.1. Općenito o programskim paradigmama ................................................................ 27

4.1.2. Imperativna programska paradigma ..................................................................... 27

4.1.3. Objektna programska paradigma ......................................................................... 27

4.1.4. Funkcijska programska paradigma ....................................................................... 27

4.2. Programski jezici i programske paradigme .................................................................. 28

4.2.1. Odabir jezika = odabir paradigme? ....................................................................... 28

4.2.2. R i programske paradigme ................................................................................... 28

5. Upravljanje programskim tokom ..................................................................................... 29

5.1. Uvjetno izvođenje naredbi ........................................................................................... 29

5.1.1. Naredba if - else ................................................................................................... 29

5.1.2. Naredba ifelse ...................................................................................................... 29

5.2. Programske petlje ....................................................................................................... 30

5.2.1. R i programske petlje ........................................................................................... 30

5.2.2. Petlja repeat ......................................................................................................... 30

5.2.3. Petlja while ........................................................................................................... 31

5.2.4. Petlja for ............................................................................................................... 31

5.2.5. Korištenje programskih petlji u jeziku R ................................................................ 32

6. Funkcije u jeziku R ........................................................................................................... 33

6.1. Ugrađene funkcije ....................................................................................................... 33

6.1.1. Paketi i staza pretrage .......................................................................................... 33

6.1.2. Dohvat pomoći ..................................................................................................... 34

6.1.3. Klasične, primitivne i interne funkcije .................................................................... 35

6.2. Korisnički definirane funkcije ....................................................................................... 37

6.2.1. Sintaksa pisanja funkcije ...................................................................................... 37

6.2.2. Princip kopiranja-kod-izmjene (engl. copy-on-change) ......................................... 39

6.2.3. Anonimne funkcije ................................................................................................ 41

7. Objekti u jeziku R ............................................................................................................. 42

7.1. Različiti objektni modeli jezika R.................................................................................. 42

7.1.1. Pregled objektnoga modela S3 ............................................................................ 42

7.1.2. Generičke funkcije ................................................................................................ 44

7.2. Stvaranje vlastitih konstruktorskih i generičkih funkcija ................................................ 45

7.2.1. Konstruktorske funkcije ........................................................................................ 45

7.2.2. Vlastite generičke funkcije .................................................................................... 46

Page 5: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

7.3. Dodatni zadaci za vježbu............................................................................................. 47

8. Deklarativne programske petlje ...................................................................................... 49

8.1. Porodica funkcija apply ............................................................................................... 49

8.1.1. Općenito o porodici funkcija apply ........................................................................ 49

8.1.2. Funkcija apply ...................................................................................................... 50

8.1.3. Funkcija lapply ..................................................................................................... 52

8.1.4. Funkcija sapply .................................................................................................... 54

8.1.5. Ostale funkcije iz porodice apply .......................................................................... 56

8.2. Alternativa funkcijama iz porodice apply ...................................................................... 56

8.2.1. Paket purrr ........................................................................................................... 56

8.2.2. Paket plyr ............................................................................................................. 57

9. Učinkovito programiranje i upravljanje podatkovnim skupovima ................................ 58

9.1. Operator cjevovoda ..................................................................................................... 58

9.1.1. Korištenje operatora cjevovoda ............................................................................ 58

9.1.2. Operator cjevovoda i drugi operatori .................................................................... 60

9.2. Učinkovito upravljanje podatkovnim skupovima ........................................................... 61

9.2.1. Paket dplyr ........................................................................................................... 61

9.2.2. Odabir redaka ...................................................................................................... 64

9.2.3. Odabir stupaca ..................................................................................................... 65

9.2.4. Agregacija i grupiranje .......................................................................................... 67

10. Zaključak ........................................................................................................................... 70

10.1. Dodatni zadaci za vježbu ......................................................................................... 72

Page 6: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje
Page 7: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________1

1. Uvod

1.1. Osnovne informacije

U tečaju Osnove programiranja u R-u (S760) obrađuje se jezik R s programerskog stajališta.

Polazniku se pruža uvid u osnovne elemente jezika R uz obrazloženje njihovih specifičnosti u

odnosu na druge programske jezike. Posebna pažnja posvećena je principima vektorizacije i

recikliranja, te različitim načinima korištenja indeksnih vektora za učinkovito upravljanje

skupovima podataka. Također se polaznike upoznaje s funkcionalnom orijentiranošću jezika

R i njezinim učinkovitim korištenjem u pisanju vlastitoga programskog kôda. Obrađuju se

različiti tipovi funkcija, načini stvaranja vlastitih funkcija te daju savjeti o tome kako izbjeći

klasične zamke kod pisanja programskoga kôda u R-u. Konačno, polazniku se daje kratki

osvrt na neke od popularnijih dodatnih paketa jezika R koji omogućuju pisanje čistog,

jednostavnog i preglednog programskog kôda.

Tečaj je namijenjen studentima, djelatnicima visokih učilišta i javnih instituta, zaposlenicima

tvrtki i institucija te ostalim zainteresiranima.

Za pohađanje ovoga tečaja potrebno je poznavanje osnova rada na računalu i operacijskom

sustavu MS Windows te poznavanje osnova rada na Internetu. Minimalno iskustvo u

programiranju je prednost.

U ovom su priručniku naredbe pisane proporcionalnim slovima (na primjer, naredba

install.packages()).

Sintaksa naredbi pisana je proporcionalnim slovima te komentarima za dijelove koje program

ne izvodi: >library(help = "base") #pomoć za paket base.

Gradivo je uglavnom obrađeno kroz niz primjera i vježbi. Rješenja vježbi i rezultati izvođenja

programskih naredbi dani su u interaktivnim prozorima (HTML inačica) ili na kraju priručnika

(PDF inačica).

Ovaj priručnik predviđa korištenje elektroničkih radnih bilježnica koje uz njega čine nedjeljivu

cjelinu.

Page 8: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________2

1.2. Osnovno o jeziku R

1.2.1. R – programsko okruženje za statističke metode, vizualizaciju,

eksploratornu i dubinsku analizu podataka

U današnje doba postoji vrlo bogata ponuda različitih programskih jezika. Za razliku od jezika

koju su ciljano dizajnirani kao “jezici općenite namjene”, kao što su Java, C++ ili Python,

postoje i jezici čiji je dizajn usredotočen na točno određenu svrhu. Jezik R primjer je

ovakvoga jezika – iako sadrži sve nužne elemente prema kojima bismo ga mogli također

uvrstiti u skup jezika opće namjene, njegova primarna uloga jest podrška za eksploratornu,

statističku i dubinsku analizu podatkovnih skupova, uglavnom na interaktivni način

izvođenjem naredbi uz pomoć programske konzole.

Za jezik R često se kaže da su ga “dizajnirali statističari za statističare”. Ako pojam

“statističar” zamijenimo širim pojmom “podatkovnog analitičara” i uzmemo u obzir domensku

orijentiranost jezika onda ovu izjavu možemo interpretirati u pozitivnom smislu, da su

dizajneri jezika R ljudi koji razumiju potrebe analitičara i koji su jezik oblikovali upravo kako bi

istima rad učinili što lakšim i učinkovitijim. No, izjavu je lako interpretirati i u smislu da ovo nije

jezik smišljen “od programera za programere”, tj. da učenjem jezika R moramo biti spremni

na rušenje određenih konvencija i predrasuda te prihvaćanje činjenice da “R-ovski” pristup

programiranju nije nužno u skladu s pristupom kojim bismo se koristili u nekom “klasičnijem”

programskom jeziku. R je jezik koji primarno želi omogućiti korisniku/analitičaru postizanje

rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih

struktura i funkcija odražava ovu filozofiju. Postoje jasni razlozi zašto se jezik R nametnuo

kao de facto standard u području podatkovne znanosti te zašto je toliko dobro prihvaćen i od

strane korisnika čija struka nije primarno programerska. No, za učinkovito svladavanje R-a

vrlo je bitno odabrati učinkovit način učenja ovoga jezika.

1.2.2. Načini učenja R-a

Posebnosti jezika R mogu uzrokovati poteškoće kod početnika koji se priklanjaju dvama –

podjednako pogrešnim pristupima učenju ovoga jezika. Prvi je da se R-u pristupa kao jeziku

opće namjene te da ga se pokušava naučiti “klasičnom” metodologijom učenja programskih

jezika, upoznavajući njegove elemente, programske strukture, principe pisanja progresivno

složenijih programa i sl. Ovakav način učenja R-a nije nemoguć jer R sadrži sve nužne

elemente jezika opće namjene te ga je i moguće koristiti za primjene koje nisu usko

povezane s podatkovnom znanosti. Problem nastaje ako se tijekom učenja sustavno

zanemaruje orijentiranost R-a prema analizi podataka. Razdruživanjem elemenata R-a kao

programskoga jezika i njegove svrhe naići ćemo na niz prividnih nelogičnosti i

nekonvencionalnih elemenata koji su teško objašnjivi ako ne razmišljamo u kontekstu

upotrebe istih u okvirima podatkovne analize, kada oni najčešće postaju logični i jasni. Na

primjer, većina programskih jezika za prvi indeks elemenata nekoga skupa koristi broj 0, tj.

prosječan programer prirodno o prvom elementu razmišlja kao o “nultom” usprkos

poteškoćama koje ovo potencijalno uzrokuje. R u potpunosti zaobilazi ovu konvenciju i

počinje enumeraciju članova nekoga skupa s 1. Zašto? Zato što je analitičaru puno prirodnije

i praktičnije razmišljati o “prvom retku tablice” nego “nultom” te je u potpunosti rasterećen

potrebe “mentalnog posmaka za 1”, toliko prirodne u programerskom svijetu.

Drugi pogrešan način učenja jezika R, kojem najčešće teže ljudi bez programerskog

iskustva, jest onaj koji koristi dijametralno suprotni pristup, a to je učenje samo onih

Page 9: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________3

elemenata jezika R orijentiranih neposrednoj svrsi njegovoga korištenja. Ta svrha može biti,

na primjer, učitavanje skupa podataka iz datoteke, crtanje neke vizualizacije ili izrada

linearnoga prediktivnog modela. Zanemarivanjem programskih elemenata i internoga načina

funkcioniranja jezika R isti se tako svodi na niz naredbi koje korisnik uči napamet (ili zapisuje

na “šalabahter”) i koji onda predstavlja jedini i isključivi način korištenja ovoga jezika. Ovo

praktički reducira R na “aplikaciju bez sučelja” koja time postaje zbunjujuća i manje

atraktivna alternativa nekom grafičkom analitičkom alatu. Usprkos domenskoj orijentiranosti,

jezik R je i dalje programski jezik i iskorištavanje njegovoga punog potencijala zahtjeva

svladavanje vještine programiranja, kao i razumijevanje svih njegovih elemenata, a ne samo

ograničene kolekcije “korisnih” funkcija.

Ideja ovoga tečaja jest prvenstveno naći kompromis između ova dva “ekstremna” pristupa

učenja R-a te pokazati kako programerski pristup učenju uz prihvaćanje domenske

orijentiranosti jezika može uvelike olakšati svladavanje R-a te dugoročno omogućiti lakši i

učinkovitiji rad uz pisanje jasnoga i preglednoga programskog kôda. Posebna pažnja posvetit

će se specifičnostima jezika te će se neke njegove neobičnije strane demistificirati i razjasniti

u kontekstu korištenja istih u konkretnim primjerima i zadacima. Konačno, dio tečaja posvetit

će se i tome kako izbjeći klasične početničke greške i zablude koje toliko često stvaraju krivu

percepciju u ovom programskom jeziku.

1.2.3. Pregled sadržaja tečaja

Prvi dan posvetit ćemo osnovnim elementima jezika R – tipovima podataka i podatkovnim

strukturama. Posebno ćemo se usredotočiti na fundamentalne elemente R-a, principe

vektorizacije i recikliranja te dizajna indeksnih vektora, koji su ključni za kvalitetno

upoznavanje s jezikom i njegovo učinkovito korištenje.

Drugi dan bit će uglavnom usmjeren prema funkcijama kako onim ugrađenim ili dostupnim

kroz raznorazne dodatne pakete tako i onima koje korisnik može samostalno definirati. Bit će

rečeno i nekoliko riječi o paradigmama programskih jezika, osobito funkcionalnom

programiranju, koje predstavlja bitan segment učinkovitoga rada s jezikom R. Obradit će se i

elementi kontrole programskoga toka te razjasniti zašto pojedini od njih (osobito programske

petlje) nisu toliko prisutni ni poželjni u jeziku R koliko u drugim programskim jezicima.

Konačno, treći dan ćemo se usredotočiti na najpopularnije funkcije i dodatne pakete posebno

dizajnirane za to da korisniku olakšaju i ubrzaju korištenje i razumijevanje jezika R. Jedan od

bitnih aspekata ovoga jezika jest i vrlo aktivna R zajednica koja svojim doprinosima

unapređuje iskustvo rada s jezikom svim njegovim korisnicima. Kao rezultat ovoga nastao je

niz paketa koji doslovno redefiniraju način korištenja R-a i koje veliki dio R zajednice smatra

neizostavnim, obaveznim dijelom jezika.

Krenimo sada od početka i pogledajmo osnovne elemente jezika – elementarne tipove

podataka, varijable i temeljne podatkovne strukture.

Page 10: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________4

2. Osnovni tipovi podataka i podatkovne strukture

2.1. Osnovni tipovi podataka

Osnovni (elementarni ili atomarni) tipovi podataka nekoga programskog jezika predstavljaju

temeljne vrste informacije kojima neki jezik može upravljati. Ovi tipovi su danas uglavnom

standardizirani i svode se na način reprezentacije podataka logičkoga, numeričkoga ili

znakovnoga tipa.

2.1.1. Pregled osnovnih tipova

R poznaje šest osnovnih tipova podataka:

tip izvorni naziv tipa primjeri

logički logical TRUE, FALSE ili T, F

cjelobrojni integer 2L, 5L, 123456789L

realni double 4, 6, 3.14, 2e5

kompleksni complex 5 + 2i, 7 + 1i

znakovni character "A", "B", "Pero", "ABCDEFGHijklmnoPQRSTUVwyz"

bajtovni raw as.raw(2), charToRaw("1")

Cjelobrojni i realni tip podatka zajedno se svrstavaju u takozvani “numerički” tip podatka

(engl. numeric). Razlika između njih jest u tome što cjelobrojni pohranjuje cijele brojeve s

potpunom preciznošću, ali ima ograničeniji opseg i nema mogućnost pohrane brojeva koji

sadrže decimale. Realni tip sadrži određenu nepreciznost koja je rezultat činjenice da postoji

beskonačan broj realnih brojeva, ali konačan broj bitova u registru, onome u koji

pohranjujemo realni broj. U velikoj većini slučajeva u radu s R-om o ovome uopće ne

moramo brinuti – ako to eksplicitno ne zatražimo (slovo L!), R će numeričku informaciju

pohraniti u realnom tipu, a nepreciznost će nam smetati samo ako se koristimo brojevima s

više od 16 znamenaka, što je rijedak slučaj.

Dakle, za praktične svrhe uglavnom su nam zanimljivi samo logički, znakovni i realni tip

podatka (koji ćemo zbog jednostavnosti zvati “numerički”). Cjelobrojni, kompleksni i “sirovi”

(bajtovni) tip možemo zanemariti, osim ako smo suočeni s vrlo specifičnom situacijom gdje

su isti potrebni.

2.1.2. Pojam varijable

Podatke pohranjujemo u imenovane spremnike zvane “varijable”. R je takozvani “slabo

tipizirani” jezik (engl. weakly typed), što znači da varijable možemo slobodno definirati, a da

ne navedemo unaprijed koji tip podatka pohranjuju, a istu varijablu možemo vrlo lako

“preusmjeriti” da označava neki novi tip podatka.

Page 11: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________5

Primjer 1: Inicijalizacija varijabli različitih tipova

# inicijaliziramo varijable a, b i c na sljedeći način

a <- 5

b <- TRUE

c <- 14L

c <- "Pero"

# funkcija `cat` - "sirovi" ispis, samo "izbacuje" sadržaj parametara na

zaslon

cat(a, b, c)

## 5 TRUE Pero

U primjeru smo inicijalizirali tri varijable, a, b i c. Vidimo da tipove podataka nismo morali

navoditi, već je R automatski dodijelio tip varijabli ovisno o tipu podatka koji smo joj pridružili.

Isto tako, možemo vidjeti da smo varijablu c prvo inicijalizirali na cjelobrojni tip, da bi joj

potom pridružili znakovni tip. Ovim vidimo da je varijabla zapravo samo svojevrsni

“pokazivač” na informaciju u radnoj memoriji. Cjelobrojni podatak 14L više nije dohvatljiv i

predstavlja “smeće” – R ima posebnu dretvu zvanu “sakupljač smeća” koja periodički

provjerava postoji li ovakva nedohvatljiva informacija i ako da, onda istu trajno briše

označavajući taj dio memorije kao slobodan.

(NAPOMENA: jedan od češćih problema u radu s R-om jest pojava nedostatka radne

memorije, koja se može dogoditi kod rada s velikim podatkovnim skupovima, a koja rezultira

usporavanjem izvođenja programa ili njegovim rušenjem. Često je uzrok ovoga neiskustvo

analitičara/programera, čiji programski kôd stvara previše “kopija” podatkovnoga skupa koje

se jednokratno koriste i odmah čine zalihosnima, ali koje sakupljač smeća ne stigne na

vrijeme osloboditi. Tijekom ovoga tečaja naučit ćemo kako izbjeći ovu pojavu.)

2.1.3. Provjera tipa podatka

Tip podatka (tj. varijable) možemo provjeriti uz pomoć funkcija typeof ili class. Pokušajmo

upotrijebiti ove funkcije nad varijablama a, b i c koje smo inicijalizirali u prethodnom primjeru.

Vježba 1: Funkcije typeof i class

# primijenite funkciju `typeof` redom na varijable a, b i c

# primijenite funkciju `class` redom na varijable a, b i c

Razlika između typeof i class možda nije očita. Funkcija typeof odgovara na pitanje “Kako

je ovaj podatak pohranjen u memoriji?”, dok nam funkcija class otkriva odgovor na pitanje

“Što ovaj podatak predstavlja?” (točnije: “Koje je klase ovaj objekt?”). U praksi ćemo gotovo

uvijek koristiti funkciju class, jer je – kao što ćemo kasnije naučiti – većina kompleksnijih

objekata u R-u zapravo pohranjena kao liste, tako da nam tip podatka koji vraća typeof

neće biti previše koristan.

U gornjem primjeru, varijable smo inicijalizirali s jednostavnim, elementarnim vrijednostima. U

nastavku ćemo se upoznati sa složenijim podatkovnim strukturama koje predstavljaju

temeljni dio jezika R.

Page 12: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________6

2.2. Složene podatkovne strukture

Kao što ćemo vidjeti u nastavku, jedan od češćih uzoraka koji susrećemo u R-u jest činjenica

da radimo više stvari odjednom. Ovo je nužan nusprodukt činjenice da je R dizajniran za

potrebe analize velikih podatkovnih skupova – analitičar se češće orijentira na obradu

podatkovnoga skupa u cijelosti nego što se usredotočuje na jedan podatak unutar skupa. U

skladu s time, umjesto da inicijaliziramo varijable u koje pohranjujemo jedan podatak, puno

češće ćemo upravljati varijablama koje pohranjuju kolekciju ili skup podataka. Ovisno o tipu i

strukturi podataka koje pohranjujemo, R nudi četiri temeljne složene podatkovne strukture:

vektor, matricu, listu i podatkovni okvir.

2.2.1. Vektor

Vektor je jednodimenzionalna uređena kolekcija elemenata istoga tipa. Novi vektor (koji ima

više od jednog elementa) stvaramo uz pomoć funkcije c (od engl. combine).

Primjer 2: Inicijalizacija vektora

# numerički vektor

m <- c(1, 2, 3, 4, 5)

# logički vektor

v <- c(T, F, T)

# znakovni vektor

imena <- c("Ivo", "Pero", "Ana")

# korištenje samog imena varijable pokrenut će tzv. `autoprint`

# isti rezultat postigli bismo i eksplicitnim pozivom funkcije `print`

m

v

imena

## [1] 1 2 3 4 5

## [1] TRUE FALSE TRUE

## [1] "Ivo" "Pero" "Ana"

Ako stvaramo novi vektor s elementima različitih tipova podataka, R će sve elemente

automatski pretvoriti u “najjači” tip, što će na kraju postati i tip samoga vektora (termin “jači”

tip u ovom kontekstu označava mogućnost tipa da pohrani svu informaciju “pohranjenu u

slabiji tip”, a u općenitom slučaju pretvorba ide u smjeru logički -> numerički -> znakovni tip).

Page 13: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________7

Vježba 2: Stvaranje vektora

# stvorite novi vektor x s četiri proizvoljna elementa sljedećih tipova:

# logički, realni, znakovni i cjelobrojni

# ispišite na zaslon sadržaj vektora i njegovu klasu

Funkcijom c možemo također i više vektora spojiti u jedan:

Primjer 3: Spajanje vektora

# inicijaliziramo vektore a, b i c

a <- c(1, 2, 3)

b <- c(4, 5)

# uočite – varijablu smijemo nazvati c usprkos tome što postoji funkcija

c()

c <- c(6, 7, 8)

d <- c(a, b, c) # d je sada c(1, 2, 3, 4, 5, 6, 7, 8)

d

## [1] 1 2 3 4 5 6 7 8

Pored funkcije c, R nudi i dodatne pogodne načine stvaranja novih vektora:

• : – operator “raspona” (engl. range), pri čemu dajemo raspon od gornje do donje

granice, obje uključive

• seq – funkcija sekvence (engl. sequence), radi slično operatoru raspona, ali s dodatnim

mogućnostima

• rep – funkcija repliciranja (engl. replicate), ponavlja zadane elemente zadani broj puta.

Primjer 4: Stvaranje vektora – pomoćne funkcije i operatori

1:5

rep(c(1, 2, 3), times = 3)

rep(c(1, 2, 3), each = 3)

seq(1, 5, by = 0.5)

## [1] 1 2 3 4 5

## [1] 1 2 3 1 2 3 1 2 3

## [1] 1 1 1 2 2 2 3 3 3

## [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0

2.2.2. Operator [

Elementima vektora pristupamo preko indeksnog operatora [, uz pomoć kojega možemo i

mijenjati elemente vektora:

Page 14: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________8

Primjer 5: Pristup elementima vektora

# zadan je vektor a

a <- c(2, 4, 6)

a[1] # ispisuje vrijednost 2

a[2] <- 5 # element na 2. mjestu postaje 5

a[5] <- 7 # na 5. mjesto dodaje se 7, a "rupa" se popunjava s NA

a

## [1] 2

## [1] 2 5 6 NA 7

Uočite jednu pomalo neuobičajenu činjenicu – prvi element vektora u R-u ima indeks 1, a ne

0! Ovo je bitna razlika u odnosu na indeksiranje elemenata u drugim programskim jezicima.

Razlog ove specifičnosti je jednostavan – R se primarno smatra jezikom za analizu

podataka, osobito u tabličnom obliku, a u praksi je puno lakše brojati retke ili stupce

redoslijedom kojim se pojavljuju u podatkovnom skupu nego raditi “posmak za 1”.

Rekli smo da je vektor “jednodimenzionalna” podatkovna struktura – ako gledamo vektor od

n numeričkih elemenata, on zapravo predstavlja pravac u n-dimenzionalnom prostoru. R nudi

i podatkovne strukture za pohranu podataka u dvije, ali i više dimenzija. Ove strukture zovu

se matrice i polja.

2.2.3. Matrice i polja

Matrice i polja su, jednostavno rečeno, višedimenzionalni vektori. Matrica (engl. matrix) je

tako vektor s dvije dimenzije, tj. vektor koji elemente smješta u “retke” i “stupce”. Polje (engl.

array) je vektor s tri ili više dimenzija. Dok se matrice relativno često koriste u praksi, polja su

ipak nešto više ograničena na posebne scenarije. Zbog ove činjenice, u ovom poglavlju

uglavnom ćemo se baviti matricama, iako se prikazani koncepti vrlo lako poopćuju na polja.

Postoji nekoliko načina stvaranja nove matrice:

• uz pomoć funkcije matrix kojoj prosljeđujemo jednodimenzionalni vektor i željeni broj

redaka i stupaca kroz parametre nrow i ncol

• “ručnim” postavljanjem dimenzija jednodimenzionalnoga vektora uz pomoć funkcije dim

i pridruživanja dvoelementnoga numeričkog vektora s dimenzijama matrice

• “lijepljenjem” jednodimenzionalnih vektora koji predstavljaju retke ili stupce nove matrice

uz pomoć funkcija rbind (engl. row bind) i cbind (engl. column bind).

Page 15: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________9

Vježba 3: Funkcija matrix

# zadan je vektor x

x <- 1:12

# uz pomoć funkcije `matrix` i vektora x stvorite matricu s 3 retka i 4

stupca

# ispišite rezultat na zaslon

# ponovite postupak, ali pozivu funkcije dodajte parametar `byrow = T`

# ispišite rezultat na zaslon i usporedite s prethodnim rezultatom

Vježba 4: Funkcije rbind i cbind

# zadani su vektori a, b i c

a <- 1:4

b <- 5:8

c <- c(0,0)

# stvorite matricu m u kojoj će vektori a i b biti stupci

# dodajte novi redak na vrh matrice m s elementima vektora c

# ispišite matricu m

Elementima matrice također pristupamo preko indeksnog operatora [. Razlika je samo u

tome što ćemo kod indeksiranja matričnih elemenata (uglavnom) koristiti dvodimenzionalnu

referencu – indeks retka i indeks stupca, odvojene zarezom. Ako želimo cijeli redak ili cijeli

stupac, jednostavno ispustimo indeks te dimenzije (npr. m[2,] znači “drugi redak”).

Vježba 5: Indeksiranje matričnih elemenata

# zadana je matrica m

m <- matrix(1:12, nrow = 3, ncol = 4, byrow = T)

# ispišite:

# element u 2. retku, 3. stupcu

# cijeli 3. redak

# cijeli 4. stupac

Polja (engl. array) su zapravo višedimenzionalno proširenje koncepta matrice. U praksi ih

relativno rijetko susrećemo osim u specifičnim slučajevima gdje ciljano radimo s

višedimenzionalnim strukturama, na primjer modeliranje neke trodimenzionalne podatkovne

strukture ili rješavanje višedimenzionalnih problema iz linearne algebre. Zbog ove činjenice,

ovdje ih nećemo posebno obrađivati, no ako se pojavi potreba za njihovim korištenjem,

dovoljno se prisjetiti da se njima upravlja analogno matricama (npr. indeksiranje svake

dimenzije zasebno, odvojeno zarezom).

Page 16: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________10

2.2.4. Liste

Lista je element programskoga jezika R koji se koristi kao “univerzalni spremnik” bilo kakvih

podataka. Za razliku od vektora (tj. od pojma vektora kakvog smo ga inicijalno definirali), lista

može sadržavati različite tipove podataka ili, češće, skupove različitih tipova podataka.

Listu stvaramo uz pomoć funkcije list kojoj dodajemo niz parova naziva elemenata i

njihovih sadržaja. Ovi elementi mogu biti bilo što, pa čak i druge liste.

Primjer 6: Funkcija list

mojaLista <- list(a = 1, b = 2:100, c = list(x = 1, y = 2))

Pokušajmo stvoriti vlastitu listu u sljedećoj vježbi.

Vježba 6: Stvaranje liste

# stvorite novu listu naziva `svastara` koja će imati sljedeće elemente

# element naziva `brojevi` s cijelim brojevima od 1 do 3

# element naziva `slova` sa slovima "A" i "B"

# bezimeni element s logičkim vektorom `c(T,F)`

# element naziva `imena` s imenima "Ivo" i "Ana"

# ispišite listu `svastara`

Uočite da lista zadržava poredak elemenata – element bez imena prikazan je indeksom 3.

Funkcija str (engl. structure) omogućuje nam uvid u svojstva i sadržaj liste bez ispisivanja

cijele liste.

Vježba 7: Struktura liste

# ispišite strukturu liste `svastara`

Elementima liste uglavnom pristupamo na dva načina:

• uz pomoć operatora [[, tj. operatora “dvostruke uglate zagrade”

• uz pomoć operatora $ (tzv. “string” operator).

Vježba 8: Operator [[

# ispišite prvi element liste svastara korištenjem operatora [[

# provjerite njegovu klasu

Navedeni operator najčešće koristimo kako bismo dohvatili odabrani element liste koji

definiramo brojem ili (ako ima ime) nazivom elementa. Kod ovakvog dohvata moramo koristiti

kombinaciju simbola lista[["ime_elementa"]] koja je ponešto nespretna za tipkanje.

Zbog toga R nudi alternativni način pristupa elementima liste prema nazivu korištenjem

operatora $, tj. lista$ime_elementa.

Page 17: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________11

Vježba 9: Operator $

# ispišite element naziva `slova` liste svastara

# korištenjem operatora [[

# ispišite isti element korištenjem operatora $

Za kraj, naučimo dodati element u listu. Ovo je najjednostavnije učiniti korištenjem već

spomenutog operatora $, kao na primjer, lista$noviElement <- noviElement. Element

brišemo tako da mu dodijelimo vrijednost NULL.

Vježba 10: Dodavanje i brisanje elemenata liste

# listi `svastara` dodajte element `parniBrojevi` koji sadrži

# sve parne brojeve od 1 do 100

# obrišite treći element liste

# ispišite listu `svastara`

2.2.5. Podatkovni okviri (Data frames)

Podatkovni okvir predstavlja vjerojatno najpopularniju i najčešće korištenu podatkovnu

strukturu jezika R, što proizlazi iz činjenice da se radi o objektu koji reprezentira podatkovni

skup koji namjeravamo analizirati. Drugim riječima, podatkovni okvir je objekt slične funkcije

kao tablica u Microsoft Excelu ili relacijskoj bazi podataka. Gotovo svaka “sesija” u R-u svodi

se na manipuliranje podatkovnim okvirima – no, dok u Excelu tablicom upravljamo uz pomoć

grafičkoga sučelja, a u bazi uz pomoć upitnoga jezika SQL, u R-u podatkovni okvirima

upravljamo gotovo isključivo programski.

Uzmimo za primjer sljedeću tablicu:

pbr nazivMjesta prosjPlacaKn brojStanovnika prirez

10000 Zagreb 6359.00 790017 18

51000 Rijeka 5418.00 128384 15

21000 Split 5170.00 167121 10

31000 Osijek 4892.00 84104 13

20000 Dubrovnik 5348.00 28434 10

Ovdje se radi o podatkovnom skupu koji sadržava određene (ne nužno ažurne) parametre

vezane za gradove u Republici Hrvatskoj.

Podatkovni okvir najčešće stvaramo na jedan od dva načina:

• programski, uz eksplicitno navođenje sadržaja

• čitanjem iz vanjske datoteke uz pomoć funkcija read.table / read.csv / read.csv2.

U nastavku ćemo isprobati korištenje obaju načina.

Page 18: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________12

Vježba 11: Programsko stvaranje podatkovnog okvira

#zadani su sljedeći vektori

pbr = c(10000, 51000, 21000, 31000, 2000)

nazivMjesta = c("Zagreb", "Rijeka", "Split", "Osijek", "Dubrovnik")

prosjPlacaKn = c(6359., 5418., 5170., 4892., 5348.)

brojStanovnika = c(790017, 128384, 167121, 84104, 28434)

prirez = c(18, 15, 10, 13, 10)

# uz pomoć funkcije `data.frame` i gornjih vektora stvorite podatkovni

okvir `mjesto`

# koristite istu sintaksu kao kod stvaranja liste

# elemente liste nazovite imenima varijabli

# ispišite podatkovni okvir `mjesto`

Naziv CSV znači “comma separated values”, tj. “podaci odvojeni zarezom”. Prvi redak

datoteke obično sadrži nazive stupaca. Ovako oblikovane podatke lako čitamo uz pomoć

funkcije read.csv kojoj dajemo sljedeće parametre:

• stazu do CSV datoteke

• parametar stringsAsFactors = FALSE.

Potonji parametar bitan je za sprečavanje takozvanoga “faktoriziranja”, tj. kategoriziranja

znakovnih stupaca, što je postupak koji je bolje obaviti “ručno” (nakon učitavanja u

podatkovni okvir) kako bismo izbjegli eventualne greške.

NAPOMENA: Problem CSV datoteka jest taj što one koriste zarez kao razdvojnik (engl.

delimiter) elemenata zapisa, što predstavlja problem na govornim područjima koje kao

standard, umjesto decimalne točke, koriste upravo “decimalni zarez”. Zbog ove činjenice

postoji i “alternativni” CSV standard koji kao razdvojnik koristi “točku-zarez”. Budući da smo

stanovnici takvoga govornog područja, velike su šanse da ćemo se susresti s takvom

“alternativnom” CSV datotekom; u tom slučaju bismo umjesto funkcije read.csv trebali

koristiti funkciju read.csv2, ili read.table koja je općenitija funkcija za čitanje podataka iz

datoteke i omogućuje fino podešavanje niza parametara (zapravo su read.csv i read.csv2

izvedenice funkcije read.table).

Za sljedeću vježbu bit će potrebno u radnoj mapi stvoriti CSV datoteku sljedećega sadržaja:

pbr,nazivMjesta,prosjPlacaKn,brojStanovnika,prirez

10000,Zagreb,6359.00,790017,18

51000,Rijeka,5418.00,128384,15

21000,Split,5170.00,167121,10

31000,Osijek,4892.00,84104,13

20000,Dubrovnik,5348.00,28434,10

Page 19: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________13

Vježba 12: Čitanje podataka iz CSV datoteke

# učitajte podatke iz datoteke `mjesto.csv`

# podatke spremite u podatkovni okvir `mjesto2`

# ispišite podatkovni okvir `mjesto2`

Jedna od zgodnih stvari kod podatkovnih okvira jest ta što se oni ponašaju i kao matrice

(dvodimenzionalno indeksiranje) i kao liste. Ovo proizlazi iz činjenice da su podatkovni okviri

zapravo ništa drugo nego liste, samo s ograničenjem da svi elementi moraju imati istu

duljinu. Ovo ograničenje zapravo diktira “tabličnu” strukturu podatkovnog okvira, što nam

daje opciju da isti vizualiziramo kao dvodimenzionalnu tablicu te da s njom upravljamo kroz

koncepte “redaka” i “stupaca”.

Upravljanje podatkovnim okvirima predstavlja temelj rada s jezikom R i najvažniju stepenicu

za njegovo jednostavnije i učinkovito korištenje. Zbog toga je vrlo važno uložiti trud i vrijeme

za svladavanje nekih osnovnih principa koji će rad s podatkovnim okvirima (ali i ostalim

podatkovnim strukturama) učiniti što transparentnijim i razumljivijim, bez pojave “neobičnoga”

ponašanja i “neočekivanih” rezultata. Konkretno, to su principi vektorizacije i recikliranja te

konstrukcije indeksnih vektora, a koje ćemo naučiti u nastavku.

Page 20: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________14

3. Vektorizacija i indeksni vektori

3.1. Principi vektorizacije i recikliranja

3.1.1. “Sve je vektor”

U prethodnom dijelu upoznali smo osnovne tipove podataka jezika R te složene podatkovne

strukture. Pokušajmo sada odgovoriti koja je od varijabli u sljedećem primjeru “vektor”.

Primjer 7: Vektori

a <- 4

b <- "Ivana"

c <- c(1, 2, 3, 4, 5)

d <- list(a = 1:10, b = LETTERS, c = c(T,F)) # Što je LETTERS? Ispišimo

to!

e <- matrix(1:9, 3, 3)

Pokušajte prvo intuicijom odgovoriti na ovo pitanje, a potom provjerite ispravnost

pretpostavke uz pomoć funkcije is.vector.

Došli smo do jednog vrlo važnoga, i možda ponešto neočekivanoga zaključka – sve gornje

varijable su vektori, osim varijable e (iako je i ona zapravo vektor, usprkos tomu što ju

funkcija is.vector ne priznaje kao takvu, što je zapravo rezultat činjenice da ta funkcija ne

provjerava da li je nešto vektor, već ima li dodatne atribute pored names, tako da ju zapravo

ne bismo ni trebali koristiti).

Dakle, čak i broj 4 te niz znakova "Ivana" nisu ništa drugo nego jednočlane “kolekcije”

elemenata. Što ovo konkretno znači? Postoji jedan često spominjani princip jezika R, koji

glasi “sve je vektor”. Iako ovo baš nije doslovno točna izjava, ona otkriva jednu vrlo važnu

činjenicu, a to je da je većina varijabli kojima ćemo se koristiti u jeziku R zapravo vektor, što

znači da će imati slično ponašanje i koristiti slične obrasce upravljanja!

3.1.2. Matrica kao vektor

Iako smo rekli da funkcija is.vector neće prepoznati matricu kao vektor, to ne mijenja

činjenicu da matrica nije ništa drugo nego jednodimenzionalni vektor s dodanim atributom

dim koji programeru omogućuje da ga koristi kao dvodimenzionalnu strukturu. Drugim

riječima, dvodimenzionalna struktura matrice je samo prividna, ona je tu strogo zbog

praktičnosti i lakšega korištenja jezika R za potrebe matričnih izračuna. Ovo je zapravo

uobičajena pojava u programskim jezicima – višedimenzionalne strukture se zapravo

“raspliću” kako bi se uopće mogle pohraniti u jednodimenzionalnu memoriju računala, a

višedimenzionalno indeksiranje se uz pomoć poznate dimenzionalnosti “ispod haube”

preračunava u jednodimenzionalni indeks.

U ovo se možemo uvjeriti u sljedećoj vježbi.

Page 21: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________15

Vježba 13: Matrica kao vektor

# zadana je matrica m

m <- matrix(1:12, nrow = 3, ncol = 4)

# ispišite matricu m

# ispišite 2. element u 4. stupcu

# ispišite 11. element (jednodimenzionalno indeksiranje!)

Zašto nam je ovo korisno? Poznavanje interne strukture podataka nam može olakšati rad s

istima – ako znamo da je matrica zapravo pohranjena “po stupcima” onda možemo i

razumjeti zašto može doći do izrazitih usporavanja u radu s velikim matricama ako podatke

obrađujemo “po retcima”. Isto tako, kao što ćemo vidjeti u nastavku, podatkovni okviri se u

izvjesnoj mjeri ponašaju kao matrice tako da i za njih vrijedi princip “virtualne

dimenzionalnosti” – to što mi kao programeri vidimo podatke organizirane u retke, to ne znači

da su oni zapravo bliski jedni drugima na mjestu gdje su pohranjeni.

Ovo sve naravno ne znači da u radu s matricama (i podatkovnim okvirima) ne bismo trebali

koristiti pogodnosti koje nam donosi prividna dvodimenzionalnost, već samo da bismo istu

morali imati u vidu u trenucima kada dođe do eventualnih problema ili uspijemo pronaći

zgodan način kako jednodimenzionalnu prirodu iskoristiti za lakše upravljanje takvim

podacima.

3.1.3. Lista kao vektor

Opravdano je postaviti pitanje – što je s listom? Kod same definicije liste rekli smo da ona

“sadržava različite skupove podataka”, što se naizgled kosi s definicijom vektora kao

“jednodimenzionalne uređene kolekcije elemenata istoga tipa”. Objašnjenje je zapravo

jednostavno – definicija nije narušena zato što lista u biti sadržava “niz jednoelementnih listi”.

Svaki element liste je lista, što objašnjava činjenicu zašto kod indeksiranja liste ne koristimo

operator [ već [[. Budući da je svaki element liste opet lista, jednostruki operator bi nam

vratio listu, i kod dohvaćanja prvog elementa te liste opet bismo dobili listu, i tako u nedogled.

Operator [[ (a tako i operator $ koji je zapravo izveden iz prethodno navedenog) zapravo

znači “dohvati i otpakiraj” element liste.

Isprobajmo ovo uz pomoć iduće vježbe:

Vježba 14: Lista kao vektor

# zadana je lista `svastara`

svastara <- list(brojevi = c(1,2,3),

slova = c("A", "B"),

c(T,F),

imena = c("Ivo", "Ana"))

# provjerite klasu drugog elementa liste `svastara`

# koristite operator [

# provjerite klasu drugog elementa liste `svastara`

# koristite operator [[

Page 22: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________16

Opet, zašto nam je uopće bitno znati da je lista zapravo vektor? Zato što to znači da u suštini

nema bitne razlike između upravljanja vektorom i upravljanja listom; i vektor i lista se

ponašaju jednako s gledišta “kolekcije elemenata” te ako razumijemo kako upravljati

vektorom koji sadrži, na primjer, jednostavne numeričke vrijednosti, onda iste principe

možemo koristiti za upravljanje listama, iako su njezini elementi često složenije strukture od

“običnih” brojeva.

3.1.4. Podatkovni okvir kao vektor

Već smo spomenuli da je podatkovni okvir zapravo lista, samo s elementima jednake duljine.

To znači da je sve spomenuto za percepciju liste kao vektora primjenjivo i na podatkovni

okvir, pa čak i rasprava o operatorima [, [[ i $ – iako ne smijemo zaboraviti činjenicu da R

omogućuje dvodimenzionalno indeksiranje podatkovnih okvira tako da kod njih ipak nećemo

koristiti operator [[ već operator [ sa zarezom za razdvajanje dimenzija (što je kod “običnih”

lista besmisleno i neprimjenjivo).

Pokažimo ovo u idućoj vježbi.

Vježba 15: Podatkovni okvir kao vektor

# # učitajte podatke iz datoteke `mjesto.csv`

# podatke spremite u okvir `mjesto`

# ispišite drugi stupac (nazivMjesta) na sljedeće načine:

# koristite operator [ i jednodimenzionalno indeksiranje

# koristite operator [ i dvodimenzionalno indeksiranje

# koristite operator [[

# koristite operator $

Ako ste pažljivo proučili rješenje, mogli ste uočiti jasnu poveznicu između liste i podatkovnih

okvira – operator [ uz jednodimenzionalno indeksiranje vratio nam je (jednostupčani)

podatkovni okvir, dok su svi ostali načini “otpakirali” informaciju i pretvorili je u običan

znakovni vektor. Ova činjenica nam se nekad može pokazati korisnom, pogotovo ako pišemo

funkcije koje obrađuju podatkovne okvire i želimo da nam rezultat operacije bude okvir, a ne

vektor.

NAPOMENA: Razlog zašto kod operatora [ i dvodimenzionalnog indeksiranja nismo dobili

podatkovni okvir već vektor zapravo nije “otpakiravanje” elemenata liste (to operator [ ni ne

radi!), već činjenica da jezik R često “pojednostavljuje” rezultat. Ukoliko to ne želimo,

možemo kod indeksiranja dodati parametar drop = FALSE, npr. mjesto[, 2, drop =

FALSE].

Možemo ponovo postaviti pitanje – zašto nam je bitno razumjeti internu strukturu

podatkovnog okvira? Ako znamo da je okvir zapravo lista, to znači da se funkcije za obradu

listi mogu bez problema primijeniti na podatkovne okvire (s time da moramo voditi računa o

tome da su “elementi liste” zapravo stupci okvira). Isto tako, znajući da je lista zapravo

vektor, opet se vraćamo na činjenicu da funkcije za obradu vektora element po element

Page 23: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________17

postaju direktno primjenjive na liste, a time i na okvire. Konačno, već spomenuta činjenica

kod matrica vrijedi i za okvire – obrada po stupcima je puno učinkovitija od obrade po

redcima, budući da se podatkovni okvir interno “raspliće” po stupcima.

Prihvaćanjem principa “sve je vektor” već smo učinili bitan pomak u razumijevanju principa

vektorizacije i kako ga primijeniti u jeziku R. Učinimo sad korak dalje.

3.1.5. Vektorizirane operacije i princip recikliranja

Pojam vektorizacije ili bolje rečeno vektoriziranih operacija i funkcija jednostavno znači da se

operacije rade nad više elemenata odjednom. Ovo je temeljni princip kako R obrađuje

podatke i ključna prepreka za razumijevanje ovoga jezika. Budući da je u R-u “sve vektor”, to

znači da su operacije koje radimo nad našim varijablama prirodno vektorizirane, ili –

jednostavnim jezikom rečeno – R-ov uobičajeni način rada jest da radi nad cijelim skupom

odjednom, umjesto sa svakim elementom posebno.

Dakle, ako zadamo R-u da radi neku operaciju ili funkciju nad nekim vektorom vrijednosti, R

će funkciju ili operaciju izvesti nad svakim elementom posebno i vratiti rezultantni vektor kao

rezultat. Isto tako, ako provodimo binarnu operaciju nad dva vektora, ona će se provesti nad

“uparenim” ili “poravnatim” elementima obaju vektora (pretpostavimo za sada da su vektori

jednake duljine).

Vježba 16: Princip vektorizacije

# zadani su vektori x, a i b

x <- seq(-5, 5, 1)

a <- 1:3

b <- 4:6

# pozovite funkciju `abs` za računanje apsolutne vrijednosti

# nad vektorom x i ispišite rezultat

# zbrojite vektore a i b uz pomoć operatora +

# i ispišite rezultat

# pomnožite vektore a i b uz pomoć operatora *

# i ispišite rezultat

Pažljivo razmotrite rezultate prethodnoga zadatka. Ako je potrebno, skicirajte vektore a i b na

papiru s vertikalno poslaganim elementima i uočite kako radi paralelno “uparivanje”

elemenata. Primijetite da ovdje ne govorimo o “vektorskim operacijama” u strogom

matematičkom smislu, već o poravnavanju elemenata dvaju nizova i provođenja

jednostavnih operacija nad svakim od tih parova. Ovo je pogotovo očito u zadnjem primjeru

gdje nema nikakvoga “množenja vektora” kako ga uobičajeno interpretiramo u linearnoj

algebri (“skalarni produkt”), već se provodi jednostavno množenje paralelnih elemenata dvaju

vektora.

Što ako vektori nisu jednake duljine? R u ovom slučaju koristi princip recikliranja.

Page 24: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________18

Princip recikliranja navodi da se kod nejednake duljine vektora kraći vektor “reciklira”

onoliko puta koliko je potrebno da se dostigne duljina duljega vektora. Najčešći scenarij

korištenja ovoga principa su operacije u kojima je s jedne strane vektor s više elemenata, a s

druge strane jednoelementni vektor koji se onda reciklira za svaki element “velikoga” vektora.

Ono što bismo trebali izbjegavati jest scenarij recikliranja gdje duljina “velikoga” vektora nije

višekratnik duljine “maloga” – R će i dalje reciklirati kraći vektor, samo će ga na kraju morati

“odrezati” što će rezultirati odgovarajućim upozorenjem.

Vježba 17: Princip recikliranja

# zadani su vektori a, b i c

a <- 1:4

b <- c(1, 2)

c <- rep(5, 3)

# udvostručite elemente vektora a i ispišite rezultat

# podijelite vektor a vektorom b i ispišite rezultat

# pomnožite vektore a i c i ispišite rezultat

Ovdje možemo razjasniti razliku između “skalarnih” i “vektorskih” logičkih operatora u jeziku

R. Podsjetimo se, za razliku od drugih programskih jezika, R nudi dvije inačice logičkih

operatora “i” i “ili”:

• skalarni: && i ||

• vektorski: & i |

Razlika je u sljedećem:

• Skalarni logički operatori namijenjeni su korištenju s jednoelementnim vektorima,

vraćaju jedinstvenu vrijednosti TRUE ili FALSE te su pogodni za korištenje raznim u

uvjetnim izrazima.

• Vektorski logički operatori koriste standardne R-ove principe vektorizacije i recikliranja,

tj. namijenjeni su radu s logičkim vektorima i kao rezultat daju logički vektor.

Vježba 18: Skalarni i vektorski logički operatori

# zadani su vektori a i b

a <- c(T, F, F)

b <- c(T, T, F)

# primijenite skalarnu i vektorsku inačicu logičkog operatora "ili"

# nad vektorima a i b i ispišite rezultat

Vidimo da će skalarna inačica “iskoristiti” samo prvi par elemenata logičkih vektora. Ovo

znači da ju u teoriji možemo koristiti u uvjetnim izrazima, iako za to nema opravdanoga

smisla, a R će se u tom slučaju oglasiti upozorenjem kako bi nam obratio pažnju na činjenicu

da vjerojatno koristimo “krivi” operator.

Page 25: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________19

Sljedeći primjer s usporednim operatorima će možda inicijalno izgledati trivijalan, no

potrebno je obratiti posebnu pažnju na rezultate koje ćemo dobiti jer će oni imati vrlo važnu

primjenu u nastavku lekcije. Dakle, pogledajmo što se događa kod vektorizacije usporednih

operatora.

Vježba 19: Vektorizacija usporednih operatora

# zadani su vektori x i y

x <- 1:5

y <- seq(-10, 10, 5)

#ispišite x i y

#ispišite rezultat naredbe x > y i objasnite rezultat

#ispišite rezultat naredbe x < 3 i objasnite rezultat

Dakle, vektoriziranom primjenom usporednih operatora nad vektorima (ili kombinacijama

vektora i “skalara”) kao rezultat dobivamo logičke vektore. Interpretacija ovih rezultata je

ključna – ona zapravo odgovara na pitanje “na kojim je indeksima zadovoljen uvjet zadan

ovim izrazom?” Drugim riječima, dobiveni rezultati zapravo predstavljaju predložak koji

opisuje kako filtrirati elemente prema zadanom principu. Ovo je osnovni temelj takozvanoga

logičkog indeksiranja, što je jedna od metoda indeksiranja koje ćemo upoznati u nastavku.

3.2. Indeksni vektori

3.2.1. Definicija indeksnoga vektora

Do sada smo usvojili dva vrlo važna principa za razumijevanje jezika R:

• “sve je vektor”

• “sve operacije su vektorizirane”.

Jedan od razloga inicijalno strme krivulje učenja ovoga jezika jest i taj da su programeri često

naviknuti na intenzivno korištenje programskih petlji te pojedinačnu obradu elemenata

kolekcija tako da im princip vektoriziranih operacija često predstavlja dramatičan pomak

paradigme. No, ukoliko se ova problematika sagleda sa suprotne strane, može se uočiti da

se zapravo radi o bitnom pojednostavljenju sintakse – umjesto da u programu objašnjavamo

“kako” nešto učiniti, mi mu samo govorimo “što” želimo i puštamo računalo da se brine o

niskorazinskim stvarima kao što je “šetanje po kolekciji”.

Dakle, jezik R jednostavno voli raditi “više stvari odjednom”. No ovo ne znači da smo u praksi

ograničeni na pristup “sve ili ništa”, tj. da moramo uvijek sve podatke nekoga skupa

obrađivati istodobno. Jezik R nam omogućuje da točno specificiramo nad kojim elementima

skupa želimo raditi, ali bez napuštanja principa “sve odjednom” – u tome nam pomažu

takozvani indeksni vektori.

Page 26: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________20

Indeksni vektor nije ništa drugo nego običan, elementaran vektor koji predstavlja svojevrsnu

specifikaciju elemenata (indekasa) s kojima želimo raditi, bilo da ih želimo dohvatiti ili

izmijeniti. Postoje tri osnovna načina indeksiranja (i pripadajućih indeksnih vektora):

• lokacijsko indeksiranje (engl. integer- or location-based indexing), numerički indeksni

vektor

• uvjetno indeksiranje (engl. conditional or boolean-based indexing), logički indeksni

vektor

• imensko indeksiranje (engl. label-based indexing), znakovni indeksni vektor.

Koje ćemo indeksiranje odabrati ovisi o tome želimo li elementima pristupati ovisno o njihovoj

lokaciji, imenu ili prema zadanom uvjetu, a svaki tip indeksiranja u suštini se svodi na

korištenje navedenoga tipa vektora kao parametra za operator indeksiranja [.

Upoznajmo detaljno svaki od tipova indeksiranja. U vježbama koje slijede uglavnom ćemo

indeksirati jednostavne vektore, no principi koje upoznamo direktno su primjenjivi i na ostale

složene strukture (matrice, liste, podatkovne okvire), s obzirom na činjenice koje smo dosad

naučili.

3.2.2. Lokacijsko indeksiranje

Lokacijsko indeksiranje je poopćenje već upoznatoga principa indeksiranja gdje navodimo

redni broj elementa koji nas zanima. Ako želimo više elemenata, jednostavno navedemo

njihove indekse “zapakirane” u numerički vektor.

Pokušajte riješiti sljedeći zadatak korištenjem odgovarajućih numeričkih vektora kao

parametara indeksiranja.

Vježba 20: Lokacijsko indeksiranje

# zadan je vektor x

x <- 1:10

# ispišite prvi element vektora x

# ispišite prva tri elementa vektora x

# ispišite prvi, peti i sedmi element vektora x

Dakle, lokacijski indeksni vektor nije ništa drugo nego običan numerički vektor koji koristimo

zajedno s indeksnim operatorom da bismo odredili koje elemente nekoga drugog vektora

želimo “zadržati”.

Pogledajmo još neke značajke lokacijskih indeksnih vektora:

Page 27: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________21

Vježba 21: Lokacijsko indeksiranje (2)

# zadan je vektor x

x <- 1:10

# odgovorite na sljedeća pitanja uz pomoć prikladnog primjera

# što vraća indeks 0?

# što vraća negativni indeks?

# što vraća indeks izvan granica duljine vektora?

# što vraća negativni indeks izvan granica duljine vektora?

Indeksiranje se ne koristi samo za dohvaćanje elemenata. Kombinacijom operatora

indeksiranja i operatora pridruživanja možemo mijenjati elemente vektora (i to također po

principu “više elemenata odjednom”):

Vježba 22: Lokacijsko indeksiranje i pridruživanje

# zadan je vektor a

a <- 1:10

# postavite sve elemente vektora a od drugog do osmog mjesta na nulu

# ispišite vektor a

# nakon toga izvršite sljedeće naredbe:

b <- 1:20

b[2 * 1:5] <- 0

# razmislite o tome kako izgleda vektor b nakon gornje naredbe

# ispišite vektor b i objasnite rezultat

Page 28: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________22

3.2.3. Uvjetno indeksiranje

Ako smo pažljivo razmotrili rezultate dobivene kod primjera s vektoriziranim usporednim

operatorima, onda smo mogli vrlo dobro naslutiti kako radi uvjetno indeksiranje. Princip je

jednostavan – za indeksni vektor postavljamo logički vektor iste duljine kao i vektor čije

elemente želimo dohvatiti. Elementi logičkoga vektora određuju koje elemente zadržavamo

(pozicije na kojima se nalazi vrijednost TRUE) a koje odbacujemo (pozicije na kojima se nalazi

vrijednost FALSE).

Vježba 23: Uvjetno indeksiranje

# zadan je vektor x

x <- 1:10

# napravite logički vektor y duljine 10 s proizvoljnom kombinacijom

# vrijednosti TRUE i FALSE

# indeksirajte vektor x vektorom y, ispišite i objasnite rezultat

# ispišite sve elemente vektora x manje ili jednake 5

# kao logički indeksni vektor upotrijebite odgovarajući izraz

# koji koristi usporedni operator

Naredba x[x <= 5], naoko jednostavna, zapravo je jedan od ključnih principa odabira

elemenata u jeziku R. Kombinacija indeksnog operatora i uvjetnog izraza predstavlja sažet

ali vrlo moćan mehanizam rezanja vektora prema odabranom kriteriju.

Isprobajmo ovaj princip na još nekoliko primjera.

Vježba 24: Uvjetno indeksiranje (2)

# zadani su vektor y i `studenti`

y <- seq(1, 100, 7)

studenti <- c("Ivo", "Petra", "Marijana", "Ana", "Tomislav", "Tin")

# ispišite sve parne, a potom sve neparne elemente vektora y

# ispišite sve elemente vektora `studenti` koji predstavljaju imena od 3

slova

# (napomena: za prebrojavanje slova znakovnog niza u R-u koristimo funkciju

`nchar`)

Ukoliko koncept uvjetnog indeksiranja uz pomoć uvjetnih izraza i dalje nije jasan, jedna od

stvari koje mogu pomoći jest skiciranje “međurezultata” – jednostavno na papir ispišite

rezultat izraza unutar uglatih zagrada indeksnog operatora i potom razmislite kako taj rezultat

utječe na konačno rješenje.

Preostao nam je još samo zadnji tip indeksiranja koji radi na principu dohvaćanja elemenata

vektora ovisno o njihovu imenu.

Page 29: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________23

3.2.4. Imensko indeksiranje

Imensko indeksiranje radi na principu eksplicitnog imenovanja elemenata koje želimo

“zadržati”. Da bismo mogli koristiti ovakav tip indeksiranja, moramo zadovoljiti nužan

preduvjet – elementi vektora moraju imati definirana “imena”.

Vektori koje smo do sada koristili nisu imali imenovane elemente. Svaki element imao je

svoju predefiniranu poziciju unutar vektora te svoju vrijednost, ali nije imao nikakav poseban

dodatni identifikator. Programski jezik R dopušta pridavanje imena elementima vektora na

vrlo jednostavan način – korištenjem funkcije names, operatora pridruživanja te znakovnoga

vektora s odabranim imenima. Moramo voditi računa o tome da vektor imena bude jednake

duljine kao originalni vektor!

Vježba 25: Imensko indeksiranje

# zadan je vektor `visine` s postavljenim imenima elemenata

visine <- c(165, 173, 185, 174, 190)

names(visine) <- c("Marica", "Pero", "Josip", "Ivana", "Stipe")

# ispišite vektor `visine`

# ispišite koliko su visoki Pero i Ivana

Vidimo da se imensko indeksiranje očekivano svodi na prosljeđivanje odgovarajućega

znakovnog vektora kao parametra indeksiranja.

(NAPOMENA: Pažljiviji čitatelj uočit će jednu neobičnu činjenicu u gornjem programskom

kodu – poziv funkcije se koristi kao lvalue (objekt koji nastavlja postojati i nakon obavljanja

jedne operacije, za razliku od rvalue koji ne egzistira nakon naredbe u kojoj je korišten)!

Odgovor na pitanje zašto je ovakva pohrana moguća zahtijeva malo više znanja o internom

funkcioniranju jezika R, a za sada je dovoljno reći da se ovdje zapravo radi o pozivu funkcije

pravog imena names<- koji se “skriva” iza puno intuitivnije i lako razumljive sintakse).

Ako iz nekog razloga poželimo obrisati imena elemenata vektora, jednostavno pozivu

funkcije names proslijedimo NULL.

names(visine) <- NULL

Page 30: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________24

3.2.5. Indeksni vektori i matrice

Pokušajmo sada primijeniti naučeno i na složenije podatkovne strukture.

Vježba 26: Indeksni vektori i matrice

# zadana je matrica m` s postavljenim imenima stupaca

m <- matrix(1:30, 6, 5, T)

colnames(m) <- c("a", "b", "c", "d", "e")

# ispišite sve elemente matrice m od drugog do četvrtog retka

# te od trećeg do petog stupca

# sve elemente u stupcu naziva "c" postavite na nulu

# a potom ispišite prva dva retka matrice `m`

# ispišite samo stupac "d"

# ispišite opet stupac "d", ali kod indeksiranja dodajte parametar `drop =

FALSE`

# parametar odvojite zarezom (kao da se radi o "trećoj" dimenziji

indeksiranja)

3.2.6. Indeksni vektori i podatkovni okviri

Sljedeća vježba koristi podatkovni okvir mjesto koji smo već koristili u prethodnim primjerima

i vježbama.

Vježba 27: Indeksni vektori i podatkovni okviri

# ispišite tablicu `mjesto` (za referencu)

# ispišite prva tri retka, drugi i peti stupac

# ispišite stupac "prirez"

# ispišite poštanske brojeve i nazive svih mjesta koja

# imaju prirez veći od 12 % i broj stanovnika veći od 100.000

Rješavanjem ovoga zadatka mogli smo uočiti uobičajeni obrazac “rezanja” podatkovnog

okvira – logički indeksni vektor za retke, znakovni (ili numerički) za stupce. Poznavatelji SQL-

a će uočit će sličnost između te naredbe i SQL upita:

SELECT pbr, nazivMjesta

FROM mjesto

WHERE mjesto.prirez > 12 AND mjesto.brojStanovnika > 100000

Page 31: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________25

Odabir stupaca i redaka nije težak ako se dobro koristimo znanjem o indeksnim vektorima,

no kao što se vidi u zadnjem primjeru, sintaksa često nije previše čitljiva (u usporedbi s npr.

SQL-ovom sintaksom koja obavlja isti posao). Zbog toga postoje različita proširenja R-a koja

ovaj posao uvelike olakšavaju, a koja ćemo detaljno obraditi u jednoj od budućih lekcija koja

će se baviti upravljanjem podatkovnim skupovima.

Što ako NE želimo određene elemente u rezultatu, na primjer, želimo sve stupce OSIM

stupaca nazivMjesta i prirez? Sjetimo se da negativni numerički indeks znači “izbaci

element”. No, kako nam ovo koristi ako stupac identificiramo znakovnim indeksnim

vektorom? Jedna od opcija jest da provjerimo koji su po redu ti stupci i onda se koristimo

numeričkim indeksnim vektorom, no recimo da trebamo generičko rješenje koje će raditi

neovisno o položaju stupaca. Ovdje nam u pomoć nam stiže funkcija which koja pretvara

logički vektor u numerički. Procedura je sljedeća:

• dohvatimo vektor imena stupaca (funkcija names)

• stvorimo logički indeksni vektor koji označava koja imena se nalaze u odabranom skupu

imena (operator %in%)

• uz pomoć funkcije which pretvorimo ovaj logički indeksni vektor u numerički

• iskoristimo taj numerički vektor uz operator - s ciljem izbacivanja odabranih stupaca.

Primjer 8: Izbacivanje stupaca po imenu

mjesto[, -which(names(mjesto) %in% c("nazivMjesta", "prirez"))]

## pbr prosjPlacaKn brojStanovnika

## 1 10000 6359 790017

## 2 51000 5418 128384

## 3 21000 5170 167121

## 4 31000 4892 84104

## 5 20000 5348 28434

Možemo uočiti nekoliko problema sa zadnjim primjerom. Prvo, procedura je relativno

složena, pogotovo za početnika. Drugo, postoji izvjesna vjerojatnost jedne relativno

nezgodne pogreške – ako ni jedan stupac ne odgovara onima navedenim u skupu imena,

umjesto da dobijemo sve stupce, mi nećemo dobiti ni jedan (rezultat će biti “prazan”

numerički vektor, na koji operator - ne djeluje, tako da mi zapravo doslovno tražimo “nijedan”

stupac). Ovo sve pokazuje da se R-ova sintaksa za upravljanje podatkovnim okvirima u

određenim slučajevima može pokazati relativno nezgrapnom i nepotrebno kompleksnom.

Upravo zbog toga R zajednica nudi niz paketa koji umnogome olakšavaju rad s podatkovnim

okvirima te omogućuju pisanje jasnijega, preglednijega programskog kôda s puno manjom

vjerojatnosti pogreške. Vjerojatno najpopularniji takav paket jest dplyr, o kojem će biti nešto

više riječi u jednoj od nastupajućih lekcija.

Page 32: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________26

3.3. Dodatni zadaci za vježbu

1. Stvorite sljedeće vektore:

• (11, 12, 13…, 99)

• (0, 0, 0, 0… , 0) (100 nula)

• (0, 0.1, 0.2…, 1.0).

2. Kolika je suma svih brojeva od 101 do 1001, preskočimo li sve brojeve djeljive s 10?

Koristite se funkcijom sum.

3. Stvorite matricu 3 × 3 s brojevima izvođenjem sljedećih naredbi (funkciju sample ćemo

pobliže upoznati u jednoj od sljedećih lekcija):

# stvaramo matricu 3 × 3 nasumično odabranih elemenata iz skupa od 1 do 100

set.seed(1234)

m <- matrix(c(sample(1:100, 9, T)), nrow = 3, ncol = 3, byrow = T)

Izračunajte inverznu matricu uz pomoć funkcije solve. Provjerite daje li umnožak originalne i

inverzne matrice jediničnu matricu (za množenje matrica koristite se operatorom %*%).

4. Inicijalizirajte ponovo listu svastara korištenu u lekciji. Napravite sljedeće:

• ispišite klasu drugog elementa liste

• ispišite element na trećem mjestu elementa liste naziva slova

• provjerite duljinu elementa naziva imena te na zadnje mjesto dodajte ime "Pero"

• provjerite nalazi li se broj 4 u prvom elementu liste

• na zadnje mjesto liste dodajte novu listu s tri vektora a, b i c koji svi sadrže elemente

(1,2,3).

5. Učitajte podatkovni okvir mtcars (naredba data(mtcars)). Napravite sljedeće:

• ispišite sve parne retke i stupce s tri ili više slova u nazivu

• stupac mpg pokazuje potrošnju u obliku “milja po galonu goriva”. Dodajte stupac l100km

koji će sadržavati potrošnju u litrama na 100 km. Koristite se formulom litara na

100km = 378.5 / milja po galonu

• izbrišite sve retke gdje je broj cilindara (stupac cyl) jednak broju 4.

Page 33: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________27

4. R i programske paradigme

4.1. Što su programske paradigme?

4.1.1. Općenito o programskim paradigmama

Pojam “programske paradigme” predstavlja način na koji programski jezik ili programer

dizajnira, tj. oblikuje svoje programe. Postoji niz programskih paradigmi kod kojih ima dosta

preklapanja, a programski jezici često podržavaju više istih tako da se pitanje “odabira”

programske paradigme često svodi na preuzimanje određenih načela i smjernica koje

odgovaraju kako cilju koji programer želi postići tako i svojstvima odabranoga programskog

jezika.

U kontekstu ovoga tečaja bavit ćemo se programskim paradigmama, osobito u kontekstu

boljega razumijevanja jezika R. Zbog toga nećemo raditi iscrpan pregled postojećih

paradigmi kao ni ulaziti u detalje oko svake, već ćemo odabrati tri najčešće spominjane

paradigme koje direktno utječu na pristup učenju jezika R: imperativna, objektna i funkcijska

paradigma.

4.1.2. Imperativna programska paradigma

Imperativna programska paradigma predstavlja uobičajen pogled na programiranje kao

pisanje naredbi koje mogu na određeni način utjecati na stanje programa. Program je u

ovom slučaju zapravo zapis nekog algoritma u odabranom programskom jeziku, a čije

slijedno izvođenje dovodi do traženog cilja. Program ima definirani tok od početka do kraja, a

naredbama kontrole toka utječemo na uvjetno ili ponavljano izvođenje nekih njegovih

segmenata (tzv. if-else naredbe i programske petlje).

4.1.3. Objektna programska paradigma

Objektna programska paradigma temelji se na stvaranju i upravljanju takozvanim

“objektima”, tj. programskim strukturama koje enkapsuliraju podatke (tzv. “atributi”) i metode

(funkcije koje obavljaju neki posao, često vezan za atribute). Programiranje se svodi na

dizajn ovakvih objekata čije metode omogućuju međusobnu komunikaciju, a čije izvođenje

često dovodi do promjene internih stanja objekata. Objektno programiranje uvodi pojmove

kao što su “klase i instance” (predlošci objekata te sami objekti), nasljeđivanje (izvođenje

novih objekata proširenjem predložaka postojećih), polimorfizam (jedinstveno sučelje nad

različitim objektima) i sl.

4.1.4. Funkcijska programska paradigma

Osnovni princip funkcijske programske paradigme jest da je fokus na poslu koji se obavlja,

ne na objektima nad kojima se obavlja posao. Drugim riječima, ako se objektno orijentirano

programiranje usredotočuje na “imenice”, funkcijsko se usredotočuje na “glagole”.

Programiranje se svodi na stvaranje i provođenje funkcija, a izbjegava se bilo kakvo

“pamćenje stanja”. Poziv funkcije s istim ulazima uvijek mora rezultirati identičnim izlazima.

Ovakav način programiranja stavlja visoku važnost na reproducibilnost i predvidljivost

rezultata programskoga kôda, čineći razumijevanje i korištenje razvijenih programa

Page 34: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________28

jednostavnijim. Također, jedna od karakteristika funkcijske programske paradigme jest ta da

se funkcije tretiraju kao “građani prvoga reda” – one nisu nešto “zamotano” u objekte, one su

samostalne, imaju svoju referencu, mogu se slati u druge funkcije kao parametri ili vraćati

kao rezultat funkcije. Funkcijska programska paradigma često se smatra malo težom za

učenje od imperativne i (donekle) objektne, ali neupitna je njezina učinkovitost kod određenih

skupina problema.

4.2. Programski jezici i programske paradigme

4.2.1. Odabir jezika = odabir paradigme?

Već smo rekli da programski jezici često podržavaju više programskih paradigmi te

omogućavaju programerima prilagodbu na paradigmu koja najviše odgovara njihovim

osobnim preferencama ili cilju koji žele obaviti. Usprkos tomu, određeni jezici “naginju”

određenoj paradigmi, tako se npr. C++ i Java često smatraju “primarno” objektnim jezicima,

Erlang i Haskell funkcionalnima itd. Postoje jezici, kao na primjer Python, za koje je teško

jasno klasificirati naginju li određenoj paradigmi ili ne.

4.2.2. R i programske paradigme

Kod programskog jezika R dosta je teško reći radi li se o “univerzalnom” jeziku (s gledišta

paradigmi) ili ga se može svrstati u jezike koji su snažno naklonjeni određenoj paradigmi.

Neupitno je da je R domenski orijentirani jezik, tj. da je njegova primarna funkcija analiza

podataka. R uredno podržava sve elemente imperativnih jezika – u R-u možemo pisati

naredbe koje mijenjaju stanje programa, a koje je reprezentirano kroz varijable. Isto tako,

gledajući način na koji je R dizajniran, uočava se da on sadržava jasno definirane elemente

objektne programske paradigme – entiteti kojima upravljamo programirajući u R-u su objekti,

a R podržava sve nužne objektno-orijentirane principe kao što su enkapsulacija,

polimorfizam i nasljeđivanje. No, učenjem R-a lako ćemo spoznati da R radi s objektima na

dosta neobičan način, zaobilazeći neke ustaljene konvencije prisutne u drugim jezicima, te

da pisanje R programskoga kôda vrlo često naginje funkcionalnom programiranju – funkcije

su “punokrvni” objekti, prirodno je slati ih kao parametre ili primati kao rezultat drugih

funkcija, a usredotočenost na “radnju”, tj. “posao” koji moramo izvesti vrlo je sukladno

procesu analize podataka gdje se naše programiranje zapravo svodi na razvoj metode koja

će “obraditi” podatke na određeni način i koja bi za isti ulazni skup morala uvijek davati iste

rezultate.

Zaključak koji se nameće je sljedeći – kod učenja R-a treba naučiti i njegove imperativne i

objektne elemente, ali R ne bismo trebali tretirati kao “univerzalni” programski jezik jer je cilj

koji njegovim korištenjem želimo postići u jasnom i očitom skladu s funkcijskom

programskom paradigmom. Shodno tome, iako poznavanje detalja oko funkcijske

programske paradigme nije ključno za svladavanje R-a, prihvaćanje R-a kao funkcionalnoga

jezika te ulaganje truda u svladavanje barem nekih principa ove paradigme može se

višestruko isplatiti.

U nastavku ćemo se baviti elementima jezika R koji odgovaraju različitim paradigmama.

Naučit ćemo naredbe kontrole toka tipične za imperativni pristup, spominjat ćemo i objekte,

ali fokus će ipak biti na funkcijama: kako R “vidi” funkcije, kakve tipove funkcija podržava R,

kako pisati vlastite funkcije te kako ih koristiti kao “prvoklasne” objekte.

Page 35: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________29

5. Upravljanje programskim tokom

5.1. Uvjetno izvođenje naredbi

5.1.1. Naredba if - else

Ako program gledamo kao niz naredbi, onda nam uvjetno izvođenje omogućuje da se

određene naredbe izvedu samo ako su ispunjeni određeni preduvjeti (ili samo ako nisu

ispunjeni). Ovo je u gotovo svakom programskom jeziku poznato kao klasični if - else

konstrukt, a izgleda ovako:

if (izraz) {blok} else {blok}

gdje se pod izraz misli na logički izraz s jednoelementnim logičkim rezultatom.

Uvjetno izvođenje ne koristimo često u interaktivnom radu, ali je vrlo bitno ako razvijamo

vlastite programske funkcije ili skripte. Korištenje if - else konstrukta je samo po sebi

jasno i trivijalno, no moramo pripaziti da ne učinimo jednu pomalo teško uočljivu grešku.

Pokušajmo tu grešku uočiti i ispraviti u sklopu sljedećega zadatka.

Vježba 28: Naredba if-else

# izvršite sljedeću naredbu uvjetnog izvođenja

if (2 > 1) print("Uspjeh!")

# preinačite sljedeće naredbe tako da ne rezultiraju greškom

if (1 > 2) print("Uspjeh!")

else print("Nuspjeh!")

Dakle, potencijalni problem može nam stvarati else dio naredbe ako ga stavimo u novi red

nakon što je naredba if formalno “završila” s gledišta interpretera. Nakon prvog retka

interpreter će doći do vrijednosti FALSE, ispis neće biti izvršen te će interpreter nastaviti s

‘novom’ naredbom koja to u ovom slučaju nije pa će javiti grešku. Postavljanjem naredbe u

blok koji ne zatvaramo u istom retku dajemo interpreteru na znanje da “ima još”, tako da

onda uredno možemo ubaciti i željeni else dio.

5.1.2. Naredba ifelse

Na ovom mjestu nije loše naučiti i funkciju ifelse koja predstavlja svojevrsnu “vektoriziranu”

inačicu naredbe if - else. Ona, strogo gledajući, nema veze s uvjetnim izvođenjem

naredbi, ali je vrlo korisna u radu s programskim okvirima kada želimo dodati stupac –

binarnu “zastavicu” koja opisuje je li neki uvjet ispunjen ili ne.

Rad ove funkcije najlakše možemo prikazati uz pomoć primjera.

Page 36: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________30

Primjer 9: Funkcija ifelse

a <- 1:3

b <- c(0, 2, 4)

x <- ifelse(a < b, 2, 5)

x

## [1] 5 5 2

5.2. Programske petlje

5.2.1. R i programske petlje

U programskom jeziku R imamo tri tipa petlji:

• repeat – beskonačna petlja

• while – petlja s provjerom uvjeta na početku

• for – iteratorska petlja (“petlja s poznatim brojem ponavljanja”).

5.2.2. Petlja repeat

Petlja repeat je najjednostavnija petlja. Ona ima sljedeću sintaksu:

repeat {blok}

Ovdje se radi o “beskonačnoj” petlji gdje se nakon završetka bloka on ponovo izvršava i tako

unedogled. Jedini način izlaska iz ovakve petlje jest korištenje naredbe break. Pored ove

naredbe imamo i naredbu next koja će preskočiti ostatak bloka, ali neće izaći iz petlje već će

nastaviti izvršavati blok od početka.

Pogledajmo kako radi ova petlja u sljedećoj vježbi.

Vježba 29: Petlja repeat

# prije izvršavanja sljedećeg bloka odgovorite na pitanja:

# - hoće li se petlja izvršavati beskonačno?

# - što će se ispisati na zaslonu?

i <- 1

repeat {

i <- i + 1

if (i %% 2 == 0) next

print(i)

if (i > 10) break

}

## [1] 3

## [1] 5

## [1] 7

Page 37: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________31

## [1] 9

## [1] 11

Često unaprijed znamo uvjet izlaska iz petlje te bismo ga htjeli staviti na jasno vidljivo mjesto

tako da nije “skriven” u tijelu petlje. Za to nam pomaže takozvana while petlja.

5.2.3. Petlja while

Petlja while predstavlja “najčišći” oblik programske petlje čija sintaksa doslovno glasi “dok je

uvjet ispunjen, ponavljaj navedeni kôd”:

while (uvjet) {blok}

Vježba 30: Petlja while

# dodajte uvjet petlje tako da se ona izvrši

# točno 7 puta

i <- 1

while() {

print(i)

i <- i+1

}

Kod ove petlje moramo paziti da se u određenoj iteraciji moraju stvoriti uvjeti za izlaz, inače

ona također postaje “beskonačna” petlja. Usprkos tomu što imamo jasno definiran način

izlaska iz petlje, mi i u ovoj petlji možemo slobodno koristiti ključne riječi next i break, koje

imaju istu funkciju kao i kod petlje repeat.

5.2.4. Petlja for

Petlja for ili “iteratorska petlja” obično služi za “šetanje” po nekoj podatkovnoj strukturi

(najčešće vektoru), uzimajući element po element i nešto radeći s njim. Ona koristi ključnu

riječ for, ime nove (iteratorske) varijable, ključnu riječ in te vektor čije se vrijednosti uzimaju

jedna po jedna i koriste unutar petlje (uočite da navedeni in nije isto što i operator %in% koji

provjerava nalazi li se neki element u nekom skupu!). Sintaksa ove petlje je sljedeća:

for (i in v) {radi nešto s i}

Uočimo da ovdje varijabla i nije “brojač” – u svakoj iteraciji petlje ona postaje vrijednost

elementa do kojeg smo došli. Ako baš želimo iterirati po indeksima, a ne po samim

elementima, onda možemo koristiti konstrukt for (i in 1:length(a)).

Page 38: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________32

Vježba 31: Petlja for

a <- seq(-10, 10, 4)

# ispišite elemente vektora `a` jedan po jedan

# uz pomoć petlje `for`

# pristupajte elementima direktno

# ponovite isto, ali iterirajte po indeksima

Uočite da je drugi način bolji ako želite mijenjati elemente vektora ili imati informaciju na

kojem se mjestu unutar originalnoga vektora trenutačno nalazite.

5.2.5. Korištenje programskih petlji u jeziku R

Sada kad smo naučili sintaksu petlji važno je naglasiti jednu činjenicu – u programskom

jeziku R u pravilu se ne preporučuje korištenje programskih petlji. Iako ovo inicijalno

možda djeluje neobično s obzirom na sveprisutnost petlji u drugim programskim jezicima,

razlog je jednostavan – R je jezik dizajniran upravo na način da naredbe jezika rade po

principu “više stvari odjednom”. Već smo vidjeli da principi vektorizacije i recikliranja

učinkovito obavljaju poslove koji bi u drugim programskim jezicima zahtijevali petlju, a u

poglavljima koja slijede vidjet ćemo da R nudi i mnoge druge konstrukte koji nude poželjniju

alternativu od eksplicitnog korištenja programskih petlji.

Recimo, sljedeći primjer je sintaksno potpuno ispravan:

# primjer nepotrebnoga korištenja petlje

a <- 1:5

b <- 6:10

c <- numeric()

for (i in 1:length(a)) c[i] <- a[i] + b[i]

ali vjerojatno radi sporije i puno je nečitljiviji od:

# R-ovska sintaksa

a <- 1:5

b <- 6:10

c <- a + b

Sve navedeno, naravno, ne znači da petlje u R-u ne smijemo koristiti već samo to da bi

njihovo korištenje trebalo biti popraćeno dodatnim razmatranjem stvarne potrebitosti petlje na

tom mjestu te postojanja alternativne sintakse (ili funkcije!) koja isti posao obavlja

deklarativno (i potencijalno brže jer su mnoge rutine R-a implementirane u jeziku C). Rano

prihvaćanje “R-ovskoga” načina razmišljanja rezultirat će dugoročnim pozitivnim efektima koji

će se očitovati kompaktnijim, čišćim i često učinkovitijim programskim kôdom.

Page 39: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________33

6. Funkcije u jeziku R

6.1. Ugrađene funkcije

6.1.1. Paketi i staza pretrage

Pod pojmom “ugrađenih funkcija” nekoga programskog jezika često mislimo na funkcije koje

su nam odmah dostupne nakon instalacije odabrane distribucije toga jezika. Kod

programskoga jezika R vrlo brzo se upoznajemo s takvim funkcijama učeći osnovnu sintaksu

jezika te neke njegove temeljne koncepte. Tako smo do sada zasigurno već upoznali razne

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. Operatori kao što

su +, *, :, <- i sl. su također zapravo samo funkcije, izvedene na način da omogućuju

korištenje specifične “operatorske” sintakse.

Sve ove funkcije logički su organizirane u zasebne kolekcije zvane “paketi”. Novi paket

učitavamo uz pomoć naredbe library(ime_paketa), a R okolina automatski pri pokretanju

učitava predefiniranu kolekciju korisnih paketa. Potpuna sintaksa za zvanje funkcije iz nekog

paketa jest ime_paketa::ime_funkcije(parametri).

Uzmimo za primjer paket stats koji sadrži bogati skup funkcija vezanih za statističke obrade

– ovaj će paket već biti učitan čim pokrenemo distribuciju jezika R. Odaberimo iz ovog

paketa jednu od vrlo često korištenih funkcija zvanu rnorm. Ona vraća numerički vektor

željene duljine čiji su elementi nasumično odabrani iz normalne distribucije s aritmetičkom

sredinom 0 i standardnom devijacijom 1 (ove vrijednosti možemo i promijeniti uz pomoć

parametara mean i sd).

Pozovimo ovu funkciju u sljedećoj vježbi.

Vježba 32: Poziv funkcije iz odabranog paketa

# stvorite vektor x koji će imati 10 slučajnih elemenata

# izvučenih iz standardne normalne distribucije

# koristite puni naziv funkcije `rnorm` iz paketa `stats`

# zaokružite elemente vektora x na dvije decimale

# koristite puni naziv funkcije `round` iz paketa `base`

# ispišite vektor x

Iako je ovo sintaksno ispravan način pozivanja funkcije, R nam omogućuje da izuzmemo

nazive paketa i jednostavno navedemo samo naziv funkcije.

Page 40: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________34

Vježba 33: Poziv funkcije bez imena paketa

# stvorite vektor y po istom principu kao i vektor x

# obavite sve u jednom retku

# koristite nazive funkcija bez naziva paketa

# ispišite y

Kako je R znao koju funkciju pozvati ako se one nalaze u različitim paketima? Odgovor na to

daje nam koncept “staze pretrage”. Naime, R učitava pakete određenim slijedom koji se

zapisuje u spomenutu stazu pretrage (engl. search path) pri čemu se paket koji je zadnje

učitan postavlja na drugo mjesto u stazi, odmah iza radne, tj. “globalne” okoline (koja je

uvijek na prvom mjestu). Kada pozivamo neku funkciju (ili referenciramo neku varijablu), a

ista nije prisutna u radnoj okolini, R će se “prošetati” stazom pretrage i slijedno tražiti tu

varijablu ili funkciju u paketima koji se nalaze u njoj. Pretragu će završiti čim nađe prvo

“poklapanje”.

Stazu pretrage možemo i sami vidjeti uz pomoć funkcije search.

Vježba 34: Staza pretrage

# ispišite stazu pretrage uz pomoć funkcije `search` (bez parametara)

# učitajte novi paket uz pomoć funkcije `library` (npr. paket `dplyr`)

# (ako paket nije instaliran, morate ga prvo dohvatiti uz pomoć funkcije

`install.packages`)

# ispišite ponovo stazu pretrage

Uočite dvije bitne stvari. Prvo, globalna okolina uvijek ostaje na prvom mjestu, tako da

nijedan novoučitani paket ne može “pregaziti” varijable i funkcije radne okoline. Drugo, kod

učitavanja novoga paketa možemo dobiti upozorenje da su sada neki objekti “maskirani”.

Ovo se događa kada novi paket sadrži objekte s imenima koja već postoje u stazi pretrage.

Ovo ne znači da su maskirani objekti sada nedostupni, već samo da njima sada moramo

pristupati preko punog imena (ime_paketa::ime_funkcije). Zbog ovoga moramo biti

pažljivi kod izrade vlastitih programskih skripti ili izvještaja, te uvijek koristiti puno ime ako se

bojimo da će zbog naknadnog učitavanja nekog paketa neki dio programskoga kôda početi

raditi neispravno jer će se unutar njega pozivati “kriva” funkcija.

6.1.2. Dohvat pomoći

Kada koristimo programski jezik R, češće se oslanjamo na već postojeće funkcije ili na

funkcije iz dodatnih paketa koje je netko drugi stvorio i dao na uporabu R zajednici. Zbog

toga je vrlo važno znati kako pronaći informaciju o tome postoji li već, za neki zadatak koji

želimo izvršiti, stvorena funkcija te, ako da, na koji ju način pozvati.

Da bismo riješili prvi problem, utvrđivanje postoji li već neka funkcija i, ako postoji, pronaći

gdje se nalazi, potrebno je koristiti dostupnu dokumentaciju, podsjetnike ili internetske

tražilice. Alat RStudio nudi odlične podsjetnike za razne aspekte i razine korištenja jezika R,

a koji se vrlo lako mogu pronaći upisujući “RStudio Cheat Sheets” u internetsku tražilicu.

Page 41: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________35

Isto tako, vrlo često odgovor na pitanje o tome postoji li neka funkcija možemo dobiti

postavljanjem jasno sročenoga pitanja u tražilicu na engleskom jeziku, na primjer, “What is a

function for adding vector elements in R?”.

Jednom kada znamo koju funkciju odabrati, te po potrebi instaliramo paket u kojem se ona

nalazi, idući korak jest pregled dokumentacije o navedenoj funkciji. Za to postoji funkcija

help, iako je često lakše staviti znak ? i ime funkcije, na primjer, ?rnorm.

Primjer 10: Traženje pomoći

# isprobajte nazive i nekih drugih funkcija

?rnorm

Pozivanje pomoći za neku funkciju često daje informacije i o nekim srodnim funkcijama, a na

dnu dokumentacije često se daju i primjeri korištenja. Ako želimo, primjere možemo i odmah

zatražiti uz pomoć funkcije examples.

Iako dokumentacija funkcije često može djelovati pomalo nečitljivo ili čak zastrašujuće,

potrebno se naviknuti na ovakav pristup traženja pomoći jer je to najbrži i najučinkovitiji način

za pisanje kvalitetnoga, ciljno orijentiranoga programskog kôda. Često nije loše ni uz pomoć

internetske tražilice potražiti dodatne primjere korištenja tražene funkcije, ako ih se može

pronaći, jer programeri često nailaze na slične probleme te je moguće da objašnjenje ili

rješenje problema s nekom funkcijom već postoji te je lako dostupno uz postavljanje pravog

upita u internetsku tražilicu.

6.1.3. Klasične, primitivne i interne funkcije

R je jezik otvorenoga kôda, u što se vrlo lako može uvjeriti uz pomoć jednostavnog

eksperimenta. Pokušajmo pozvati funkciju sd (za traženje standardne devijacije elemenata

vektora), ali na način da napišemo samo ime funkcije, bez zagrada i parametara.

Primjer 11: Ispis funkcije sd

sd

## function (x, na.rm = FALSE)

## sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x),

## na.rm = na.rm))

## <bytecode: 0x0000000015f99d58>

## <environment: namespace:stats>

Možemo uočiti da nam R uredno daje ispis programskoga kôda ove funkcije. Točnije, vidimo

programski kôd, memorijsku adresu na kojoj je funkcija pohranjena (ako se radi o unaprijed

prevedenoj funkciji, što je često slučaj s ugrađenim funkcijama) te paket u kojem se funkcija

nalazi. Uvid u izvorni kôd može nam pomoći kod eventualnih problema s korištenjem

funkcije, ako nam čitanje dokumentacije nije već pomoglo, a imamo i opciju kopiranja kôda i

stvaranje vlastite inačice funkcije ako želimo.

Možemo zatražiti i samo parametre, tijelo ili okolinu funkcije uz pomoć funkcija formals,

body i environment.

Page 42: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________36

Primjer 12: Ispis detalja o funkciji sd

# ulazni parametri funkcije (lista!)

formals(sd)

# tijelo funkcije

body(sd)

# okolina funkcije (radna okolina ili paket)

environment(sd)

## $x

##

##

## $na.rm

## [1] FALSE

##

## sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x),

## na.rm = na.rm))

## <environment: namespace:stats>

Kod nekih funkcija nećemo uspjeti dobiti ispis, na primjer, kod funkcije sum za sumiranje

elemenata vektora.

Primjer 13: Ispis funkcije sum

sum

## function (..., na.rm = FALSE) .Primitive("sum")

Ovakve “primitivne” funkcije nalazimo samo u osnovnom (base) paketu. One formalno i nisu

“prave” R funkcije – ovdje se radi o funkcijama koje su implementirane na niskoj razini

(najčešće u jeziku C) koje su zbog toga i vrlo učinkovite. Zbog specifičnost ovakvih funkcija

dizajneri jezika R ih u pravilu izbjegavaju tako da se nove primitivne funkcije implementiraju

samo ako za to postoje dovoljno jaki razlozi.

Pored primitivnih funkcija imamo i takozvane “interne” funkcije, koje ćemo prepoznati po

ispisu .Internal umjesto .Primitive. Ovdje se također radi o niskorazinskim funkcijama

koje nisu “prave” R funkcije iako imaju neka dodatna svojstva koje primitivne nemaju, tj. kod

njihovoga pozivanja logika izvođenja se provodi sličnije “pravim” R funkcijama.

U praksi nema neke konkretne potrebe za učenjem detalja o primitivnim i internim funkcijama

– time se bavi programerski tim koji dizajnira jezik R, a prosječnim korisnicima je dovoljna

informacija da se logika ovakvih funkcija odvija na niskoj razini, vrlo učinkovito, ali da

nemamo jednostavan uvid u njihovo funkcioniranje pored onoga što nudi dokumentacija (što

je često i dovoljno, jer je svrha ovih funkcija često očigledna).

Konačno, za neke funkcije možemo dobiti prilično zbunjujući ispis, npr. za funkciju mean:

Page 43: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________37

Primjer 14: Ispis funkcije mean

mean

## function (x, ...)

## UseMethod("mean")

## <bytecode: 0x0000000013f40e30>

## <environment: namespace:base>

Vidimo da smo umjesto tijela funkcije dobili poziv funkcije neobičnog imena UseMethod.

Ovakav ispis ćemo zapravo često susretati ako pokušamo pogledati kako izgledaju neke

popularne ugrađene funkcije, kao na primjer, print, plot ili summary. Razlog tomu je što su

ovo zapravo takozvane “generičke” funkcije koje su direktno povezane s načinom na koji R

realizira svoj objektni model. Više o njima naučit ćemo u idućoj lekciji.

6.2. Korisnički definirane funkcije

Već smo rekli da standardni način korištenja programskoga jezika R uglavnom uključuje

primjenu postojećih funkcija, bilo da se radi o funkcijama iz paketa isporučenih s R

distribucijom ili onih naknadno dohvaćenih iz CRAN repozitorija. No, tijekom rada u R-u

često se pojavljuje potreba za programiranjem vlastite funkcije, bilo da se radi o jednokratnoj,

“anonimnoj” funkciji ili implementaciji nekoga složenijeg algoritma ili procesa koji planiramo

naknadno koristiti u budućim programima. Shodno tome, svladavanje jezika R neizbježno

uključuje i učenje pisanja vlastite, korisnički definirane funkcije.

6.2.1. Sintaksa pisanja funkcije

U općenitom slučaju, definicija nove funkcije izgleda ovako:

ime_funkcije <- function(ulazni argumenti) {

tijelo funkcije

}

Uočimo da kod definicije funkcije koristimo operator <-. Ovo nije slučajno – definicija funkcije

nije ništa drugo nego stvaranje objekta klase function koji onda pridružujemo određenoj

varijabli; ime varijable zapravo je “naziv” funkcije.

Funkcija prima nula ili više ulaznih parametara (argumenata). Za razliku od programskih

jezika kao što su C++ ili Java, u R-u ne definiramo eksplicitno njihove tipove – ulazni

argumenti imaju samo ime te opcionalno nazivnu vrijednost.

Cilj funkcije je vratiti rezultat u pozivajuću funkciju ili program. Funkcija formalno može vratiti

samo jednu vrijednost, što nije nužno restrikcija ako želimo vratiti više vrijednosti,

jednostavno taj skup vrijednosti uokvirimo u vektor, listu ili objekt. Ključna riječ return je

opcionalna – funkcija vraća rezultat zadnjeg izraza u funkciji pa je često dovoljno navesti

samo varijablu koja predstavlja povratnu vrijednost kao zadnji red funkcije.

Page 44: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________38

Funkcija je, dakle, jednostavno komad programskoga kôda koji na osnovi ulaznih

argumenata i programske logike vraća neki rezultat. Ako želimo povećati robusnost funkcije

na način da ćemo odbiti izvođenje posla ako nisu zadovoljeni određeni uvjeti, unutar tijela

funkcije možemo koristiti funkciju stopifnot(<logički izraz>). Ova funkcija izračunava

zadani logički izraz i prekida uokvirujuću funkciju ako navedeni uvjet nije istinit.

Vježba 35: Prva korisnički definirana funkcija

# napišite funkciju `veci` koja prima dva numerička vektora iste duljine

# i vraća vektor koji sadrži veći od dva elementa na istim mjestima

# ako jedan ili oba vektora nisu numerički ili nisu iste duljine,

# funkcija mora izbaciti grešku

# u funkciji nemojte koristiti petlje

#

# NAPUTAK: sjetimo se funkcije `ifelse`!

# pozovite funkciju `veci` nad kombinacijama vektora

# c(T, F, T) i c(1, 2, 3)

# c(1, 2, 3, 4) i c(5, 6, 7)

# c(1, 2, 3) i c(0, 4, 2)

# (preporuka – drugi dio zadatka isprobati direktno u konzoli!)

Kod poziva funkcije možemo, ali ne moramo, navesti imena parametara, a R dozvoljava

miješanje imenovanih i neimenovanih parametara (iako to nije nešto što bismo trebali često

koristiti u praksi). Kada R bude povezivao poslane vrijednosti s formalnim parametrima,

imenovani parametri imat će prioritet te će se prvi razriješiti, a potom će se redom razrješivati

neimenovani parametri.

U ovo se možemo uvjeriti u sljedećoj vježbi. Ova vježba usput demonstrira i korištenje jedne

vrlo korisne funkcije – paste, koja konkatenira znakovne nizove uz automatsko dodavanje

razmaka (za spajanje bez razmaka postoji alternativna funkcija paste0).

Vježba 36: Parametri funkcije

ispisiABC <- function(a, b, c) {

print(paste("A:", a, "B:", b, "C:", c))

}

# razmislite – što ispisuje sljedeći poziv funkcije?

ispisiABC(1, a = 2, 3)

Page 45: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________39

U praksi bismo se trebali držati konvencije da prvo koristimo neimenovane parametre, a

potom imenovane. Uobičajeno je da postavljamo samo one imenovane parametre čija nam

nazivna vrijednost ne odgovara, pri čemu strogi raspored nije bitan (iako će praćenje

rasporeda zadanoga potpisom funkcije povećati čitljivost našega kôda).

Ako želimo napisati funkciju koja prima proizvoljan broj argumenata, koristimo se elementom

..., tj. trotočkom. Primjer ovakve funkcije jest gore prikazana ugrađena funkcija paste koja

može primiti proizvoljan broj znakovnih nizova. Ako koristimo trotočku u našoj funkciji, u

potpisu je u pravilu stavljamo na kraj liste argumenata, a unutar same funkcije ju potom

jednostavno pretvorimo u listu te pristupamo njezinim parametrima na način koji nam

odgovara.

Vježba 37: Funkcija s proizvoljnim brojem parametara

ispisiParametre <- function(...) {

parametri <- list(...)

for (p in parametri) print(p)

}

# pozovite gornju funkciju s proizvoljnim parametrima

6.2.2. Princip kopiranja-kod-izmjene (engl. copy-on-change)

Jedno od češćih pitanja koje se postavlja kod učenja novih programskih jezika jest rade li

funkcije na način “poziva preko vrijednosti” (engl. call-by-value) ili “poziva preko reference”

(engl. call-by-reference). Razlika se svodi na sposobnost funkcije da mijenja vrijednosti

varijabli koje su poslane na mjestu formalnih argumenata funkcije; kod call-by-value principa

u funkciju se šalju samo “vrijednosti” parametara, tj. “kopije” originalnih argumenata. S druge

strane, kod call-by-reference principa funkcija prima “reference” originalnih varijabli, tj.

ponaša se kao da su originalne varijable proslijeđene funkciji i sve izmjene nad njima odraziti

će se u pozivajućem programu.

Jezik R koristi hibridni princip poznat po nazivom “kopiranje kod izmjene” (engl. copy-on-

modify). Kod ovog principa u funkciju se prosljeđuju reference argumenata, što nam

omogućuje da prenosimo i “velike” varijable bez straha da će doći do nepotrebnog kopiranja.

No ovo vrijedi samo ako funkcija ne mijenja vrijednost dobivenih varijabli – u trenutku kada

funkcija pokuša provesti bilo kakvu izmjenu, provodi se kopiranje varijable i funkcija dalje

nastavlja rad na kopiji. Zbog ovoga se kaže da R kao takav ne podržava call-by-reference

(usputno – jedan razlog uvođenja objekata tipa “reference classes”, tj. RC objekata u jezik R

upravo je uvođenje ovoga principa).

Pogledajmo što se događa kada naivno pokušamo izmijeniti varijablu iz pozivajuće okoline.

Page 46: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________40

Primjer 15: Pokušaj izmjene varijable iz pozivajuće okoline

f <- function(x) {

x <- x + 1

}

x <- 5

f(x)

print(x)

## [1] 5

Ako zaista želimo da funkcija mijenja varijablu iz pozivajuće (npr. globalne) okoline, to

možemo izvesti korištenjem operatora <<-. Ovo je takozvani “operator dodjele vanjskom

opsegu” (engl. scoping assignment operator), a njegova funkcija jest da izmjeni varijablu

zadanog imena koja se nalazi “negdje na stazi pretrage”. R ide slijedno po stazi pretrage i

mijenja prvu pojavu navedene varijable. Ako varijabla toga naziva ne postoji nigdje u stazi

pretrage, R će stvoriti novu varijablu u prvoj okolini iznad okoline funkcije.

Primjer 16: Operator <<-

# operator `<<-`

f <- function(x) {

x <- 6

x <<- 7

}

x <- 5

f()

x

## [1] 7

Budući da ovaj operator utječe na varijable izvan svoje okoline, treba biti oprezan s njegovim

korištenjem.

Page 47: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________41

6.2.3. Anonimne funkcije

Ako definiramo funkciju, a ne pridružimo istu nekoj varijabli, onda smo stvorili takozvanu.

“anonimnu funkciju”.

Primjer 17: Anonimna funkcija

function(x) x * x

Uočimo da ovime nismo uveli nikakav novi koncept jer je svaka funkcija inicijalno

“anonimna”. Ako se vratimo na sintaksu definicije funkcije, vidimo da je ona zapravo

kombinacija stvaranja anonimne funkcije i pridjeljivanja iste nekoj varijabli uz operator

pridruživanja. Naravno, ostavljanje funkcije anonimnom, kao što smo izveli u gornjem

primjeru, nema previše smisla, isto kao što nema smisla definirati neki vektor ili listu bez

pridjeljivanja reference na taj objekt – u tom slučaju stvoreni objekt nije ni na koji način

iskoristiv jer nema nijedne poveznice prema njemu te će ga R vrlo brzo obrisati u sklopu

rutine “čišćenja smeća”.

Možemo se zapitati kako onda izgleda scenarij gdje je korištenje anonimne funkcije smisleno

i korisno? Eksplicitne anonimne funkcije koristimo kada nam baš treba “jednokratna” funkcija,

recimo kao argument neke druge funkcije. Primjere ovoga vidjet ćemo u kasnijim

poglavljima.

Page 48: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________42

7. Objekti u jeziku R

7.1. Različiti objektni modeli jezika R

Kako bismo objasnili što su zapravo generičke funkcije, moramo se vratiti na priču o

programskim paradigmama i činjenici da je R dizajniran kao objektno orijentirani jezik,

zajedno s mehanizmima koje objektno orijentirana paradigma nalaže – enkapsulacija

(združivanje različitih varijabli u zajedničku cjelinu), polimorfizam (uporaba iste funkcije nad

različitim objektima rezultirat će različitim operacijama ovisno o prirodi objekta) i nasljeđivanje

(izvođenje novih objekata iz postojećih na način da ih proširujemo dodatnim elementima).

R je svoj inicijalni način modeliranja objekata preuzeo iz jezika S te su stoga takvi objekti

poznati kao “S3 objekti” (prema inačici jezika S iz koje su izvorno preuzeti). Ovaj način, koji

ćemo upoznati u nastavku, zapravo je vrlo nekonvencionalan, ali i jednostavan za korištenje.

Upravo ova nekonvencionalnost i jednostavnost zasmetala je R programerima koji teže

složenijem, profinjenijem i robusnijem objektnom modelu, tako da ubrzo nastaju tipovi

objekata koji više odgovaraju načinu na koji drugi jezici upravljaju objektima. Novi objektni

modeli dobivaju svoje poklonike, ali ne uspijevaju istisnuti osnovni, S3 model, koji ne gubi na

popularnosti.

Sve ovo dovelo je do toga da danas formalno imamo čak tri tipa objekata u programskom

jeziku R (tj. četiri, ako brojimo i bazične objekte):

• osnovni objekti (engl. base classes) – osnovni, “bazični” elementi jezika R (funkcije,

vektori, podatkovni okviri)

• S3 objekti – objektni model preuzet iz jezika S (inačica 3)

• S4 objekti – formalniji i rigorozniji način stvaranja objekata koji se približava standardnim

objektno-orijentiranim mehanizmima iz drugih jezika

• RC objekti (engl. reference classes) – najnoviji način stvaranja objekata (uveden u

inačici R 2.12) koji u potpunosti replicira “klasične” objektno-orijentirane principe

utemeljene na razmjeni poruka.

Postojanje triju različitih modela definiranja objekata (možemo zanemariti osnovni jer njega

formalno ne možemo “proširivati” novim objektima) može djelovati demotivirajuće – je li

potrebno naučiti sva tri modela? Kako ih razlikovati? Koji odabrati? No, usprkos činjenici da

se priča o objektnoj prirodi jezika R tijekom njegova razvoja (nepotrebno) zakomplicirala,

dobra vijest je da je za većinu potreba sasvim dovoljno naučiti na koji način radi model S3.

Veliki broj popularnih R paketa koristi isključivo S3 klase i moguće je raditi dugo vremena u

jeziku R bez potrebe za učenjem S4 ili RC modela. Zbog ove činjenice, u nastavku ćemo se

usredotočiti isključivo na S3 klase, dok ćemo bavljenje drugim objektnim modelima ostaviti

kao opcionalni izazov za zahtjevnije korisnike.

7.1.1. Pregled objektnoga modela S3

Kao što je već rečeno, S3 objekti zapravo su preneseni iz programskog jezika S i

predstavljaju relativno primitivno poimanje koncepta “objekta”, barem što se tiče očekivanja

glede standardnih načina stvaranja objekata i pripadajućih metoda. S3 objekt je zapravo

obična lista kojoj smo definirali atribut klase, imena class .

Page 49: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________43

Primjer 18: Stvaranje novoga S3 objekta

# stvaramo novi objekt klase `osoba`

pero <- list(oib = "12345678", prezime = "Peric", tezina = 78)

class(pero) <- "Osoba"

I to je to! Uočite da nemamo formalno definiranoga “predloška” klase koji onda instanciramo

u objekt kao što je ustaljena praksa u drugim programskim jezicima. Kod S3 objekata

jednostavno stvaramo listu i onda deklariramo da je ta lista objekt određene klase, iako je

struktura te klase zapravo samo implicirana izgledom objekta (i ne mora uopće odgovarati

strukturi nekog drugog objekta koji se deklarirao kao pripadajući istoj klasi).

Naravno, ovako ležeran način konstrukcije objekata ipak nije preporučljiv te se zbog toga

savjetuje da klase ne oblikujemo “ručno”, već da to radimo uz pomoć posebne

konstruktorske funkcije čiji će parametri zapravo definirati izgled objekta (ovo ćemo naučiti u

lekciji o stvaranju vlastitih funkcija).

Možemo se pitati što se događa s nasljeđivanjem ako se klase definiraju na ovako trivijalan

način?

R omogućuje nasljeđivanje, ali također na vrlo neformalan i relativno trivijalan način, i svodi

se na to da umjesto navođenja samo jednoga “naziva” klase uz pomoć atributa class, mi

stvorimo znakovni vektor gdje će prvi element biti naziv klase, a ostali elementi će biti klase

roditelji, poredani prema “važnosti”. Na primjer, ako smo stvorili novi objekt mate klase

Zaposlenik nad kojim bismo htjeli koristiti iste implementacije određenih generičkih funkcija

razvijenih za potrebe objekata klase Osoba, onda je dovoljno izvesti sljedeće:

Primjer 19: Nasljeđivanje S3 objekta

mate <- list(oib = "12345678", prezime = "Peric", tezina = 78,

godZaposlenja = 2001)

class(mate) <- c("Zaposlenik", "Osoba")

Primijetimo da smo ovdje sav posao oko nasljeđivanja atributa obavili “ručno”, tj. trebali smo

se sami pobrinuti da mate ima atribute klase Osoba koje će generička funkcija koju pozivamo

koristiti.

Ponovimo kratko dosadašnje zaključke o S3 objektima:

• S3 objekti funkcioniraju na jednostavan, neformalan način – to su jednostavno liste s

postavljenom proizvoljnom vrijednosti class atributa

• puno toga ostavljeno je na odgovornosti programera

• jednostavni su za uporabu ako su jednostavni i objektni modeli koje dizajniramo, ali

nisu pogodni za kompleksnije objektne modele zbog teškog održavanja modela i

velike mogućnosti pogrešaka.

Page 50: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________44

7.1.2. Generičke funkcije

Gledajući gore definirani način dizajna objekta, opravdano je postaviti i dodatno pitanje – a

gdje su metode? Kao što znamo, standardni objektno-orijentirani principi pretpostavljaju

enkapsulaciju atributa ali i metoda u okvir objekta. Upravo tu leži osnovna razlika između S3

objekta i “standardnih” objekata iz drugih programskih jezika – kod S3 objekata metode se

definiraju izvan objekta u obliku takozvanih generičkih funkcija.

Zašto je tomu tako? Ideja je sljedeća – u radu s objektima korisnik (programer, analitičar)

često poziva iste funkcije (npr. “ispis”, “crtanje”, “sažeti opis”) nad objektima različitoga tipa.

Funkcija istog imena ali različite implementacije, ovisno o objektu nad kojim radi, zove se

generička funkcija. Tako, recimo, funkcija print uvijek rezultira nekakvim ispisom, ali kako

će ispis izgledati zapravo ovisi o objektu kojeg ispisujemo.

Ovaj način dizajna objekata može djelovati iznimno nekonvencionalno, no on često

omogućuje puno intuitivniji rad, pogotovo korisnicima koji nemaju veliko iskustvo s

programiranjem. Konkretno, usporedimo naredbu:

pokreni(auto, brzina = 20)

s naredbom:

auto.pokreni(brzina = 20)

Čitajući prvu naredbu, auto doživljavamo kao “objekt” (u smislu službe riječi u rečenicu), tj.

naredba je oblikovana tako da nešto radimo “nad” tim objektom. Druga naredba auto

postavlja kao subjekt, što je uobičajena praksa u objektno-orijentiranim jezicima, ali nije u

skladu s općenitim poimanjem obavljanja radnji nad nekim objektima. Drugim riječima, u

razgovornom jeziku prirodnije je reći da mi vozimo auto, nego da tražimo od automobila da

vozi samoga sebe.

Nadalje, s obzirom na funkcijsku prirodu jezika R, u radu s njim često provodimo “slične”

poslove nad različitim objektima – ispisujemo njihov sadržaj, crtamo ih na grafu, tražimo neke

sažete detalje o njima i sl. Upravo zbog toga, a i činjenice da u R-u često radimo interaktivno,

R je dizajniran na način da razmišljamo o tome što želimo učiniti umjesto da se pitamo gdje

je funkcija koju želimo pozvati. Ako želimo ispisati neki objekt, logično je da ga samo

proslijedimo funkciji print, ako ga želimo nacrtati funkciji plot, a ako želimo ispis sažetka –

funkciji summary.

Kako pojedina funkcija “zna” što učiniti s objektom? Odgovor je jednostavan – generička

funkcija je samo “sučelje” prema “pravoj” funkciji koju pozivamo, a logika kako pronaći pravu

funkciju je vrlo trivijalna – ako je ime generičke funkcije genFun, a naziv klase objekta koju joj

prosljeđujemo nazKlase, funkcija koja se zapravo poziva jest genFun.nazKlase. Ako takve

funkcije nema, poziva se funkcija genFun.default.

U ovo se lako možemo uvjeriti samostalno u sklopu sljedeće vježbe.

Page 51: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________45

Vježba 38: Generičke funkcije

# ispišite tijelo funkcije `summary`

# ispišite tijelo funkcije koja se zapravo poziva kad pozovete

# funkciju `summary` nad objektom klase `factor`

Razumijevanjem principa rada generičkih funkcija upotpunili smo sliku o S3 objektima.

Najvažnija stvar koju moramo usvojiti jest da kod ovog modela funkcije nisu dio samog

objekta, već se definiraju zasebno, a poveznica između objekta i “njegove” metode jest

samo u nazivu funkcije pomoću kojega će R “povezati” generičku funkciju i taj objekt. Iako

je ovaj princip primitivan i podložan greškama u rukama nepažljivoga programera, on je

neupitno jednostavan za uporabu i vrlo učinkovit. Konačno, uočimo da ovaj pristup nije u

potpunosti svojstven isključivo jeziku R – slične principe nalazimo i u drugim programskim

jezicima (npr. Python povezuje print funkciju i objekt preko posebne metode objekta

__str__). R je samo specifičan po tome što taj princip koristi kao nazivni mehanizam dizajna

metoda za svoje objekte.

Objekte i generičke funkcije ponovo ćemo posjetiti kada naučimo stvarati vlastite funkcije, što

će nam omogućiti da stvorimo kako konstruktore naših objekata tako i njihove generičke

funkcije.

7.2. Stvaranje vlastitih konstruktorskih i generičkih funkcija

7.2.1. Konstruktorske funkcije

Prisjetimo se priče o S3 objektima. Rekli smo da je S3 jedan prilično neformalan objektni

model, gdje programer zapravo sâm “slaže” vlastiti objekt punjenjem liste novim elementima

te pridjeljivanjem (proizvoljnoga) naziva klase. Pogledajmo ponovo primjer stvaranja objekta

klase osoba:

Primjer 20: Stvaranje novoga S3 objekta (ponavljanje)

# stvaramo novi objekt klase `osoba`

pero <- list(oib = "12345678", prezime = "Peric", tezina = 78)

class(pero) <- "Osoba"

Ovakav način stvaranja objekata vrlo je problematičan i podložan pogreškama. Puno bolji

način je stvaranje zasebne funkcije koja će “složiti” objekt na osnovu zadanih parametara.

Ovo je takozvani konstruktor ili konstruktorska funkcija. Sintaksno gledano,

konstruktorska funkcija je samo “obična” funkcija koja slaže objekt umjesto nas, no

prebacivanjem odgovornosti stvaranja objekta na zasebnu funkciju pružamo način

konzistentnoga, robusnoga stvaranja objekta, pogotovo ako u funkciju ubacujemo razne

provjere ispravnosti ulaznih podataka.

Page 52: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________46

Primjer 21: Konstruktorska funkcija

# konstruktor klase osoba

Osoba <- function(oib, prezime, tezina) {

stopifnot(is.character(oib))

stopifnot(is.character(prezime))

stopifnot(is.numeric(tezina) && tezina > 0)

o <- list(oib = oib, prezime = prezime, tezina = tezina)

class(o) <- "Osoba"

o

}

Pokušajmo sada u idućoj vježbi iskoristiti ovu funkciju za stvaranje novog objekta klase

Osoba.

Vježba 39: Stvaranje objekta uz konstruktorsku funkciju

# stvorite varijablu `ivo` koja će biti klase `Osoba`, a koja će imati

sljedeće vrijednosti atributa:

# OIB: 1357135713, prezime: Ivić, tezina: 76

# ispišite varijablu `ivo`

Uočimo da je kod ispisa našeg objekta R iskoristio funkciju print.default koja je naš

objekt ispisala kao listu (što on zapravo i jest). Ako želimo neki drugačiji, prilagođeni ispis,

moramo dodati novu generičku funkciju ciljano orijentiranu klasi Osoba. Ovo ćemo izvesti u

sljedećoj lekciji.

7.2.2. Vlastite generičke funkcije

U lekciji o generičkim funkcijama naučili smo da R metode objekata definira “izvan” objekata

u obliku generičkih funkcija koje onda prosljeđuju poziv “pravim” implementacijama. Također

smo naučili da je mehanizam koji ovo omogućuje vrlo jednostavan, tj. da je dovoljno

implementirati novu funkciju naziva genFun.nazKlase (uz pretpostavku da je naziv

generičke funkcije genFun). R će potom automatski pozivati dotičnu funkciju kod poziva

genFun(objekt), gdje je objekt klase nazKlase.

Pokušajmo sada iskoristiti naše znanje o stvaranju korisnički definiranih funkcija kako bismo

omogućili prilagođeni ispis objekata klase Osoba uz pomoć funkcije print.

Page 53: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________47

Vježba 40: Vlastita generička funkcija

# napišite novu funkciju koja će omogućiti ispis klase `Osoba`

# uz pomoć generičke funkcije `print`

# ispis mora izgledati ovako:

# OIB: <oib>, Prezime: <prezime>, tezina: <tezina>

# unutar tijela funkcije za slaganje ispisa koristite funkciju `paste`

# a za sam ispis funkciju `cat`

# ispišite varijablu `ivo` uz pomoć generičke funkcije `print`

Ovime zaokružujemo priču o korisnički definiranim funkcijama. U nastavku tečaja usredotočit

ćemo se na funkcije i pakete jezika R koji naš rad čine jednostavnijim i učinkovitijim te

omogućuju brz i ugodan rad s podatkovnim skupovima.

7.3. Dodatni zadaci za vježbu

1. Stvorite podatkovni okvir mjesto uz pomoć sljedeće naredbe:

mjesto <- data.frame( pbr = c(10000, 51000, 21000, 31000, 2000),

nazivMjesta = c("Zagreb", "Rijeka", "Split", "Osijek",

"Dubrovnik"),

prirez = c(18, 15, 10, 13, 10))

Dodajte ovom okviru stupac prirezOpis koji će biti kategorijska varijabla s razinama

"mali", "srednji" i "visok" ovisno o tome je li postotak prireza strogo manji od 12,

između 12 i 15 ili strogo veći od 15. Koristite se naredbom ifelse.

2. Zamijenite petlje u sljedećem bloku ekvivalentnim vektoriziranim operacijama (za drugu

petlju proučite dokumentaciju funkcije sum).

a <- numeric()

i <- 1

while (i <= 100) {

a <- c(a, i)

i <- i + 1

}

suma <- 0

for (i in a) {

if (i %% 2 == 0) suma <- suma + i*i

}

print(suma)

Page 54: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________48

3. Stvorite objekt klase Kvadar s atributima visina, sirina i dubina jednakim 10, 20 i 30.

4. Sljedeća naredba stvorit će vektor od 20 nasumično odabranih prirodnih brojeva od 1 do

100. Uz pomoć internetske tražilice i/ili službene dokumentacije pronađite ugrađene

funkcije koje izvršavaju zadane zadatke.

# učitavamo 20 prirodnih brojeva od 1 do 100, s ponavljanjem

set.seed(1234)

a <- sample(1:100, 20, replace = T)

Ispišite:

• vektor a

• vrijednosti vektora a poredane obrnutim redoslijedom

• jedinstvene vrijednosti iz vektora a

• vrijednosti vektora a poredane uzlazno.

5. R ima funkciju which koja pretvara logički vektor u numerički s rednim brojevima

elemenata koji su TRUE (tako c(T, F, F, F, F, T, F, T) postaje c(1, 6, 8)).

Implementirajte vlastitu inačicu ove funkcije.

6. Implementirajte konstruktor klase Zaposlenik koja nasljeđuje objekt klase Osoba sa

sljedećom konstruktorskom funkcijom i funkcijom ispisa:

Osoba <- function(oib, prezime, tezina) {

o <- list(oib = oib, prezime = prezime, tezina = tezina)

class(o) <- "Osoba"

o

}

print.Osoba <- function(o) {

rez <- paste("OIB:", o$oib, ", Prezime:", o$prezime, ", tezina:",

o$tezina, "\n")

cat(rez)

}

Zaposlenik uz atribute klase Osoba ima i atribut nadredjeni koji predstavlja referencu na

nadređenoga zaposlenika (ako postoji; inače je NULL).

Stvorite dva objekta klase Zaposlenik (jedan nadređen drugom) i ispišite ih uz pomoć

funkcije print. Potom implementirajte vlastitu inačicu generičke funkcije print za klasu

Zaposlenik koja ispisuje podatke o zaposleniku i podatke o nadređenom zaposleniku (ako

postoji; inače ispisuje poruku da nema nadređenoga zaposlenika). Ponovo ispišite oba

zaposlenika uz pomoć funkcije print.

Page 55: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________49

8. Deklarativne programske petlje

8.1. Porodica funkcija apply

8.1.1. Općenito o porodici funkcija apply

U prethodnoj lekciji naučili smo da su programske petlje u jeziku R relativno “nepoželjne”.

Glavni razlog tomu jest vektorizirana, deklarativna priroda jezika gdje uporabom operatora

i/ili pozivom funkcije vrlo često implicitno tražimo da se obradi više od jednog elementa. Ovo

je u suprotnosti s prirodom programskih petlji gdje često programskim naredbama prvo

eksplicitno objašnjavamo kako se “prošetati” po odabranoj podatkovnoj strukturi, da bismo

tek unutar petlje objasnili kako obraditi pojedini element te dali eventualne detalje za slaganje

rezultata.

Kod obrade podataka često nam nije dovoljna jedna programska petlja – na primjer,

zamislimo da želimo pohraniti zbrojeve redaka neke matrice. Ovo znači da moramo proći

kroz sve retke matrice te da za svaki redak moramo proći kroz sve njegove elemente kako

bismo ih sumirali. Ovo je često korišteni scenarij “petlje unutar petlje” gdje opet svu logiku

“šetanja” po podatkovnoj strukturi moramo eksplicitno opisati naredbama.

Programski jezik R za sve ove scenarije preferira deklarativni pristup. Ovo okvirno znači da

se u jeziku R potencira usredotočavanje na samu logiku obrade – što želimo postići – a ne

logiku koja opisuje kako prolaziti kroz strukturu. Ovo pogotovo ima smisla ako uočimo da je

logika prolaženja kroz strukturu zapravo relativno trivijalna – za jednodimenzionalne strukture

idemo element-po-element, za dvodimenzionalne po redcima ili stupcima unutar kojih opet

element-po-element itd. Drugi razlog je veća učinkovitost kôda – ako logiku prolaženja kroz

strukturu prepustimo ugrađenim funkcijama umjesto da je eksplicitno sami pišemo,

smanjujemo mogućnost pogreške i omogućujemo bolju optimizaciju (uz pretpostavku da

dizajneri programskoga jezika implementiraju određene optimizacijske rutine unutar

ugrađenih funkcija koje provode samo “petljanje”).

Upravo iz ovog razloga osmišljena je takozvana porodica funkcija apply. Ovo su funkcije

koje nam, uz samu vektoriziranu prirodu jezika, omogućuju da složenije kombinacije

programskih petlji također izrazimo deklarativno tj. da prepustimo niskorazinsku logiku

“petljanja” i slaganja rezultata ugrađenoj funkciji te da se usredotočimo na posao koji treba

učiniti nad svakim pojedinim elementom. Ovaj princip sukladan je već spomenutim principima

funkcionalnoga programiranja, što će pogotovo postati očito kada uočimo činjenicu da je

ključan ulazni parametar svake funkcije iz porodice apply referenca na neku drugu funkciju

koja obavlja traženi posao nad elementima.

Naziv porodice potječe od činjenice da (gotovo) sve funkcije iz nje imaju sufiks “apply”. Neki

članovi ove porodice su:

• apply

• lapply

• sapply

• vapply

• tapply, mapply, rapply itd.

Page 56: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________50

Sve ove funkcije rade na sličan način – kao ulazne argumente primaju skup podataka,

funkciju koju želimo primijeniti na elemente toga skupa te opcionalne dodatne parametre, a

kao izlaz daju skup rezultata funkcije, najčešće “upakirane” u prigodni format. Razlika se

uglavnom svodi na tipove ulaznih i izlaznih argumenata, te konkretne detalje oko provedbe

same funkcije i/ili pripreme rezultata.

Članove apply porodice najlakše je naučiti kroz primjere. Započet ćemo s funkcijom koja

dijeli ime s cijelom porodicom – funkcijom apply.

8.1.2. Funkcija apply

Kao što je rečeno, funkcija apply jedina je koja doslovno dijeli ime s porodicom ovih funkcija.

Namijenjena je radu s matricama (zapravo s poljima, ali s obzirom na to da se relativno

rijetko radi sa strukturama koje imaju više od dvije dimenzije, ovdje ćemo se usredotočiti

samo na matrice).

Sintaksa naredbe je sljedeća:

rezultat <- apply( <matrica>, <redovi (1) ili stupci (2)>, <funkcija> )

Ili, opisano riječima, za provođenje funkcije apply:

• odaberemo matricu

• odlučimo “šećemo” li se po redcima ili stupcima

• primjenjujemo odabranu funkciju na sve retke ili stupce jedan po jedan.

Ovisno o tome što funkcija radi, kao rezultat dobivamo matricu ili (što je češći slučaj) vektor.

Pokušajmo primijeniti ovu funkciju na konkretnom primjeru.

Vježba 41: Funkcija apply

m <- matrix(1:9, nrow = 3, ncol = 3, byrow = TRUE)

# ispišite matricu `m`

# uz pomoć funkcije `apply` izračunajte

# i ispišite zbrojeve stupaca matrice `m`

# (funkcija `sum`!)

# uz pomoć funkcije `apply` izračunajte

# i ispišite umnoške redaka matrice `m`

# (funkcija `prod`!)

Ako želimo nad redcima/stupcima provesti neki specifičan zadatak, za to vrlo često koristimo

anonimnu funkciju, na primjer:

Page 57: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________51

Primjer 22: Funkcija apply i anonimne funkcije

# izvlačimo prvi i zadnji element svakoga retka

apply(m, 1, function(x) c(x[1], x[length(x)]))

## [,1] [,2] [,3]

## [1,] 1 4 7

## [2,] 3 6 9

Pokušajmo ovo samostalno izvesti u sljedećoj vježbi.

Vježba 42: Funkcija apply i anonimne funkcije

# uz pomoć funkcije `apply` za svaki redak matrice `m`

# izračunajte prirodni logaritam sume elemenata

# zaokružen na 2 decimale (funkcija `round`!)

Ponovimo – funkcija apply (i srodne funkcije) implicitno rastavljaju ulaznu podatkovnu

strukturu na elemente. U primjerima gore ti elementi – retci ili stupci – zapravo su numerički

vektori. Argument x koji prima anonimna funkcija je upravo taj vektor ili, bolje rečeno svaki od

tih vektora koji joj se prosljeđuju jedan po jedan. Rezultati funkcije se pamte i “pakiraju” u

konačni rezultat.

Pokušajmo riješiti zadnju vježbu bez korištenja funkcije apply.

Vježba 43: Petlja kao alternativa funkciji apply

# uz pomoć programske petlje za svaki redak matrice `m`

# izračunajte prirodni logaritam sume elemenata

# zaokružen na 2 decimale (funkcija `round`!)

# rezultat pohranite ovdje:

rez <- numeric(nrow(m))

Ako usporedimo sintakse primjera s i bez korištenja funkcije apply, možemo se uvjeriti koliko

je sintaksa koja koristi apply zapravo “čišća” i preglednija. Ako koristimo petlje moramo

eksplicitno navesti logiku prolaženja strukturom i čuvanja međurezultata, što odvlači pažnju

od opisa posla koji zapravo želimo obaviti.

Što ako apply funkciji želimo proslijediti više parametara? Na primjer, recimo da umjesto

gornje funkcije koja izvlači prvi element retka želimo funkciju s dva parametra – prvi je vektor,

a drugi cijeli broj koji označava koji broj treba izvući. Odgovor je jednostavan – dodatne

parametre jednostavno navedemo na kraju poziva funkcije.

Page 58: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________52

Primjer 23: Funkcija apply i dodatni parametri pozivajuce funkcije

# apply funkcija i ulazna funkcija s više parametara

apply(m, 1, function(x,y) x[y], 2) # izvlačimo drugi element svakoga retka

## [1] 2 5 8

Ovaj primjer treba shvatiti samo kao demonstraciju prosljeđivanja dodatnih parametara

pozvanoj funkciji jer izvlačenje drugog elementa svakoga retka neke matrice zapravo traži

drugi stupac i u ovom slučaju u praksi se nećemo koristiti funkcijom apply već jednostavno

operatorom indeksiranja.

Konačno, treba napomenuti da za sličnu obradu podataka u matričnom obliku ne moramo

nužno koristiti apply – dosta popularnih operacija, kao što su zbrajanje elemenata redaka ili

stupaca, računanje prosjeka elemenata redaka i stupaca i sl. već je implementirano kroz

funkcije kao što su rowSums, colSums, rowMeans, colMeans i sl. One su jednostavnije za

uporabu, no specijalizirane – za dodatnu fleksibilnost najčešće je apply najpogodnija opcija.

8.1.3. Funkcija lapply

Ime funkcije lapply dolazi od riječi “list apply” – tj. “apply funkcija koja radi s listama”.

Jednostavno rečeno radi se o funkciji koja će kao ulazni argument primiti listu i neku funkciju,

primijeniti funkciju na svaki pojedini element liste i rezultat vratiti opet u obliku liste.

Vježba 44: Funkcija lapply

l <- list(a = 1:3, b = rep(c(T, F), 10), c = LETTERS)

# pomoću funkcije `lapply` izračunajte duljinu (broj elemenata)

# svakog elementa liste `l`

Isto kao kod funkcije apply, kod funkcije lapply često kao parametar koristimo anonimne

funkcije. Pokušajmo u sljedećoj vježbi napisati malo složeniju anonimnu funkciju za obradu

elemenata zadane liste.

Page 59: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________53

Vježba 45: Funkcija lapply i anonimne funkcije

# obradite elemente liste `l` na sljedeći način:

# - izračunajte srednju vrijednost ako se radi o numeričkom vektoru

# - prebrojite vrijednosti TRUE ako se radi o logičkom vektoru

# - ispišite duljinu vektora za sve ostale slučajeve

# konačni rezultati moraju biti pohranjeni u novu listu

# koristite funkciju `lapply` i anonimnu funkciju

# ne zaboravite da i anonimna funkcija može koristiti blokove!

Razmotrimo jednu bitnu činjenicu – funkcija lapply namijenjena je uporabi nad listama, a

podatkovni okviri su zapravo liste. Drugim riječima, funkcija lapply je vrlo zgodna za

obradu tabličnih podataka kada želimo određenu funkciju primijeniti na stupce podatkovnog

okvira.

Isprobajmo ovo u sljedećoj vježbi. Jedna od češćih operacija koje se provode kod analize

podataka jest takozvana “normalizacija” numeričkih stupaca podatkovnog okvira, tj. svođenje

svih numeričkih vrijednosti na “normalnu” distribuciju aritmetičke sredine 0 i standardne

devijacije 1. Ovo možemo uraditi tako da svaku pojedinu vrijednost umanjimo za aritmetičku

sredinu stupca (funkcija mean) te podijelimo sa standardnom devijacijom stupca (funkcija sd).

Ovo je odličan scenarij za demonstraciju korištenja funkcije lapply.

Vježba 46: Funkcija lapply i podatkovni okviri

df <- data.frame( a = 1:10, b = seq(100, 550, 50),

c = LETTERS[1:10], d = rep(c(T,F), 5),

e = -10:-1)

# normalizirajte numeričke stupce uz pomoć funkcije `lapply`

# ostale stupce nemojte mijenjati

# normalizirane vrijednosti zaokružite na tri decimale

# rezultat pohranite u varijablu df

# ispišite df

Jedna od stvari koja nas može zasmetati jest ta da smo nakon korištenja funkcije lapply

dobili listu – ovo je posljedica prirode ove funkcije, kod koje su i ulaz i izlaz uvijek lista. Nama

bi vjerojatno više odgovaralo da izlaz bude podatkovni okvir. Postoje dva načina da ovo vrlo

jednostavno omogućimo: prvi način jest da rezultat funkcije proslijedimo funkciji

as.data.frame, a drugi da varijabli postojećeg podatkovnog okvira kojoj pridružujemo

rezultat dodamo uglate zagrade (što R interpretira kao “stavi rezultat u stupce ovog

podatkovnog okvira”).

Page 60: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________54

Primjer 24: Pretvaranje rezultata funkcije lapply u podatkovni okvir

# prvi način

df <- as.data.frame(lapply(df, <funkcija>))

# drugi način

df[] <- lapply(df, <funkcija>)

8.1.4. Funkcija sapply

Funkcija lapply je u suštini dosta jednostavna za korištenje i baš zbog te činjenice vrlo

popularna. No već smo napomenuli činjenicu da ona uvijek kao rezultat vraća listu, iako bi

nam nekada više odgovarala neka druga podatkovna struktura, kao na primjer vektor. Jedna

stvar koju možemo pokušati izvesti nad rezultatom funkcije lapply jest primijeniti funkciju

unlist koja će “pojednostaviti” listu u obični vektor ako ona sadrži jednostavne, jednočlane

elemente.

Vježba 47: Funkcija unlist

l <- list(a = 1:10, b = 10:20, c = 100:200)

# izračunajte srednje vrijednosti elemenata liste `l`

# rezultate ispišite kao numerički vektor

# koristite lapply i unlist

Prikazana kombinacija lapply i unlist će nam kao rezultat dati jednodimenzionalni vektor,

što nam u velikom broju slučajeva odgovara. No, ponekad bi nam više odgovarala neka

druga podatkovna struktura, na primjer matrica. U ovom slučaju potreban nam je i dodatni

korak preoblikovanja jednodimenzionalnoga vektora u matricu uz pomoć funkcije matrix, pri

čemu moramo eksplicitno zadati broj redaka i stupaca.

Može se postaviti pitanje zašto funkcija lapply ne bi mogla “pogledati” rezultat koji je dobila i

sama odrediti optimalnu podatkovnu strukturu za oblikovanje rezultata (vektor, matrica ili

lista)? Upravo ta ideja stoji iza funkcije sapply ili “simplified list apply”. Ova funkcija prvo

interno obavlja lapply, a potom se rezultat pojednostavljuje na vektor, matricu ili polje,

ovisno o karakteristikama dobivenih rezultata.

Page 61: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________55

Vježba 48: Funkcija sapply

l <- list(a = 1:10, b = 10:20, c = 100:200)

# izračunajte medijane elemenata liste `l`

# i rezultate ispišite kao numerički vektor

# koristite funkciju `sapply`

# izvucite prvi i zadnji element svakog od elemenata liste `l`

# koristite `sapply` i anonimnu funkciju

Uočite da smo kao rezultat zadnjega primjera dobili matricu, ali da ju je R oblikovao “po

stupcima”. Ako bismo htjeli matricu s elementima poredanim po redcima, za to, nažalost, ne

možemo koristiti sapply jer se matrica formira interno, bez mogućnosti prosljeđivanja

parametra byrow = T. Za dobivanje takve matrice jedna opcija nam je već spomenuta

kombinacija funkcija lapply, unlist i matrix ili, što je jednostavnije, transponiranje

rezultata sapply uz pomoć funkcije t (od engl. transpose).

Funkcija sapply je prilično popularna, pogotovo za interaktivni rad. No, moramo napomenuti

njezinu ponešto nepredvidljivu prirodu – tip rezultata često nije unaprijed poznat, već se on

određuje na osnovi dobivenoga rezultata. Upravo zbog toga treba biti oprezan ako se ova

funkcija ugrađuje u programske skripte koje očekuju rezultat točno određenoga tipa. U tim

slučajevima preporučuje se pogledati funkciju vapply koja predstavlja “robusnu” inačicu

funkcije sapply, a zapravo kombinira funkciju sapply i deklaraciju “očekivanoga” rezultata,

tako da će skripta prestati s radom ako rezultat ne odgovara očekivanom.

Za kraj pokažimo jednu čestu korištenu sintagmu funkcije sapply u sprezi s podatkovnim

okvirima:

Primjer 25: Ispis klasa stupaca podatkovnog okvira

# podatkovni okvir `iris` je jedan od

# ugrađenih podatkovnih skupova

sapply(iris, class)

## Sepal.Length Sepal.Width Petal.Length Petal.Width Species

## "numeric" "numeric" "numeric" "numeric" "factor"

Iako smo ovu informaciju mogli dobiti i uz pomoć funkcije str, prikazani način je jedan od

najbržih za dobivanje uvida u klase stupaca podatkovnih okvira te ga R programeri često

koriste nakon učitavanja novoga podatkovnog skupa.

Page 62: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________56

8.1.5. Ostale funkcije iz porodice apply

U prethodnim poglavljima naveli smo vjerojatno najpopularnije članove porodice apply. Ova

porodica ima još članova, uključujući i neke koji nemaju sufiks -apply:

• mapply, koja primjenjuje funkcije paralelno nad više podatkovnih struktura

• vapply, već spomenuta “robusna” inačica sapply

• rapply, koja rekurzivno primjenjuje funkcije unutar strukture

• tapply, koja primjenjuje funkcije nad podskupovima unutar strukture definirane

faktorima

• Map, inačica mapply koja ne pojednostavljuje rezultat

• by, inačica tapply predviđena za podatkovne okvire

• itd.

Razlog zašto ove funkcije nećemo detaljno obrađivati jest dvojak: prvo, kao što je već

rečeno, ovi članovi porodice apply u praksi se primjenjuju puno rjeđe od funkcija apply,

lapply i sapply koje smo prikazali u prethodnim poglavljima; drugo, porastom popularnosti

jezika R pojavio se i veliki broj paketa orijentiranih upravo poboljšanju postojećih funkcija u

smislu lakšeg i učinkovitijeg programiranja, osobito u radu s podatkovnim okvirima. U

nastavku ćemo pogledati neke od ovih paketa i funkcija.

8.2. Alternativa funkcijama iz porodice apply

8.2.1. Paket purrr

Osnovna ideja paketa purrr jest definiranje skupa “alata” koji će R programerima omogućiti

praćenje principa funkcijskoga programiranja na konzistentan i pregledan način. Jedna od

očitih razlika između funkcija koje pruža ovaj paket i članova porodice apply jest ta što paket

purrr umjesto termina apply koristi termin map, što je sukladno terminu koji se koristi u

drugim programskim jezicima za obavljanje sličnoga posla (tj. gdje se primjena funkcije na

sve članove nekoga skupa često naziva “mapiranje”). Isto tako, paket purrr omogućuje

jasno definiranje tipa rezultata već kroz sam naziv funkcije, pa će tako funkcija map_lgl kao

rezultat dati logički vektor, a map_dfr podatkovni okvir.

Paket purrr nećemo detaljno obrađivati, no korisnicima koji imaju interes što više se

prilagoditi funkcijskoj programskoj paradigmi definitivno se preporučuje detaljnije

proučavanje dokumentacije ovoga paketa.

Page 63: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________57

8.2.2. Paket plyr

Paket plyr nastao je iz želje da se “poprave” određeni nedostaci porodice funkcija apply,

osobito vezani za nekonzistentne potpise funkcija, nazive parametara i tipove rezultata. Ovaj

paket također dodaje neke zgodne funkcionalnosti, kao što su bolje procesiranje grešaka,

podrška za paralelno procesiranje itd.

Slično funkcijama paketa purrr (koji je zapravo nastao kasnije), paket plyr definira funkcije

čiji naziv opisuje način djelovanja funkcije. Osnovica paketa plyr su takozvane XXply

funkcije gdje ply zapravo predstavlja skraćenicu od apply, a dva početna slova opisuju ulaz

i izlaz u funkciju, konkretno:

• d kao podatkovni okvir (engl. data frame)

• a kao matricu/polje (engl. array)

• l kao listu.

Tako, na primjer, imamo funkciju ldply koja kao ulaz prima listu te vraća podatkovni okvir, ili

alply koja kao ulaz prima matricu (ili polje) i vraća listu.

Paket plyr dugo je vremena bio jedan od najpopularnijih R paketa, zadržavajući mjesto u

top 5 najviše korištenih CRAN paketa iz godine u godinu. Jedini razlog relativnoga pada

njegove popularnosti u zadnjih nekoliko godina jest pojava njegovoga svojevrsnog

“nasljednika”, paketa dplyr, koji se strogo orijentirao obradi podataka unutar podatkovnih

okvira te uveo novi princip upravljanja okvirima donekle inspiriran jezikom SQL. Ovaj paket je

toliko dobro prihvaćen da ga veliki dio R programera smatra integralnim i nezaobilaznim

dijelom jezika R, tako da ćemo ga djelomično upoznati i u nastavku ovoga tečaja.

Page 64: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________58

9. Učinkovito programiranje i upravljanje

podatkovnim skupovima

9.1. Operator cjevovoda

9.1.1. Korištenje operatora cjevovoda

Pogledajmo sljedeći primjer: zamislimo da u jeziku R želimo stvoriti 100 nasumičnih realnih

varijabli u rasponu [0,100], zaokružiti ih na dvije decimale, iz ovog skupa odabrati uzorak od

10 varijabli, izračunati aritmetičku sredinu uzorka i ispisati ga na zaslon. Jedno od mogućih

programskih rješenja moglo bi biti sljedeće:

Primjer 26: Ulančano pozivanje funkcija

set.seed(1234) # (zbog ponovljivosti)

# rješenje gornjega primjera

rez <- runif(100, 0, 100) # 100 nasumičnih brojeva iz uniformne razdiobe od

0 do 100

rez <- round(rez,2)

rez <- sample(rez, 10)

rez <- mean(rez)

rez

## [1] 51.123

Ovakav kôd ima dosta nepotrebnoga ponavljanja – u svakom retku koristimo varijablu rez

koja čuva međurezultate i operator pridruživanja pomoću kojega pridružujemo nove rezultate

varijabli rez. Alternativno, mogli smo sve obaviti u jednom retku.

Vježba 49: Učahurene funkcije

set.seed(1234)

# ponovite gornji primjer, ali uz pomoć samo jednoga retka programskoga

kôda

Ovdje vidimo jedan tipičan primjer “kodnoga sendviča”, koji nije problem samo u R-u, već se

pojavljuje u većini programskih jezika – rezultat jedne funkcije postaje ulaz u drugu te ako

želimo sve obaviti bez eksplicitnog čuvanja međurezultata, kao rezultat ćemo dobiti

programski kôd koji je podložan greškama kod pisanja te je vrlo teško čitljiv.

Prirodan način interpretacije ovakvoga primjera bio bi “slijeva na desno”; kad obavimo jedan

posao, rezultat postaje ulaz u drugi posao i tako sve do završetka procesa. Ako bi postojao

način za predočavanje ovakve intuitivne interpretacije programskim kôdom, pojednostavili

bismo si ne samo pisanje kôda već bi takav kôd postao daleko čitljiviji i lakši za održavanje i

eventualnu naknadnu prilagodbu. Upravo ovo bila je motivacija za razvoj takozvanoga

“pipeline” operatora koji nudi paket magrittr.

Page 65: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________59

Paket čudnog imena zapravo je inspiriran imenom apstraktnoga slikara Renea Magrittea,

točnije njegovom slavnom slikom “La trahison des images” koja prikazuje lulu ispod koje su

riječi “Ceci n’est pas une pipe”. Cjevovodni, tj. “pipeline” operator izgleda ovako: %>%, a

programski kôd čini daleko čitljivijim te je postao izrazito omiljen u R zajednici, pogotovo kod

procedura koje uključuju intenzivno “ulančavanje” funkcija.

Kako radi %>%operator? Vrlo jednostavno – postavimo ga nakon poziva neke funkcije i iza

njega navedemo poziv druge funkcije u kojem mjesto rezultata prve naznačimo točkom. Ovo

možemo raditi koliko god puta želimo, tj. ovisno o tome koliko poziva “ulančavamo”.

h(g(f(x), y), z, w) # kôd bez %>% operatora

f(x) %>% g(., y) %>% h(., z, w) # kôd s %>% operatorom

Ako je rezultat prethodne funkcije na prvom mjestu sljedeće funkcije, onda se točka (štoviše,

cijeli taj argument) može izbaciti, tako da je sintaksa još kraća:

f(x) %>% g(y) %>% h(z,w) # kôd s %>% bez korištenja točke

Ako ne želimo koristiti točku, moramo samo voditi računa o tome da su pozivi funkcija u

lancu zapravo formalno nepravilni, jer imaju “nevidljivi” prvi argument. Usprkos tomu, mnogi

R programeri vole ovakvu sintaksu jer zahtijeva manje tipkanja i nešto je preglednija, a

spomenuta nepravilnost ne smeta dok god je programer upoznat s postojanjem “nevidljivog”

argumenta.

Probajmo sada preoblikovati naš prvi primjer uz pomoć %>% operatora.

Vježba 50: Operator %>%

set.seed(1234)

# ponovo riješite primjer s početka poglavlja, ali uz pomoć %>% operatora

Uočite kako čitanjem gornjega programskog kôda vrlo lagano interpretiramo smisao te linije

programskoga kôda, pogotovo u usporedbi s istom naredbom napisanom u obliku

“sendviča”.

Krajnji rezultat našega “lanca” funkcija možemo pohraniti uobičajenim načinom:

suma <- 1:10 %>% sum # rezultat se pohranjuje u varijablu 'suma'

a ako se želi zadržati laka interpretacija “slijeva nadesno”, može se koristiti i “obrnuti”

operator pridruživanja: ->.

1:10 %>% sum -> suma # radi istovjetno gornjem primjeru

Uočite da u situacijama kada je rezultat prethodne funkcije jedini parametar sljedeće,

zagrade možemo izbaciti u potpunosti (dakle, u gornjim primjerima sum, sum() ili sum(.) bi

svi radili jednako).

Pokušajmo sada kombinirati %>% operator i lapply.

Page 66: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________60

Vježba 51: Funkcija lapply i operator %>%

l <- list(a = 1:10, b = 10:20, c = 100:200)

# stvorite matricu koja će sadržavati prvi i zadnji element svakog elementa

liste

# elementi moraju biti poredani po redcima

# koristite funkcije lapply, unlist i matrix te %>% operator

# rezultat spremite u varijablu `rez`

# ispišite `rez`

%>% operator je posebno pogodan za upravljanje podatkovnim skupovima, osobito u

scenarijima kada imamo definiranu proceduru transformacije podataka (npr. filtriramo neke

retke, potom odaberemo stupce, zatim grupiramo podatke ovisno o nekoj kategorijskoj

varijabli). Uz pomoć ovog operatora dobivamo preglednu reprezentaciju našega procesa

prilagodbe podataka koju kasnije lako prilagođavamo i po potrebi proširujemo. Primjeri u

nastavku će često prema potrebi koristiti ovaj operator, te preporučujemo njegovo

svladavanje prije nastavka s lekcijama koje slijede.

9.1.2. Operator cjevovoda i drugi operatori

Operator cjevovoda vrlo je pogodan u sprezi s “klasičnim” funkcijama, no možemo naići na

problem kada ga želimo kombinirati s drugim operatorima. Uzrok problema jest sintaksa -

operator cjevovoda svoju učinkovitost postiže upravo nametanjem nove, “slijedne” sintakse,

koja nije kompatibilna sa sintaksom koju nameću drugi operatori, kao npr. +, %% ili [.

Ukoliko nam je zaista bitno da u našem programskom kodu imamo “neprekinuti” lanac poziva

funkcija koji će sadržavati ne samo funkcije, nego i druge operatore, onda je jedno od

rješenja koristiti operatore kao “obične” funkcije. Naime, svaki operator je zapravo funkcija

koja dijeli ime s operatorom (uz korištenje backtick navodnika kako bi se mogli koristiti

simbolima), tako da su sljedeći parovi izraza zapravo ekvivalentni:

Primjer 27: Operatori kao funkcije

# svaki par naredbi jest ekvivalentan 2 + 3 `+`(2, 3) 1 : 5 `:`(1, 5) x <- c(1, 2, 3) `<-`("x", c(1,2,3)) x[1] `[`(x, 1) ## [1] 5 ## [1] 5 ## [1] 1 2 3 4 5 ## [1] 1 2 3 4 5 ## [1] 1 ## [1] 1

Page 67: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________61

Pokušajmo ovaj princip iskoristiti u sljedećoj vježbi.

Vježba 52: Složenija uporaba operatora cjevovoda

set.seed(1234)

# "uredite" sljedeću naredbu uz pomoć operatora cjevovoda

matrix(table(sample(round(sqrt(sample(1:10000, 10000, replace = T))),

100))[1:9], 3, 3)

## [,1] [,2] [,3]

## [1,] 1 2 1

## [2,] 1 3 1

## [3,] 1 1 2

9.2. Učinkovito upravljanje podatkovnim skupovima

9.2.1. Paket dplyr

Paket dplyr jedan je od novijih paketa jezika R čija je glavna funkcija učinkovito i

pojednostavljeno upravljanje podatkovnim okvirima uz pomoć skupa intuitivno dizajniranih

funkcija. U trenutku pisanja ove lekcije i dalje se nalazi na vrhu liste najpopularnijih paketa

usprkos jakoj “konkurenciji” u ovom području koju čine iznimno popularni paketi kao što su

plyr, data.table, ali i osnovne, tradicionalne metode za rad s podatkovnim okvirima. Bilo

koja od ovih opcija predstavlja dobar izbor, a iako paket dplyr nije najučinkovitiji glede

samih performansi (daleko najbolje performanse trenutačno nudi paket data.table),

prednost ovoga paketa je njegova intuitivna, lako čitljiva sintaksa koja programski rad s

podatkovnim okvirima čini brzim i jednostavnim te je odličan izbor za programere koji se

upoznaju s R-om ili se jednostavno žele usredotočiti na rad s podacima kroz razumljiv,

pregledan i lako održiv programski kôd.

Strogo gledano, paket dplyr ima skromniju funkcionalnost od one koju nudi paket plyr. No

ova značajka nije nedostatak, već rezultat ciljanoga dizajna – funkcije paketa dplyr su

orijentirane potrebama korisnika, dizajnirane na način da pružaju podršku za onaj tip poslova

koji se najčešće koriste.

Konkretne prednosti koje donosi paket dplyr su sljedeće:

• jednostavna sintaksa (slična SQL-u, ali proceduralna) koja koristi pet glavnih “glagola” za

manipulaciju podatkovnim okvirima i kao takva definira svojevrsni samostalni jezik

unutar jezika R

• veća učinkovitost od metoda koje nudi osnovni paket; inicijalno je dplyr dizajniran za

veću učinkovitost programiranja, ne nužno za bolje performanse, no u međuvremenu

implementacija određenih rutina u C-u omogućila je poboljšanje i performansi izvođenja

• integracija s relacijskim bazama podataka; funkcije paketa dplyr mogu se koristiti

direktno nad tablicama u bazi uz pomoć automatskog prevođenja funkcija paketa dplyr

u SQL naredbe

Page 68: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________62

Spomenutih osnovnih “pet glagola” koje nudi paket dplyr su sljedeći:

• filter – za filtriranje podatkovnog skupa po redcima

• select – za odabir pojedinih stupaca

• arrange – za promjenu redoslijeda redaka

• mutate – za stvaranje novih stupaca iz postojećih

• summarise – za agregiranje podataka

Pored pet osnovnih glagola vrlo često koristimo i:

• group_by – za grupiranje podataka unutar podatkovnog skupa

• porodicu join – funkcija za spajanje podatkovnih okvira

Poznavatelji jezika SQL lako će uočiti paralele između tog jezika i navedenih funkcionalnosti

paketa dplyr. Najveća razlika jest u tome što SQL radi “deklarativno”, tj. moramo pratiti

pravila izgradnje SQL naredbe koja “sve radi odjednom”, dok u R-u uz pomoć funkcija paketa

dplyr i već ranije upoznatog operatora %>% radnje nad podatkovnim skupovima možemo

izvoditi proceduralno, s jasnim tijekom obrade podataka slijeva na desno.

Budući da su funkcije paketa dplyr uglavnom namijenjene upravljanju podatkovnim

skupovima, trebat će nam ogledni podatkovni skup – za to možemo odabrati skup mtcars

kojeg možemo učitati i kratko proučiti na sljedeći način.

Primjer 28: Ogledni skup mtcars

# učitavamo skup `mtcars` u globalnu okolinu

data(mtcars)

# struktura podatkovnoga skupa

str(mtcars)

# prvih nekoliko redaka

head(mtcars)

## 'data.frame': 32 obs. of 11 variables: ## $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ... ## $ cyl : num 6 6 4 6 8 6 8 4 4 6 ... ## $ disp: num 160 160 108 258 360 ... ## $ hp : num 110 110 93 110 175 105 245 62 95 123 ... ## $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ... ## $ wt : num 2.62 2.88 2.32 3.21 3.44 ... ## $ qsec: num 16.5 17 18.6 19.4 17 ... ## $ vs : num 0 0 1 1 0 1 0 1 1 1 ... ## $ am : num 1 1 1 0 0 0 0 0 0 0 ... ## $ gear: num 4 4 4 3 3 3 3 4 4 4 ... ## $ carb: num 4 4 1 1 2 1 4 2 2 4 ... ## mpg cyl disp hp drat wt qsec vs am gear carb ## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 ## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 ## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 ## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 ## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 ## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Page 69: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________63

Za dodatne informacije o ovom podatkovnom skupu možemo isprobati i naredbu ?mtcars

što će nam otvoriti stranicu dokumentacije o istom. Ukratko, radi se o podacima o 32

automobila iz 70-tih godina prošlog stoljeća, objavljenih u američkom časopisu Motor Trend.

Pored imena automobila, skup sadržava sljedeće atribute:

• mpg – potrošnja (u galonima po milji)

• cyl – broj cilindara

• disp – veličina motora

• hp – broj konjskih snaga

• drat – omjer stražnje osovine

• wt – težina (u tisućama funti)

• qsec – ubrzanje (do prelaska četvrtine milje)

• V/S – tip motora

• am – tip mjenjača

• gear – broj brzina

• carb – broj karburatora.

Prije nastavka napravit ćemo malu prilagodbu skupa mtcars. Budući da su imena

automobila spremljena kao imena redaka, moguće je da ista neće biti prisutna u

“uljepšanom” ispisu podatkovnih okvira koje koristi R Markdown. Zbog toga ćemo ista

spremiti u zasebni stupac, a potom ćemo prerasporediti stupce i obrisati imena redaka.

Primjer 29: Prilagodba skupa mtcars

# imena redaka stavite u zasebni stupac `carName`

mtcars$carName <- rownames(mtcars)

# preraspodijelite stupce - zadnji stupac na prvo mjesto

mtcars <- mtcars[, c(12, 1:11)]

# obrišite imena redaka

rownames(mtcars) <- NULL

Pogledajmo sada glavne funkcije koje nam nudi paket dplyr.

Page 70: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________64

9.2.2. Odabir redaka

U poglavlju o podatkovnim okvirima već smo naučili da se “rezanje” podatkovnih okvira može

raditi slično rezanju matrica – uz pomoć indeksnih vektora kojima definiramo koje

retke/stupce zadržavamo. Isto tako, naučili smo da indeksni vektori mogu biti cjelobrojni

(lokacijski), logički i imenski.

Kod definicije podskupa redaka daleko najčešći tip indeksnoga vektora je logički – uz pomoć

varijabli, tj. stupaca definiramo određeni kriterij koji “filtrira” retke. Nažalost, osnovna je R-ova

sintaksa korištenja logičkih indeksnih redaka za određivanje podskupa redaka ponešto

nespretna. Pretpostavimo da imamo podatkovni okvir df s dva stupca a i b. Klasična R-ova

sintaksa za odabir redaka (uz pomoć uvjetnog indeksiranja) izgleda ovako:

df[df$a > 5 & df$b != 3, ]

Prvi i očiti problem jest potreba ponavljanja imena podatkovnog okvira. Drugi problem jest

pitanje čitljivosti – gornju naredbu nije lako vizualno interpretirati, tj. naknadnim pregledom

kôda nije lako odmah uočiti da se radi o reduciranju broja redaka.

Paket dplyr nudi alternativnu funkciju filter koja eksplicitnom sintaksom odaje da se radi

o filtriranju redaka, a također omogućuje korištenje imena stupaca bez potrebe za

referenciranjem imena podatkovnog okvira:

filter(df, a > 5 & b != 3)

Isto tako, dobro je uočiti da je prvi argument funkcije sâm podatkovni skup, što nam

omogućuje jednostavno ulančavanje. Na ovom principu dizajnirana je većina funkcija paketa

dplyr.

Gore navedena funkcija predstavlja najčešći način odabira podskupa redaka, a poznavatelji

SQL-a uočiti će vrlo veliku sličnost s WHERE segmentom SQL upita. Pored funkcije filter, za

određivanje podskupa redaka imamo i sljedeće funkcije, također vrlo intuitivnih imena (radi

lakše interpretacije umjesto potpisa funkcija dajemo primjere parametara):

• distinct(df) – za uklanjanje duplikata

• slice(df, 1:10) – za lokacijsko indeksiranje

• sample_frac(df, 0.2) – nasumični odabir dijela skupa po danom omjeru

• sample_n(df, 50) – nasumični odabir zadanog broja redaka

• top_n(df, 10, a) – prvih 10 redaka, gledano po poretku stupca a

Također, za željeni raspored redaka u ispisu možemo koristiti:

• arrange(df, a, desc(b)) – poredaj po stupcu a uzlazno pa po b silazno

Isprobajmo ove funkcije u sljedećoj vježbi (zadaci se odnose na podatkovni skup mtcars).

Page 71: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________65

Vježba 53: Funkcije za odabir redaka

# učitajte paket `dplyr` (ako već nije učitan)

# ispišite podatke o svim autima koji troše manje od 15 galona po milji

# ispišite podatke o autima u prva tri retka (bez operatora indeksiranja!)

# ispišite podatke o tri nasumično odabrana automobila

# ispišite podatke o pet najtežih automobila

# ispis poredajte od najtežeg prema najlakšem

Uočite korištenje operatora cjevovoda u zadnjem rješenju. Većina funkcija paketa dplyr

dizajnirana je za ovakav pristup korištenja istih, kojeg ćemo se i mi držati u primjerima i

vježbama koji slijede.

Nadalje, uočimo nešto drugačiji ispis nakon uporabe funkcije slice. Ova funkcija dizajnirana

je na način da rezultat pretvara u takozvani tibble, što je klasa (iz istoimenog paketa) koja

nasljeđuje podatkovni okvir i dodaje mu neke funkcionalnosti koje olakšavaju rad s njima.

Najveća prednost ove klase jest “pametni” ispis, pogotovo u situacijama kada nehotice

zadamo R-u da ispiše podatkovni okvir s iznimno velikim brojem redaka. Kod regularnog

podatkovnog okvira ovo često rezultira smrzavanjem konzole, dok kod objekata klase

tibble postoji rutina koja presretne ovo ponašanje te ispiše samo manji dio redaka,

omogućujući ugodan daljnji rad. Ako želimo iskoristiti ove (i druge) pogodnosti ove klase,

možemo naše podatkovne okvire pretvoriti u tibble korištenjem funkcije as_tibble:

Primjer 30: Pretvorba podatkovnog okvira u tibble

# pretvaramo `mtcars` u `tibble`

mtcars <- as_tibble(mtcars)

9.2.3. Odabir stupaca

Druga metoda rezanja podatkovnog okvira jest odabir podskupa stupaca. Za razliku od

biranja podskupa redaka, gdje se najčešće služimo logičkim indeksiranjem tj. filtriranjem po

određenom kriteriju, stupce najčešće odabiremo po njihovomu imenu. Sintaksa odabira

podskupa stupaca po imenu uz pomoć osnovnoga načina indeksiranja u R-u izgleda, na

primjer, ovako (podatkovni okvir df sa stupcima a, b i c):

df[, c("a", "b", "c")]

Ovdje također uočavamo određenu nespretnost i teškoću interpretacije. Nazivi stupaca

moraju biti ugrađeni u funkciju stvaranja vektora što smanjuje čitljivost, a naredba nigdje

eksplicitno ne iskazuje da se radi o odabiru stupaca već da moramo zaključiti iz položaja

indeksnoga vektora. Dodatno, nema jednostavnoga načina za odabir raspona stupaca po

imenu, postojanju nekoga podniza ili uzorka unutar imena i sl.

Page 72: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________66

Funkcija select nam omogućuje eksplicitni odabir stupaca uz pomoć sintakse:

select(df, a, b, c)

Dakle, jednostavno navodimo podatkovni okvir i niz stupaca koje želimo odabrati. Ovdje je

također lako uočiti sličnost sa SQL-om, konkretno SELECT segmentom SQL upita.

Gornja sintaksa nije sve što ova funkcija nudi – select ima čitav niz pomoćnih funkcija i

operatora koji uvelike proširuju njezinu funkcionalnost, od kojih navodimo samo neke:

• select(podaci, a:c) – odaberi stupce od a do c

• select(podaci, -a, -b) – odaberi sve stupce osim a i b

• select(podaci, starts_with("PO"))) – odaberi stupce koji počinju sa slovima

"PO"

• select(podaci, contains("stup")) – odaberi stupce koji sadrže podniz "stup".

U sljedećoj vježbi isprobat ćemo funkciju select, pri čemu će neki zadaci kombinirati odabir

stupaca i odabir redaka. Svi zadaci također se odnose na skup mtcars.

NAPOMENA: Ako smo obavili pretvorbu podatkovnog okvira u tibble, onda će pokušaj

ispisa svih redaka uvijek rezultirati s ispisom samo 10 redaka s napomenom koliko redaka je

ostalo neispisano. Kao što smo već spominjali, ovo je ciljano svojstvo ove klase koje štiti

analitičara od “prevelikih” ispisa koji potencijalno mogu zamrznuti razvojno sučelje, no ako

zaista želimo ispisati više redaka od onog što vidimo na zaslonu, onda to možemo učiniti na

sljedeći način:

# pretpostavka: df je tibble, želim ispis 20 redaka

df %>% print(n = 20) # ili samo print(20)

Vježba 54: Odabir stupaca uz funkciju select

# za sve automobile iz skupa ispišite ime auta, potrošnju, broj cilindara i

težinu

# za aute sa 6 cilindara ispišite sve stupce od imena do broja konjskih

snaga

# za nasumično odabranih 10% automobila iz skupa ispišite

# sve atribute osim težine i broja brzina

# ispis poredajte po imenu automobila

# za aute u prvih 5 redaka ispišite sve stupce koji sadrže slovo `a`

Page 73: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________67

9.2.4. Agregacija i grupiranje

U literaturi koja se bavi analizom podataka često ćemo naići na takozvanu SAC paradigmu

(engl. Split-Apply-Combine). Radi se o strategiji koja se svodi na rastavljanje velikoga

zadatka na manje dijelove, obavljanje određenoga posla na svakom od dijelova te, konačno,

kombiniranje svih rezultata u jedinstvenu cjelinu. Porodica funkcija apply koju smo upoznali

primjer je ovakvoga pristupa.

U analizi podatkovnih skupova često primjenjujemo ovaj pristup: prvo podijelimo skup po

odabranim kategorijama, a onda nad svakim podskupom radimo neku obradu. Poznavatelji

SQL-a će ovaj princip lako prepoznati kao “grupiranje i agregaciju” koji se provode kroz GROUP

BY segment SQL upita uz prateće elemente u SELECT dijelu. Paket dplyr nudi vrlo sličnu

funkcionalnost (iako na proceduralni način) – prvo provedemo “grupiranje”, tj. stvaranje

podskupova redaka nekog okvira, a onda provodimo daljnje obrade paralelno nad svakim

podskupom, da bismo na kraju sve rezultate sakupili u jedinstveni podatkovni okvir.

Za grupiranje dplyr nudi funkciju group_by kojom tablicu (podatkovni okvir) pretvaramo u

“grupiranu tablicu” (grouped_tbl):

# grupiranje tablice

df <- group_by(df, a, b, c)

Isprobajmo ovu funkciju na našem okviru mtcars.

Vježba 55: Grupirana tablica

# stvorite novi podatkovni okvir imena `mtcarsCyl`

# koji će sadržavati retke iz tablice `mtcars`

# grupirane po cilindru

Grupiranjem nismo izgubili nikakvu informaciju – grupirani podatkovni okvir i dalje izgleda

identično kao i originalni “negrupirani” okvir. Grupiranje je samo dodatna informacija koja se

upisuje u okvir i koja naznačava da se neke daljnje operacije ne izvode nad cijelim okvirom,

već nad pojedinim grupama.

Najčešće operacije koje radimo nad grupiranim tablicama su takozvane “agregacijske”

operacije – operacije koje niz vrijednosti svode na jednu vrijednost uz pomoć odabrane

agregacijske funkcije, kao na primjer minimum, maksimum, suma, prosjek i sl.

Za agregaciju paket dplyr koristi funkciju summarise u sprezi s funkcijama (navodimo samo

neke):

• min, max – minimum i maksimum

• mean, median, sd – prosjek, medijan, standardna devijacija

• first, last, nth – prvi element, zadnji, n-ti

• n, n_distinct – broj redaka, broj jedinstvenih redaka

Sintaksa uporabe agregacijskih funkcija je na primjer

summarise(df, mean(a), sd(a))

Page 74: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________68

U ovom slučaju za podatkovni okvir df tražimo aritmetičku sredinu i standardnu devijaciju

elemenata stupca a. Ako se ne radi o grupiranoj tablici, dobit ćemo sredinu i devijaciju

cijeloga stupca; ako smo tablicu grupirali, onda će se ove funkcije izračunati zasebno za

svaki grupirani dio tablice.

Isprobajmo ovo na našim okvirima mtcars i mtcarsCyl.

Vježba 56: Funkcija summarise

# izračunajte minimalan, maksimalan i prosječan broj

# konjskih snaga za okvir `mtcars`

# izračunajte minimalan, maksimalan i prosječan broj

# konjskih snaga za okvir `mtcarsCyl`

Uočimo da kod grupirane tablice funkcija summarise automatski u ispis uključuje i stupce po

kojima smo grupirali.

U praksi najčešće grupiranu tablicu ne pohranjujemo u novu varijablu, već unutar jedne

naredbe provodimo cijeli proces odabira redaka i stupaca, grupiranja i provođenja

agregacije. Tako jedan složeniji izraz može izgledati ovako:

filter(df, a > 5) %>% select(a, b, c) %>% group_by(a,b) %>%

summarise(prosjekC = mean(c)) %>% arrange(desc(b))

NAPOMENA: Uočite dodavanje imena prosjekC agregiranom stupcu. Ovo je preporučeni

postupak kod provedbe agregacije, jer ovo ime novoga stupca možemo koristiti u daljnjim

funkcijama.

Spomenimo za kraj funkciju ungroup kojom možemo po želji ukloniti postojeća grupiranja –

ovo radimo kad god želimo da se naknadne operacije više ne rade “po grupama”, već na

razini cijeloga podatkovnog okvira. Naime, grupiranje neće samo utjecati na agregacijske

funkcije, već će se odraziti i na funkcije kao što su mutate (npr. rangiranje unutar grupa) ili

arrange, tj. promjena redoslijeda (uređivanje rasporeda redaka unutar grupa).

Kombinacijama ungroup i group_by lako uvodimo i ukidamo grupiranja prema želji.

Konačno, ponovimo još jednom lako uočljivu sličnost gornjeg izraza sa SQL upitima u

relacijskim bazama, uz bitnu razliku da ovdje operacije provodimo proceduralno što uvelike

povećava čitljivost te da vrlo lako u bilo kojem trenutku možemo pohraniti i provjeriti

međurezultat.

Pokušajmo sada napisati složeni upit koji koristi veći dio naučenih elemenata:

Page 75: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________69

Vježba 57: Funkcija summarise (2)

# iz skupa automobila `mtcars` odaberite samo aute s ručnim mjenjačem

# zadržite samo stupce s imenom, potrošnjom, brojem cilindara i težinom

# grupirajte n-torke po broju cilindara i ispišite broj n-torki u svakoj

grupi

# konačni rezultat poredajte po broju n-torki svake podgrupe

# NAPOMENA: agregacijska funkcija n() ne treba nikakve argumente!

Uočite da nam je u zadnjem rješenju select segment zapravo potpuno nepotreban, jer

kombinacija funkcija group_by i summarise ionako implicitno uklanja sve stupce koji nisu ni

dio grupiranja ni agregacijske funkcije.

Page 76: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________70

10. Zaključak

Za kraj ponovimo najvažnije stvari naučene na ovom tečaju:

1) R je domenski orijentirani jezik čije uspješno svladavanje zahtjeva kako učenje

njegovih programskih elemenata tako i razmišljanje o njihovoj primjeni unutar

konteksta analize podatkovnih skupova

2) R se zasniva na principu vektorizacije – vektor je osnovna podatkovna struktura, a

operacije se najčešće izvode na način da se provode nad svim elementima podatkovne

strukture odjednom

3) funkcijska programska paradigma prikladna je prirodi jezika R; u R-u se sve vrti oko

funkcija tako da je normalno referencirati funkcije kroz varijable, slati ih kao parametre u

druge funkcije ili primati ih kao rezultat

4) R preferira deklarativnu sintaksu gdje programer korištenjem odgovarajućih funkcija

na visokoj razini definira posao koji želi obaviti, bez niskorazinskih detalja koji opisuju

kako prolaziti nekom podatkovnom strukturom; zbog toga u jeziku R u pravilu treba

izbjegavati programske petlje (što ne znači da ih treba potpuno izbaciti, već samo

uvijek razmišljati o mogućoj alternativi)

5) funkcije porodice apply omogućuju nam da učinkovito zaobiđemo korištenje petlji;

paketi kao što su purrr i plyr dodatno olakšavaju rad pružajući kolekcije funkcija s

konzistentnijim, preglednijim sučeljima

6) operator cjevovoda %>% uvelike povećava preglednost programskoga kôda i

olakšava programiranje

7) funkcije paketa dplyr stvaraju svojevrsni “jezik unutar jezika” i omogućuju brz i

učinkovit rad nad podatkovnim skupovima

Što dalje? Ako ćemo jezik R koristiti za njegovu primarnu svrhu – analizu podatkovnih

skupova - onda se isplati proučiti pakete u kolekciji tidyverse. Ova kolekcija predstavlja

preporučeni skup podataka za “podatkovnu znanost” (engl. data science); ona na određeni

način nadograđuje jezik R i nudi “platformu unutar platforme”, jer su paketi dizajnirani

korištenjem iste filozofije dizajna i zajedničkih, dijeljenih sučelja. Uz pakete dplyr, magrittr.

tibble i purrr koje smo spominjali u ovoj lekciji, dio ovoga paketa jesu i paketi:

• ggplot2 – iznimno popularni paket za crtanje vizualizacija

• readr – paket za lako učitavanje podataka iz različitih izvora

• tidyr – paket za brzo preoblikovanje podatkovnih okvira, posebno pogodan za

popravljanje “neurednih”, loše strukturiranih podatkovnih skupova

• stringr – paket za upravljanje znakovnim nizovima

• lubridate – paket za upravljanje datumima i vremenskim oznakama

• DBI – paket za integraciju s relacijskim bazama podataka

• itd.

Učenje svakog od paketa iz kolekcije tidyverse bitno proširuje spektar mogućnosti jezika R,

pogotovo u segmentu pripreme podataka, što je često najzahtjevniji i najnaporniji dio

podatkovne znanosti. Odlična knjiga za ovaj segment je R for Data Science, autora Hadley-

Page 77: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________71

a Wickham-a (koji je autor velikoga dijela paketa kolekcije tidyverse), besplatno dostupna u

elektroničkom obliku na adresi http://r4ds.had.co.nz/.

Ako težimo produbljivanju znanja o samom programskom jeziku R, onda je preporuka

posvetiti više vremena učenju objektnih modela jezika R, detaljnijim karakteristikama

funkcijske programske paradigme i njezine primjene u jeziku R, metodama optimizacije,

integracije s rutinama u jeziku C++ i sl. Preporučena literatura je knjiga Advanced R, također

od Hadley-a Wickhama, isto tako besplatno dostupna u elektroničkom obliku na adresi

http://adv-r.had.co.nz/.

Konačno, ako težimo zvanju podatkovnoga znanstvenika i uz znanje R-a želimo naučiti što

više o metodologiji statističke i dubinske analize podataka te primjenama jednostavnih i

složenijih metoda strojnog učenja uz konkretne scenarije korištenja u jeziku R, preporučena

literatura može biti:

• OpenIntro statistics, autori David M. Diez, Christopher D. Barr i Mine Çetinkaya-

Rundel uvodna knjiga o statistici, dostupna na

https://www.openintro.org/stat/textbook.php, sadrži i vlastiti CRAN paket openintro s

primjerima

• Introduction to Statistical Learning, autori Gareth James, Daniela Witten, Trevor

Hastie i Robert Tibshirani, knjiga koja se bavi primjenom metoda strojnog učenja s

konkretnim primjerima, dostupna na http://www-bcf.usc.edu/~gareth/ISL/, također ima

svoj CRAN paket ISLR

Ne treba zaboraviti ni na već spomenute R podsjetnike koje nudi RStudio, dostupne na

poveznici https://www.rstudio.com/resources/cheatsheets/. Već samo proučavanje

podsjetnika može pružiti inspiraciju za daljnje učenje i odabir paketa, ovisno o željenoj

primjeni.

U svakom slučaju jezik R je puno više od “neobičnoga” programskog jezika, a dostupni

paketi proširenja (uz razvojno sučelje RStudio) doslovno redefiniraju R kao platformu za

podatkovnu znanost. Dobar R programer uložit će trud kako bi razumio sve elemente jezika,

kako one osnovne, tako i one koji dodatno oplemenjuju jezik i nude nove načine pristupa

rješavanju problema. Usvajanje znanja o tome zašto nešto radi na određeni način, te jasno

razumijevanje prednosti i mana određenih funkcionalnosti i pristupa dugoročno će dovesti do

ugodnog korištenja ovog programskog jezika za niz potencijalnih scenarija – od jednostavne

obrade manjeg podatkovnog skupa uz prigodne vizualizacije, do učinkovitog upravljanja

velikim količinama neurednih podataka uz razvoj i usporedbu kompleksnih deskriptivnih i

prediktivnih modela.

Page 78: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________72

10.1. Dodatni zadaci za vježbu

1. Uzmimo matricu m stvorenu sljedećom naredbom:

m <- rbind(1:5, seq(2, 10, 2), rep(3, 5), 3:7, seq(100, 500, 100))

Uz pomoć funkcije apply i nove anonimne funkcije stvorite vektor koji će sadržavati prvi

parni element svakoga retka, ili nulu ako pripadajući redak nema parnih elemenata.

2. Sljedeće naredbe stvorit će listu od 100 elemenata gdje će svaki element biti numerički

vektor nasumične duljine od 1 do 10.

set.seed(1234)

lista <- replicate(100, sample(1:10, sample(1:10, 1)))

Uz pomoć funkcija lapply ili sapply (i dodatnih naredbi ako je potrebno) stvorite:

• numerički vektor v s duljinama elemenata liste

• listu l s normaliziranim numeričkim vektorima originalne liste

• numerički vektor ind4 s indeksima svih elemenata liste koji sadrže broj 4

• podatkovni okvir df5 koji kao stupce sadrži sve elemente liste duljine 5

3. Inicijalizirajte generator slučajnih brojeva uz pomoć naredbe set.seed(1234). Potom

uz pomoć jedne naredbe i %>% operatora izvedite sljedeće:

• stvorite 100000 nasumičnih brojeva izvučenih iz normalne razdiobe s aritmetičkom

sredinom 10000 i standardnom devijacijom 1000

• zaokružite brojeve na prvi veći cijeli broj

• izbacite duplikate iz skupa

• poredajte skup po veličini

• slučajnim odabirom iz skupa izvucite 100 elemenata

• organizirajte tih 100 elemenata u matricu 10 × 10, složenu po redcima

• izračunajte sume redaka matrice

• ispišite prosjek suma redaka na zaslon.

U sljedećim zadacima za vježbu poslužiti ćemo se proširenim podatkovnim skupom

mammals sleep dostupnim u vizualizacijskom paketu ggplot2. Učitajte paket ggplot2 te

potom prenesite podatkovni okvir msleep u globalnu okolinu uz pomoć funkcije data.

Prije rješavanja učitajte podatkovni skup i upoznajte se s njim uz pomoć uobičajenih funkcija.

4. Za 10 biljojeda koji najdulje spavaju ispišite ime, koliko dnevno spavaju i tjelesnu težinu

u kilogramima. Ispis poredajte po duljini spavanja silazno.

5. Ispišite prosječno, najdulje i najkraće vrijeme spavanja životinja ovisno o njihovomu tipu

prehrane.

Page 79: Osnove programiranja u R-u - unizg.hr...rezultata na lak, brz i fleksibilan način. Dizajn osnovnih tipova podataka, programskih struktura i funkcija odražava ovu filozofiju. Postoje

Osnove programiranja u R-u (S760)

__________________________________________________________________________73