2 Osnovni tipovi podataka i operatori
“Osnovni” ili “primitivni” tipovi podataka su temeljni izgradbeni blokovi programskih jezika. Ovdje se najčešće misli na ugrađene mehanizme koji omogućuju pohrane elementarne informacije - najčešće logičkog, numeričkog ili znakovnog tipa. Većina programskih jezika koristi iste ili vrlo slične načine pohrane takvih informacija, što znači da implementira slične osnovne tipove podataka - razlika je često u detaljima kao što su odabrani nazivu tipa, nazivni broj bajtova i sl. U svakom slučaju najčešći prvi korak kod učenja novog programskog jezika jest upoznavanje osnovnih tipova podataka koje isti podržava.
Sljedeća stvar koja nas potom može zanimati jest sintaksa jezika, tj. način na kojeg pišemo naredbe koje interpreter jezika može razumjeti i izvršiti. Jezik R u svojoj sintaksi slijedi slične konvencije viđene u jezicima kao što su Python, Ruby ili Java, naravno uz određene specifičnosti (kao i svaki pojedini programski jezik). Neka osnovna sintaksna pravila su: svaka naredba u pravilu mora ići u svoj redak, ali uvlačenje naredbi nije bitno kao ni stavljanje točke-zareza na kraj naredbe; blokove definiramo vitičastim zagradama; tipove varijabli ne moramo definirati unaprijed već se oni samo prilagođavaju pridruženoj vrijednosti; komentari započinju znakom #
; i sl. Sintaksu ćemo najbolje naučiti kroz primjere - učenjem elemenata jezika sintaksna pravila često postaju intuitivno jasna. Najbolje je krenuti sa jednostavnim funkcijama i operatorima, što ćemo i učiniti u ovoj lekciji.
Lekciju ćemo završiti raspravom o tzv. “nedostajućim” ili “nepostojećim” vrijednostima. Budući da R ima svoj vlastiti način definiranja takve vrste vrijednosti, odmah ćemo pojasniti način na koji su one u R-u implementirane kako bi u lekcijama koje slijede bili pripremljeni za lako upravljanje takvim vrijednostima (koje se vrlo često susreću u radu sa stvarnim podatkovnim skupovima).
2.1 Osnovni tipovi podataka
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" |
bajtovi | raw | as.raw(2) , charToRaw("1") |
Neke opaske:
- cjelobrojni i realni tipovi se često zajedno tretiraju kao tip
numeric
(iako ovo nije u potpunosti konzistentno!) - kompleksni tip mora imati deklariranu imaginarnu konstantu čak i ako je ona 1 (
2 + i
nije dobar zapis!) - tip “sirovih” bajtova se relativno rijetko koristi
Provjeru da li je neka varijabla određenog tipa možemo raditi uz pomoć funkcije is.<naziv_tipa>
. Ovo ćemo isprobati u sljedećem zadatku. Prije nego što počnemo s rješavanjem uvedimo jedan novitet: u zadacima gdje ispisujemo više stvari na zaslon korisno je vizualno odvojiti različite segmente ispisa kako bismo lakše shvatili na koji dio koda se referenciraju. U tu svrhu ćemo koristiti naredbu cat("-----------\n")
koja jednostavno na zaslon ispisuje niz crtica i prelazi u novi red. Mogli smo koristiti i funkciju print()
, no ona uvijek započinje ispis sa indeksom elementa dok naredba cat
predstavlja “sirovi” ispis, što nam u ovom slučaju više odgovara.
Zadatak 2.1 - provjera tipova podataka
#isprobajte sljedeće naredbe:
#is.logical(FALSE)
#is.integer(2L)
#is.double(1.11)
# izvedite sljedeće provjere:
# da li je 5L numerički tip?
# da li je 3.14 numerički tip?
# da li je "ABC" znakovni tip?
# da li je 4 + 2i kompleksni tip?
# da li je 5 cjelobrojni tip?
is.logical(FALSE)
is.integer(2L)
is.double(1.11)
cat("-----------\n")
is.numeric(5L)
is.numeric(3.14)
is.character("ABC")
is.complex(4 + 2i)
is.integer(5)
## [1] TRUE
## [1] TRUE
## [1] TRUE
## -----------
## [1] TRUE
## [1] TRUE
## [1] TRUE
## [1] TRUE
## [1] FALSE
Da li ste uočili nešto neobično u ovim provjerama? Pokušajte objasniti dobiveni rezultat.
Tip neke varijable ili konstante možemo dohvatiti uz pomoć funkcija typeof
ili class
. Razlika između njih je sljedeća:
typeof
- dohvaća “primitivni” ili “osnovni” tip podatka (integer
,double
)class
- “objektni tip”, zapravo vrijednost atributaclass
Zadatak 2.2 - dohvat naziva tipa podatka
# ispišite tipove sljedećih konstanti: TRUE, 2L, F, 3.14, "ABC"
# ispišite klase istih konstanti. Ima li razlike?
typeof(TRUE)
typeof(2L)
typeof(F)
typeof(3.14)
typeof("ABC")
cat("-----------\n")
class(TRUE)
class(2L)
class(F)
class(3.14)
class("ABC")
## [1] "logical"
## [1] "integer"
## [1] "logical"
## [1] "double"
## [1] "character"
## -----------
## [1] "logical"
## [1] "integer"
## [1] "logical"
## [1] "numeric"
## [1] "character"
Podatke možemo eksplicitno pretvarati iz jednog tipa u drugi uz pomoć funkcije as.<naziv_tipa>
:
Zadatak 2.3 - pretvorba tipova podataka
# Izvedite sljedeće pretvorbe i ispišite rezultat
# 2.35 u integer
# TRUE u numeric
# 100L u character
# 2.35 u character
# 2e2 u character
# 0 u logical
# 2.75 u logical
as.integer(2.35)
as.numeric(TRUE)
as.character(100L)
as.character(2.35)
as.character(2e2)
as.logical(0)
as.logical(2.75)
## [1] 2
## [1] 1
## [1] "100"
## [1] "2.35"
## [1] "200"
## [1] FALSE
## [1] TRUE
R će sam provoditi implicitnu pretvorbu ukoliko je moguća:
Zadatak 2.4 - implicitna pretvorba
# napišite izraze koji odgovaraju sljedećem i ispišite rezultat:
# aritmetički operator između logičke i numeričke varijable
# aritmetički operator između cjelobrojne i numeričke varijable
# logički operator negacije primjenjen na numeričku varijablu
# aritmetički operator između logičke i numeričke varijable
TRUE + 5
# aritmetički operator između cjelobrojne i numeričke varijable
5L + 3.14
# logički operator negacije primjenjen na numeričku varijablu
!25
## [1] 6
## [1] 8.14
## [1] FALSE
Implicitna pretvorba će se izvesti samo ako je smislen - npr. aritmetički operator između znakovne i numeričke varijable rezultirati će greškom.
2.2 Operatori
Kao i u drugim programskim jezicima, R dozvoljava korištenje operatora u izrazima. Neki od češće korištenih operatora su:
- aritmetički
+
,-
,*
,/
,**
(potencija),%%
(modulo),%/%
(cjelobrojno dijeljenje) - usporedni
<
,<=
,>
,>=
,==
,!=
- logički
!
(negacija),&&
(skalarni “i”),||
(skalarni “ili”),&
(vektorski “i”),|
(vektorski “ili”) - pridruživanje
<-
ili=
Zadatak 2.5 - operatori
# isprobajte izraze `5 / 2` i `5 %/% 2`
# provjerite koliko iznosi "17 na kvadrat" i "ostatak dijeljenja 101 sa 12"
# provjerite što je rezultat sljedećih izraza: `17 > 13`, `!TRUE`, `5 && 0`, `0. || 2`
# isprobajte izraze `5 / 2` i `5 %/% 2`
5 / 2
5 %/% 2
cat("-----------\n")
# provjerite koliko iznosi "17 na kvadrat" i "ostatak dijeljenja 101 sa 12"
17 ^ 2
101 %% 12
cat("-----------\n")
# provjerite što je rezultat sljedećih izraza: `17 > 13`, `!TRUE`, `5 && 0`, `0. || 2`,
17 > 13
!TRUE
5 && 0
0. || 2
## [1] 2.5
## [1] 2
## -----------
## [1] 289
## [1] 5
## -----------
## [1] TRUE
## [1] FALSE
## [1] FALSE
## [1] TRUE
Logičke vrijednosti i usporedne operatore najčešće ćemo koristiti kod tzv. “uvjetnog izvođenja naredbi”, poznatog iz drugih programskih jezika kao “IF ELSE” naredba. U R-u njezina sintaksa izgleda ovako:
if (izraz) {blok} else {blok}
Isprobajmo ovu naredbu na sljedećem zadatku:
Zadatak 2.6 - uvjetno izvođenje naredbi
## [1] "Uspjeh!"
Uočili smo gore da imamo dvije vrste logičkih operatora za “i” i “ili”. Razliku ćemo objasniti kasnije, za sada je dovoljno reći da se kod uvjetnog izvođenja naredbi ili programskih petlji gotovo isključivo koristimo operatorima &&
i ||
(“C++ - ovski” operatori!).
Isto tako, već smo spomenuli da R nudi dva operatora pridruživanja, <-
i =
. Između njih postoje neke sitne razlike, no one nemaju gotovo nikakav utjecaj na uobičajeno korištenje ovog operatora u praksi. U literaturi se za pridruživanje vrijednosti novim varijablama može vidjeti i jedna i druga inačica, no mi ćemo u nastavku primarno i konzistentno koristiti <-
, ponajviše zato kako bi programski kod bio vizualno distinktivniji od drugih programskih jezika.
NAPOMENA: za jednostavniji upis operatora <-
možemo se koristiti kombinacijom tipaka ALT
i -
Kod pridruživanja pazimo da je s lijeve strane tzv. “lijeva vrijednost” (engl. lvalue). Ovo u programerskom smislu interpretiramo kao “nešto u što se može pohraniti izračunata vrijednost”.
U pravilu se u R-u kao lvalue koristi varijabla, iako se tu ponekad može pojaviti i poziv funkcije. Ovu možda inicijalno zbunjujuću pojavu razjasniti ćemo kasnije.
Imenovanje varijabli uglavnom slijedi pravila iz drugih programskih jezika - dozvoljena su slova, brojke, podcrta ali i točka . Prvi simbol mora biti slovo ili točka.
.mojaVarijabla <- 5 #OK
moja.Varijabla <- 5 #OK
_mojaVarijabla <- 5 # nije OK
123Varijabla <- 5 # nije OK
U praksi za varijable složenih imena trebamo odabrati jednu od sljedećih konvencija:
Bitno je da u programskom kodu ne miješamo konvencije tj. da nakon odabira budemo konzistentni.
Ukoliko baš inzistiramo na čudnim imenima koja koriste specijalne znakove, onda ih moramo staviti pod tzv. “lijeve jednostruke apostrofe” (engl. backticks):
Zadatak 2.7 - ime varijable sa specijalnim znakovima
# upišite proizvoljno ime sa specijalnim znakovima unutar lijevih apostrofa
# i ispišite vrijednost varijable
#`` <- 2
## [1] 2
Ovakav način imenovanja varijabli nije previše koristan u praksi, ali ima i svoju svrhu - budući da su operatori u R-u zapravo funkcije (čija su imena doslovno +
, **
i sl.) upotrebom lijevih apostrofa možemo ih direktno referencirati u njihovom originalnom obliku, što se može pokazati vrlo praktičnim kod tzv. funkcijskog programiranja (o čemu ćemo govoriti u jednoj od budućih lekcija).
Pridjeljivanje vrijednosti novim nazivima varijabli mi zapravo stvaramo nove varijable u radnoj okolini (koja se u R-u naziva “globalna okolina”). Sve varijable koje smo do sada stvorili možemo vidjeti uz pomoć funkcije ls()
. Ukoliko želimo obrisati neke varijable, samo navedemo njihova imena u pozivu funkcije rm()
(npr. rm(x, y, z)
). Za brisanje svih varijabli iz radne okoline koristimo poziv rm(list=ls())
, s time što tu moramo biti oprezni (nema “undo”!).
Zadatak 2.8 - ispis i brisanje varijabli globalne okoline
# ispišite sve do sada stvorene varijable globalne okoline
# obrišite neke od gore ispisanih varijabli - npr. rm(x, y, z)
# ponovo ispišite dostupne varijable
# obrišite SVE varijable globalne okoline
# (oprezno s ovim pozivom u praksi!)
# uvjerite se da je globalna okolina prazna
# ispišite sve do sada stvorene varijable globalne okoline
ls()
# obrišite neke od upravo ispisanih varijabli - npr. rm(x, y, z)
# ponovo ispišite dostupne varijable
rm(x, y)
ls()
# obrišite SVE varijable globalne okoline
# (oprezno s ovim pozivom u praksi!)
# uvjerite se da je globalna okolina prazna
rm(list=ls())
ls()
Konačno, kad god nam treba pomoć oko neke funkcije, imamo sljedeće opcije na raspolaganju:
- napišemo samo
<ime_funkcije>
(bez zagrada sa parametrima) i stisnemo- ukoliko je funkcija pisana u R-u (a nije samo proxy prema implementaciji u C-u) na zaslon ćemo dobiti ispis izvornog koda funkcije - napišemo
help(<ime_funkcije>)
ili?<ime_funkcije>
čime dobijamo stranicu pomoći o funkciji sa popisom parametara, primjerima i sl. - napišemo
example(<ime_funkcije>)
pri čemu dobijemo popis primjera korištenja funkcije i dobivenih rezultata
Sljedeći isječak koda prikazuje način korištenja gornjih metoda (zbog štednje prostora ne prikazujemo njihov rezultat).
#programski kod funkcije `ls`
ls
# pomoć za funkciju `ls`
?ls # ili help(ls)
# primjeri korištenja funkcije `ls`
example(ls)
2.3 Nedostajuće, nepoznate i nemoguće vrijednosti
U R-u postoji tri načina modeliranja “nepostojećih” vrijednosti:
NA
- (not available) nedostajuća ili nepoznata vrijednost određenog tipaNaN
- (not a number) “nemogući” broj, npr.0/0
NULL
- nepostojeća vrijednost, doslovno “ništa”
Zadatak 2.9 - rad sa NA, NaN i NULL
# Koliko je "5 + nepoznati broj"?
# Koliko je "5 + nepostojeći broj"?
# provjerite klase sljedećih konstanti i izraza:
# NA
# aritmetička operacija između numeric i NA
# NaN
# NULL
# Koliko je "5 + nepoznati broj"?
5 + NaN
# Koliko je "5 + nepostojeći broj"?
5 + NA
cat("-----------\n")
# provjerite klase sljedećih konstanti i izraza i objasnite rezultat:
# NA
# aritmetička operacija između numeric i NA
# NaN
# NULL
class(NA) # logički tip je "najslabiji"!
class(5 + NA)
class(NaN)
class(NULL)
## [1] NaN
## [1] NA
## -----------
## [1] "logical"
## [1] "numeric"
## [1] "numeric"
## [1] "NULL"
Provjeru nedostajućih vrijednosti radimo slično provjeri tipova podataka - koristimo funkcije is.na
, is.nan
i is.null
. Moramo voditi računa da je NaN
zapravo podvrsta od NA
te da je NULL
zapravo potpuno zasebna klasa sa specifičnim ponašanjem - pokušaj aritmetičkih ili logičkih operacija nad NULL
vrijednosti neće rezultirati “novom” nepostojećom vrijednosti već upozorenjima i “praznim” rezultatima. Ovo je posebno bitno napomenuti poznavateljima jezika SQL - ono što je NULL
u SQL-u je NA
u R-u i to je ono što u pravilu koristimo u praksi, dok NULL
ima vrlo specifične primjene te ga puno rjeđe koristimo u programskom kodu.
Zadatak 2.10 - provjera vrijednosti NA, NaN i NULL
# Što je od idućeg NA? NA, NaN, NULL, "", 0
# Što je od idućeg NaN? NA, NaN, NULL
# Što je od idućeg NULL? NA, NaN, NULL
# Što je od idućeg NA? NA, NaN, NULL, "", 0
is.na(NA)
is.na(NaN)
is.na(NULL)
is.na("")
is.na(0)
cat("-----------\n")
# Što je od idućeg NaN? NA, NaN, NULL
is.nan(NA)
is.nan(NaN)
is.nan(NULL)
cat("-----------\n")
# Što je od idućeg NULL? NA, NaN, NULL
is.null(NA)
is.null(NaN)
is.null(NULL)
## [1] TRUE
## [1] TRUE
## logical(0)
## [1] FALSE
## [1] FALSE
## -----------
## [1] FALSE
## [1] TRUE
## logical(0)
## -----------
## [1] FALSE
## [1] FALSE
## [1] TRUE
Za kraj posvetimo se malo NA
vrijednosti, budući da ćemo ju vrlo često susretati u praksi. Pojednostavljeno rečeno, ukoliko se pojavljuju NA
vrijednosti, možemo očekivati sljedeće nuspojave:
- rezultati aritmetičkih izraza rezultiraju sa
NA
vrijednosti - rezultati poziva nekih funkcija rezultiraju sa
NA
(osim ako ne navedemo kompenzacijske akcije, kao npr. parametarna.rm = T
koji zapravo znači “ignorirajNA
”) - rezultati logičkih izraza mogu ali ne moraju rezultirati sa
NA
vrijednosti ovisno o tom da li izraz ovisi oNA
ili ne (npr.TRUE || NA
ima rezultatTRUE
, aliFALSE || NA
ima rezultatNA
)
S ovim zadnjim moramo biti posebno oprezni, budući da NA
u uvjetnom izrazu rezultira greškom:
U ovoj lekciji upoznali smo se sa osnovnim elementima jezika R. U radu s R-om u pravilu radimo sa složenim tipovima podataka koje ćemo upoznati u nastavku - a to su vektori, matrice, podatkovni okviri i liste.
Zadaci za vježbu
- Što je rezultat sljedećih naredbi? Razmislite o mogućem rezultatu prije izvršavanja.
as.complex(2)
as.integer(-3.25)
as.logical("0")
as.numeric(TRUE)
as.character(11.5+2i)
as.numeric("ABC")
- Kako u R-u izgledaju sljedeći opisni izrazi:
- “tri puta deset na devetu”
- “logaritam od 5”
- “cjelobrojno dijeljenje 10 sa 3”
- “ostatak cjelobrojnog dijeljenja 10 sa 3”
- “tangens od 75 stupnjeva” |
Uz pomoć
if
izraza provjerite da li se rezultat dijeljenja nule s nulom smatra kao vrijednostNA
,NaN
iliNULL
.Ubacite u varijablu
x
vrijednost5
. Ispišite sve varijable okoline. Potom u varijablux
ubaciteNULL
. Postoji li i dalje ova varijabla?
Programirajmo u R-u by Damir Pintar is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
Based on a work at https://ratnip.github.io/FER_OPJR/