11 Prilagodba podatkovnih skupova uz 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 nalazi se 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 sa podatkovnim okvirima. Bilo koja od ovih opcija predstavlja dobar izbor, a iako paket dplyr
nije najučinkovitiji glede samih performansi (daleko najbolje performanse trenutno nudi paket data.table
), prednost ovog paketa je njegova intuitivna, čitljiva sintaksa koja programski rad sa podatkovnim okvirima čini brzim i jednostavnim te je odličan izbor za programere koji se upoznaju sa R-om ili se jednostavno žele usredotočiti na rad sa podacima kroz čitljiv, lako održiv programski kod.
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 i poboljšanje i performansi izvođenja) - integracija sa relacijskim bazama podataka (funkcije paketa
dplyr
mogu se koristiti direktno nad tablicama u bazi uz pomoć automatskog prevođenja funkcija paketadplyr
u SQL naredbe)
Spomenutih osnovnih “pet glagola” koje nudi paket dplyr
su sljedeći:
filter
- za filtriranje podatkovnog skupa po retcimaselect
- za odabir pojedinih stupacaarrange
- za promjenu redoslijeda redakamutate
- za stvaranje novih stupaca iz postojećihsummarise
- 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.
Prije detaljnog pregleda funkcionalnosti paketa dplyr
pogledajmo ogledni podatkovni skup kojeg ćemo koristiti tijekom ove lekcije.
11.1 Ogledni podatkovni skup: Titanic
Odaberimo za početak jedan često korišteni podatkovni skup – “Titanic Passenger Survival Dataset”. Ovaj skup pruža informacije o sudbinama putnika prekoocenaskog putničkog broda Titanic koji je potonuo 14. travnja 1912. godine pri čemu je od 2223 putnika i članova posade preživjelo samo njih 706. Podatkovni skup između ostalog sadrži imena putnika, spol, godište u trenutku potonuća, putničku klasu i sl. Postoji inačica ovog podatkovnog skupa koja dolazi sa samom R distribucijom, no mi ćemo koristiti njegovu proširenu inačicu sa Kaggle natjecanja “Titanic: Machine Learning From Disaster” o kojem se više detalja može saznati na ovoj poveznici.
Učitajmo ovaj podatkovni skup uz pomoć funkcije read_csv
, funkcije paketa readr
koja “nadograđuje” funkciju read.csv
. Isto tako, umjesto funkcije str
, pokušajmo koristiti njezin ekvivalent glimpse
kojeg nudi paket dplyr
.
Zadatak 11.1 - podatkovni skup Titanic
# učitajte podatkovni skup iz datoteke `Titanic.csv` u varijablu `titanic` uz pomoć funkcije `read_csv` iz paketa `readr`
# prije korištenja funkcije proučite dokumentaciju iste
#pogledajte strukturu podatkovnog okvira `titanic` uz pomoć funkcije `glimpse`
#i prvih nekoliko redaka uz pomoć funkcije `head`
## Parsed with column specification:
## cols(
## PassengerId = col_integer(),
## Survived = col_integer(),
## Pclass = col_integer(),
## Name = col_character(),
## Sex = col_character(),
## Age = col_double(),
## SibSp = col_integer(),
## Parch = col_integer(),
## Ticket = col_character(),
## Fare = col_double(),
## Cabin = col_character(),
## Embarked = col_character()
## )
## Observations: 891
## Variables: 12
## $ PassengerId <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,...
## $ Survived <int> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0,...
## $ Pclass <int> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3,...
## $ Name <chr> "Braund, Mr. Owen Harris", "Cumings, Mrs. John Bra...
## $ Sex <chr> "male", "female", "female", "female", "male", "mal...
## $ Age <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, ...
## $ SibSp <int> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4,...
## $ Parch <int> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1,...
## $ Ticket <chr> "A/5 21171", "PC 17599", "STON/O2. 3101282", "1138...
## $ Fare <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, ...
## $ Cabin <chr> NA, "C85", NA, "C123", NA, NA, "E46", NA, NA, NA, ...
## $ Embarked <chr> "S", "C", "S", "S", "S", "Q", "S", "S", "S", "C", ...
## # A tibble: 6 x 12
## PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare
## <int> <int> <int> <chr> <chr> <dbl> <int> <int> <chr> <dbl>
## 1 1 0 3 Brau~ male 22 1 0 A/5 2~ 7.25
## 2 2 1 1 Cumi~ fema~ 38 1 0 PC 17~ 71.3
## 3 3 1 3 Heik~ fema~ 26 0 0 STON/~ 7.92
## 4 4 1 1 Futr~ fema~ 35 1 0 113803 53.1
## 5 5 0 3 Alle~ male 35 0 0 373450 8.05
## 6 6 0 3 Mora~ male NA 0 0 330877 8.46
## # ... with 2 more variables: Cabin <chr>, Embarked <chr>
Zašto koristiti funkciju read_csv
umjesto funkcije sličnog imena read.csv
? Postoji nekoliko razloga:
- veća autonomija, tj. kvalitetnije ugrađene vrijednosti nazivnih parametara
- veća brzina
- ispis statusnih informacija radi lakšeg uočavanja grešaka
- nema automatske kategorizacije
- učitani objekt se automatski pretvara u “tibble”
Svi ovi razlozi su jasni, osim možda zadnjeg. Što je “tibble”? To je kolokvijalni naziv za objekt klase tbl_df
, koja predstavlja svojevrsnu nadogradnju klase data.frame
, tj. osnovnog podatkovnog okvira. Najveća prednost ovog tipa objekta jest što će se kod pokušaja ispisa cijelog okvira na zaslon (što se najčešće događa omaškom i što rezultira “smrzavanjem” R konzole) pozvati posebna funkcija koja će ispisati samo dio okvira i moći će se odmah nastaviti s radom.
Ako želimo neki postojeći podatkovni okvir “nadograditi” u tibble, to možemo učiniti uz pomoć naredbe as_tibble
(uočite kako se funkcije iz kolekcije tidyverse
često oslanjaju na podcrtu tj. “underscore” kako bi ih lakše identificirali kao nadogradnje osnovnih funkcija koje imaju slične nazive ali koriste točku).
Uz pomoć funkcije options
i parametara tibble.print_max
i tibble.width
možemo kontrolirati koliko u općenitom slučaju želimo ispisati stupaca i redaka u općenitom slučaju npr:
# zelim u ispisu vidjeti najvise 10 redaka ali sve stupce
options(tibble.print_max = 10, tibble.width = Inf)
a kod pojedinog ispisa broj redaka možemo birati uz pomoć parametra n
:
Prije nastavka bilo bi dobro pobliže se upoznati sa podatkovnim skupom kojeg ćemo koristiti, bilo nešto detaljnijim istraživanjem podatkovnog skupa, bilo prikupljanjem dokumentacije o istom. Kraći opis samog podatkovnog skupa preuzet sa službenih stranica Keggle natjecanja slijedi:
VARIABLE DESCRIPTIONS:
survival Survival
(0 = No; 1 = Yes)
pclass Passenger Class
(1 = 1st; 2 = 2nd; 3 = 3rd)
name Name
sex Sex
age Age
sibsp Number of Siblings/Spouses Aboard
parch Number of Parents/Children Aboard
ticket Ticket Number
fare Passenger Fare
cabin Cabin
embarked Port of Embarkation
(C = Cherbourg; Q = Queenstown; S = Southampton)
SPECIAL NOTES:
Pclass is a proxy for socio-economic status (SES)
1st ~ Upper; 2nd ~ Middle; 3rd ~ Lower
Age is in Years; Fractional if Age less than One (1)
If the Age is Estimated, it is in the form xx.5
With respect to the family relation variables (i.e. sibsp and parch)
some relations were ignored. The following are the definitions used
for sibsp and parch.
Sibling: Brother, Sister, Stepbrother, or Stepsister of Passenger Aboard Titanic
Spouse: Husband or Wife of Passenger Aboard Titanic (Mistresses and Fiances Ignored)
Parent: Mother or Father of Passenger Aboard Titanic
Child: Son, Daughter, Stepson, or Stepdaughter of Passenger Aboard Titanic
Other family relatives excluded from this study include cousins,
nephews/nieces, aunts/uncles, and in-laws. Some children travelled
only with a nanny, therefore parch=0 for them. As well, some
travelled with very close friends or neighbors in a village, however,
the definitions do not support such relations.
U ovom trenutku bi trebalo razmotriti uključuje li podatkovni skup neke kategorijske podatke. Kao što smo naučili, za razliku od funkcije read.csv
koja automatski faktorizira sve znakovne stupce (što nije preporučljivo), funkcija read_csv
iz paketa readr
ne faktorizira ništa, već to ostavlja na odgovornost analitičaru. Iako ovo predstavlja dodatni posao za analitičara, razina kontrole i robusnosti koja se time dobiva je više nego dostatni kompromis.
U podatkovnom skupu titanic
uočavamo sljedeće kategorijske varijable:
- Survival (preživljavanje – 2 kategorije: 0 i 1)
- Pclass (putnička klasa – 3 kategorije: 1, 2 i 3)
- Sex (spol – 2 kategorije: “M” i “F”)
- Embarked (luka ukrcaja – 3 kategorije: “C”, “Q” i “S”)
Faktorizirajmo navedene stupce.
Zadatak 11.2 - Kategorizacija stupaca podatkovnoga skupa Titanic
# pretvorite stupce `Survival`, `Pclass`, `Sex` i `Embarked`
# podatkovnog okvira `titanic` u faktore
# proučite podatkovni okvir `titanic`
# uz pomoć funkcije `glimpse`
# pretvorite stupce `Survival`, `Pclass`, `Sex` i `Embarked`
# podatkovnog okvira `titanic` u faktore
titanic$Survived <- as.factor(titanic$Survived)
titanic$Pclass <- as.factor(titanic$Pclass)
titanic$Sex <- as.factor(titanic$Sex)
titanic$Embarked <- as.factor(titanic$Embarked)
# proučite podatkovni okvir `titanic`
# uz pomoć funkcije `glimpse`
glimpse(titanic)
## Observations: 891
## Variables: 12
## $ PassengerId <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,...
## $ Survived <fct> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0,...
## $ Pclass <fct> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3,...
## $ Name <chr> "Braund, Mr. Owen Harris", "Cumings, Mrs. John Bra...
## $ Sex <fct> male, female, female, female, male, male, male, ma...
## $ Age <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, ...
## $ SibSp <int> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4,...
## $ Parch <int> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1,...
## $ Ticket <chr> "A/5 21171", "PC 17599", "STON/O2. 3101282", "1138...
## $ Fare <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, ...
## $ Cabin <chr> NA, "C85", NA, "C123", NA, NA, "E46", NA, NA, NA, ...
## $ Embarked <fct> S, C, S, S, S, Q, S, S, S, C, S, S, S, S, S, S, Q,...
Postoji i sažetija alternativa za faktoriziranje stupaca koja koristi lapply
:
categories <- c("Survived", "Pclass", "Sex", "Embarked")
titanic[categories] <- lapply(titanic[categories], as.factor)
Sada kada dobro razumijemo naš podatkovni skup i kada smo obavili inicijalne pripreme u smislu kategorizacije stupaca, možemo krenuti sa upoznavanjem funkcija paketa dplyr
.
11.2 Stvaranje podskupa obzervacija uz filter
i slice
U poglavlju o podatkovnim okvirima smo već 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 indeksnog vektora je logički - uz pomoć varijabli tj. stupaca definiramo određeni kriterij koji “filtrira” retke. Nažalost, osnovna R-ova sintaksa korištenja logičkih indeksnih redaka za određivanje podskupa redaka je ponešto nespretna, što se može vidjeti iz primjera:
Prvi i očiti problem jest potreba ponavljanja imena podatkovnog okvira (koju možemo eliminirati uz pomoć funkcije attach
, što smo rekli da nije idealno rješenje jer donosi niz novih potencijalnih problema). Drugo pitanje jest problem čitljivosti - gornju naredbu nije lako vizualno interpretirati, tj. naknadnim pregledom koda nije lako odmah uočiti da se radi o reduciranju broja redaka.
Funkcija filter
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:
Isto tako, dobro je uočiti da je prvi argument funkcije sam 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 (poznavatelji SQL-a uočiti će sličnost sa 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(podaci)
- za uklanjanje duplikataslice(podaci, 1:10)
- za lokacijsko indeksiranjesample_frac(podaci, 0.2)
- nasumični odabir dijela skupa po danom omjerusample_n(podaci, 50)
- nasumični odabir zadanog broja redakatop_n(podaci, 10, a)
- prvih n redaka, gledano po poretku stupca a
Za poredak redaka u ispisu možemo koristiti:
arrange(podaci, a, desc(b))
- poredaj po stupcua
uzlazno pa pob
silazno
Isprobajmo ovo na primjerima:
Zadatak 11.3 - odabir redaka
# ispišite podatke o svim putnicima prve klase starijim od 60 godina
# ispišite podatke o svim preživjelim muškim putnicima koji
# u imenu imaju `George` ili `Frank`
# provjerite ima li u podatkovnom skupu duplih obzervacija
# nasumično izaberite i ispišite podatke o pet putnika koji nisu preživjeli potonuće
# ispis poredati silazno po cijeni karte
# ispišite podatke o pet najstarijih putnica prve klase
# ispišite podatke o svim putnicima prve klase starijim od 60 godina
filter(titanic, Pclass == 1 & Age > 60)
## # A tibble: 14 x 12
## PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare
## <int> <fct> <fct> <chr> <fct> <dbl> <int> <int> <chr> <dbl>
## 1 55 0 1 Ostb~ male 65 0 1 113509 62.0
## 2 97 0 1 Gold~ male 71 0 0 PC 17~ 34.7
## 3 171 0 1 Van ~ male 61 0 0 111240 33.5
## 4 253 0 1 Stea~ male 62 0 0 113514 26.6
## 5 276 1 1 Andr~ fema~ 63 1 0 13502 78.0
## 6 439 0 1 Fort~ male 64 1 4 19950 263
## 7 457 0 1 Mill~ male 65 0 0 13509 26.6
## 8 494 0 1 Arta~ male 71 0 0 PC 17~ 49.5
## 9 546 0 1 Nich~ male 64 0 0 693 26
## 10 556 0 1 Wrig~ male 62 0 0 113807 26.6
## 11 626 0 1 Sutt~ male 61 0 0 36963 32.3
## 12 631 1 1 Bark~ male 80 0 0 27042 30
## 13 746 0 1 Cros~ male 70 1 1 WE/P ~ 71
## 14 830 1 1 Ston~ fema~ 62 0 0 113572 80
## # ... with 2 more variables: Cabin <chr>, Embarked <fct>
# ispišite podatke o svim preživjelim muškim putnicima koji
# u imenu imaju `George` ili `Frank`
filter(titanic, str_detect(Name, "(George|Frank)") & Sex == "male" & Survived == 1)
## # A tibble: 6 x 12
## PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare
## <int> <fct> <fct> <chr> <fct> <dbl> <int> <int> <chr> <dbl>
## 1 166 1 3 "Gol~ male 9 0 2 363291 20.5
## 2 371 1 1 Hard~ male 25 1 0 11765 55.4
## 3 508 1 1 "Bra~ male NA 0 0 111427 26.6
## 4 571 1 2 Harr~ male 62 0 0 S.W./~ 10.5
## 5 710 1 3 "Mou~ male NA 1 1 2661 15.2
## 6 832 1 2 Rich~ male 0.83 1 1 29106 18.8
## # ... with 2 more variables: Cabin <chr>, Embarked <fct>
## [1] TRUE
# nasumično izaberite i ispišite podatke o pet putnika koji nisu preživjeli potonuće
# ispis poredati silazno po cijeni karte
filter(titanic, Survived == 0) %>% sample_n(5) %>% arrange(desc(Fare))
## # A tibble: 5 x 12
## PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare
## <int> <fct> <fct> <chr> <fct> <dbl> <int> <int> <chr> <dbl>
## 1 496 0 3 Yous~ male NA 0 0 2627 14.5
## 2 464 0 2 Mill~ male 48 0 0 234360 13
## 3 869 0 3 van ~ male NA 0 0 345777 9.5
## 4 92 0 3 Andr~ male 20 0 0 347466 7.85
## 5 61 0 3 Sira~ male 22 0 0 2669 7.23
## # ... with 2 more variables: Cabin <chr>, Embarked <fct>
# ispišite podatke o pet najstarijih putnica prve klase
filter(titanic, Pclass == 1 & Sex == "female") %>% top_n(5, Age) %>% arrange(Age)
## # A tibble: 6 x 12
## PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare
## <int> <fct> <fct> <chr> <fct> <dbl> <int> <int> <chr> <dbl>
## 1 12 1 1 Bonn~ fema~ 58 0 0 113783 26.6
## 2 196 1 1 Lure~ fema~ 58 0 0 PC 17~ 147.
## 3 269 1 1 Grah~ fema~ 58 0 1 PC 17~ 153.
## 4 367 1 1 Warr~ fema~ 60 1 0 110813 75.2
## 5 830 1 1 Ston~ fema~ 62 0 0 113572 80
## 6 276 1 1 Andr~ fema~ 63 1 0 13502 78.0
## # ... with 2 more variables: Cabin <chr>, Embarked <fct>
Uočite da “prvih n” redaka samo odabire podskup redaka, neće ga nužno ispisati po redu. Ukoliko želimo poredani ispis moramo koristiti i funkciju arrange
.
11.3 Stvaranje podskupa varijabli uz select
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 tj. varijable najčešće referenciramo po njihovom imenu. Sintaksa odabira podskupa stupaca po imenu uz pomoć osnovnog načina indeksiranja u R-u izgleda na primjer ovako:
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ć do moramo zaključiti iz položaja indeksnog vektora. Dodatno, nema jednostavnog načina za odabir raspona stupaca po imenu, postojanju nekog podniza ili uzorka unutar imena i sl.
Funkcija select
nam omogućuje eksplicitni odabir stupaca uz pomoć sintakse:
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.
No gornja sintaksa nije sve što ova funkcija nudi - select
ima čitav niz pomoćnih funkcija i operatora koji uvelike proširuju njezinu funkcionalnost, kao npr:
select(podaci, a:c)
- odaberi stupce oda
doc
select(podaci, -a, -b)
- odaberi sve stupce osima
ib
select(podaci, starts_with("PO")))
- odaberi stupce koji počinju sa slovima"PO"
select(podaci, contains("stup"))
- odaberi stupce koji sadrže podniz"stup"
select(podaci, matches("[123]{2,3}"))
- odaberi stupce koji odgovaraju danom regularnom izrazu
Ovo nisu sve mogućnosti, no dodatne opcije lako je pronaći u službenoj dokumentaciji.
Isprobajmo ovu naredbu, također na podatkovnom skupu Titanic.
Zadatak 11.4 - funkcija select
# za nasumično odabranih 10 redaka ispišite ime putnika, dob te
# da li je preživio potonuće ili ne
# za prvih 10 najstarijih putnika ispišite sve atribute od imena do cijene karte
# za nasumično odabranih 1% redaka ispišite sve atribute osim identifikatora
# putnika i broja kabine
# za retke od broja 10 do broja 20 ispišite sve stupce koji počinju samoglasnikom
# za nasumičnih 10 putnika koji imaju nepoznat broj godina ispišite sve
# atribute od imena do cijene karte,
# potom putničku klasu te konačno da li je putnik preživio ili ne
# poredajte ispis abecedno po imenu
# za nasumično odabranih 10 redaka ispišite ime putnika, dob te
# da li je preživio potonuće ili ne
sample_n(titanic, 10) %>% select(Name, Age, Survived)
## # A tibble: 10 x 3
## Name Age Survived
## <chr> <dbl> <fct>
## 1 Panula, Mrs. Juha (Maria Emilia Ojala) 41 0
## 2 Taussig, Miss. Ruth 18 1
## 3 Sirayanian, Mr. Orsen 22 0
## 4 Lemore, Mrs. (Amelia Milley) 34 1
## 5 Hamalainen, Master. Viljo 0.67 1
## 6 Sandstrom, Miss. Marguerite Rut 4 1
## 7 Baclini, Miss. Marie Catherine 5 1
## 8 Stead, Mr. William Thomas 62 0
## 9 Penasco y Castellana, Mrs. Victor de Satode (Maria Josef~ 17 1
## 10 Hassan, Mr. Houssein G N 11 0
# za prvih 10 najstarijih putnika ispišite sve atribute od imena do cijene karte
top_n(titanic, 10, Age) %>% select(Name:Fare)
## # A tibble: 11 x 7
## Name Sex Age SibSp Parch Ticket Fare
## <chr> <fct> <dbl> <int> <int> <chr> <dbl>
## 1 Wheadon, Mr. Edward H male 66 0 0 C.A. 245~ 10.5
## 2 Ostby, Mr. Engelhart Cornelius male 65 0 1 113509 62.0
## 3 Goldschmidt, Mr. George B male 71 0 0 PC 17754 34.7
## 4 Connors, Mr. Patrick male 70.5 0 0 370369 7.75
## 5 Duane, Mr. Frank male 65 0 0 336439 7.75
## 6 Millet, Mr. Francis Davis male 65 0 0 13509 26.6
## 7 Artagaveytia, Mr. Ramon male 71 0 0 PC 17609 49.5
## 8 Barkworth, Mr. Algernon Henry W~ male 80 0 0 27042 30
## 9 Mitchell, Mr. Henry Michael male 70 0 0 C.A. 245~ 10.5
## 10 Crosby, Capt. Edward Gifford male 70 1 1 WE/P 5735 71
## 11 Svensson, Mr. Johan male 74 0 0 347060 7.78
# za nasumično odabranih 1% redaka ispišite sve atribute osim identifikatora
# putnika i broja kabine
sample_frac(titanic, 0.01) %>% select(-PassengerId, -Cabin)
## # A tibble: 9 x 10
## Survived Pclass Name Sex Age SibSp Parch Ticket Fare Embarked
## <fct> <fct> <chr> <fct> <dbl> <int> <int> <chr> <dbl> <fct>
## 1 0 3 Thorneycro~ male NA 1 0 376564 16.1 S
## 2 1 3 Nilsson, M~ fema~ 26 0 0 347470 7.85 S
## 3 1 2 Faunthorpe~ fema~ 29 1 0 2926 26 S
## 4 1 3 Jansson, M~ male 21 0 0 350034 7.80 S
## 5 0 2 Eitemiller~ male 23 0 0 29751 13 S
## 6 0 3 Sdycoff, M~ male NA 0 0 349222 7.90 S
## 7 1 2 Davies, Ma~ male 8 1 1 C.A. ~ 36.8 S
## 8 0 3 Reed, Mr. ~ male NA 0 0 362316 7.25 S
## 9 1 2 Leitch, Mi~ fema~ NA 0 0 248727 33 S
# za retke od broja 10 do broja 20 ispišite sve stupce koji počinju samoglasnikom
slice(titanic, 10:20) %>% select(matches("^[AEIOUaeiou]"))
## # A tibble: 11 x 2
## Age Embarked
## <dbl> <fct>
## 1 14 C
## 2 4 S
## 3 58 S
## 4 20 S
## 5 39 S
## 6 14 S
## 7 55 S
## 8 2 Q
## 9 NA S
## 10 31 S
## 11 NA C
# za nasumičnih 10 putnika koji imaju nepoznat broj godina ispišite sve
# atribute od imena do cijene karte,
# potom putničku klasu te konačno da li je putnik preživio ili ne
# poredajte ispis abecedno po imenu
filter(titanic, is.na(Age)) %>% sample_n(10) %>%
select(Name:Fare, Pclass, Survived) %>% arrange(Name)
## # A tibble: 10 x 9
## Name Sex Age SibSp Parch Ticket Fare Pclass Survived
## <chr> <fct> <dbl> <int> <int> <chr> <dbl> <fct> <fct>
## 1 Boulos, Mr. Hanna male NA 0 0 2664 7.22 3 0
## 2 Klaber, Mr. Herman male NA 0 0 113028 26.6 1 0
## 3 Lennon, Mr. Denis male NA 1 0 370371 15.5 3 0
## 4 "O'Dwyer, Miss. E~ female NA 0 0 330959 7.88 3 1
## 5 Peter, Miss. Anna female NA 1 1 2668 22.4 3 1
## 6 Peters, Miss. Kat~ female NA 0 0 330935 8.14 3 0
## 7 Plotcharsky, Mr. ~ male NA 0 0 349227 7.90 3 0
## 8 Razi, Mr. Raihed male NA 0 0 2629 7.23 3 0
## 9 Sage, Mr. Frederi~ male NA 8 2 CA. 2~ 69.6 3 0
## 10 Zabour, Miss. Tha~ female NA 1 0 2665 14.5 3 0
11.4 Stvaranje novih stupaca uz mutate
U radu sa podatkovnim skupovima često se pojavi potreba za stvaranjem dodatnih varijabli uz pomoć informacija pohranjenih u jednoj ili više postojećih varijabli. Novi stupac najčešće stvaramo uz pomoć nekog izraza koji opisuje na koji način transformiramo postojeće podatke; motivacija može biti normalizacija numeričke varijable, stvaranje indikatorske ili kategorijske varijable, sumiranje više varijabli u jednu jedinstvenu varijablu ili bilo koja druga transformacija s ciljem dobivanja nove varijable koja je na neki način potrebna za daljnji proces analize.
Ako pretpostavimo stvaranje novog stupca koji će pohraniti zbroj numeričkih vrijednosti postojećih stupaca, onda bi uz pomoć osnovnih funkcija R-a to proveli na sljedeći način:
Paket dplyr
nam nudi alternativu u obliku funkcija mutate
i transmute
:
Razlika: mutate
vraća cijeli originalni podatkovni okvir uz novostvorene stupce, dok transmute
zadržava samo one koje smo naveli unutar poziva funkcije. Zbog toga transmute
možemo koristiti kao skraćenu kombinaciju mutate
i select
, npr:
(NAPOMENA: Ovdje se ne moramo bojati degradacije performansi zbog kopiranja okvira, budući da R kod dodavanja stupca radi “plitku kopiju” (shallow copy), tj. novi okvir samo referencira novi stupac dok dijeli reference na preostale stupce sa “originalnim” okvirom. “Duboka kopija” (deep copy) bi značilo da se svi stupci kopiraju u potpuno novi okvir, u kojem slučaju bi ova operacija bila resursno “skupa”)
Uočite da mutate
i transmute
nisu alternative naredbi UPDATE iz SQL-a, već zapravo odgovaraju SELECT segmentu naredbe u scenarijima kada ne biramo pojedine stupce već ih kombiniramo u sklopu izraza.
Funkcije mutate
i transmute
koriste uobičajene (vektorizirane) funkcije i operatore, a imamo na raspolaganju i niz dodatnih tzv. “prozorskih” (window) funkcija koje nam omogućuju dodatnu fleksibilnost kod stvaranja novih varijabli, kao npr:
ntile
,cut
- stvaranje kategorijske varijable koja grupira vrijednosti un
ladica;ntile
će napraviti kategorije jednake veličine, dok ćecut
rezati u ovisnosti o veličini intervala numeričkih vrijednostidense_rank
,min_rank
- rangiranje obzervacija (razlika je samo u tretiranju obzervacija sa istim vrijednostima)between
- opisuje da li je varijabla nekog stupca u intervalu zadanom sa dva druga stupcapmin
,pmax
- “paralelni minimum i maksimum”, tj. minimum ili maksimum vrijednosti odabranih stupaca gledano po retcima- itd.
Popis svih dostupnih funkcija može se pronaći u dokumentaciji.
Zadatak 11.5 - funkcija mutate
# tablici `titanic` dodajte logički stupac `hadRelativesOnBoard` koji će opisivati
# da li je putnik imao rodbine na brodu
# za nasumičnih 10 putnica starijih od 20 godina koje su se ukrcale u Southamptonu
# ispišite ime putnice, putničku klasu i prilagođenu cijenu karte
# cijenu karte zamijenite odgovarajućom cijenom koliko bi ona iznosila danas
# (pretpostavite da je $1 iz 1912. jednak današnjih $23.85, uračunavajući inflaciju)
# novi stupac nazovite `FareToday` a
# iznos zaokružite na dvije decimale i dodajte prefiks `$`
# ispis poredajte po putničkoj klasi silazno
# stvorite stupac FareCategory kojim ćete podijeliti cijene karata
# u pet kategorija jednake veličine
# potom nasumično izaberite 20 putnika i ispišite
# ime putnika, putničku klasu, cijenu karte i kategoriju
# ispis poredajte po kategoriji cijene
# tablici `titanic` dodajte stupac `EmbarkationPort` u kojem će pisati puno
# ime luke ukrcaja (Southampton, Queenstown ili Cherbourg)
# koristite `mutate` i dva `ifelse`
# ispišite prvih 10 redaka tablice `titanic`
# za nasumičnih 10 putnica starijih od 20 godina koje su se ukrcale u Southamptonu
# ispišite ime putnice, putničku klasu i prilagođenu cijenu karte
# cijenu karte zamijenite odgovarajućom cijenom koliko bi ona iznosila danas
# (pretpostavite da je $1 iz 1912. jednak današnjih $23.85, uračunavajući inflaciju)
# novi stupac nazovite `FareToday` a iznos zaokružite na dvije decimale i dodajte prefiks `$`
# ispis poredajte po putničkoj klasi silazno
filter(titanic, Sex == 'female', Age > 20, Embarked == 'S') %>% sample_n(10) %>%
transmute(Name, Pclass, FareToday = str_c("$", round(Fare * 23.85, 2))) %>% arrange(desc(Pclass))
## # A tibble: 10 x 3
## Name Pclass FareToday
## <chr> <fct> <chr>
## 1 "Ford, Miss. Doolina Margaret \"Daisy\"" 3 $819.84
## 2 Sandstrom, Mrs. Hjalmar (Agnes Charlotta Bengtsson) 3 $398.3
## 3 Panula, Mrs. Juha (Maria Emilia Ojala) 3 $946.55
## 4 Jussila, Miss. Mari Aina 3 $234.33
## 5 Stanley, Miss. Amy Zillah Elsie 3 $180.07
## 6 Honkanen, Miss. Eliina 3 $189.01
## 7 Nilsson, Miss. Helmina Josefina 3 $187.32
## 8 Davis, Miss. Mary 2 $310.05
## 9 Leader, Dr. Alice (Farnham) 1 $618.41
## 10 Carter, Mrs. William Ernest (Lucile Polk) 1 $2862
# stvorite stupac FareCategory kojim ćete podijeliti cijene karata
# u pet kategorija jednake veličine
# potom nasumično izaberite 20 putnika i ispišite
# ime putnika, putničku klasu, cijenu karte i kategoriju
# ispis poredajte po kategoriji cijene
mutate(titanic, FareCategory = ntile(Fare, 5)) %>% sample_n(20) %>%
select(Name, Pclass, Fare, FareCategory) %>% arrange(FareCategory)
## # A tibble: 20 x 4
## Name Pclass Fare FareCategory
## <chr> <fct> <dbl> <int>
## 1 Charters, Mr. David 3 7.73 1
## 2 Connolly, Miss. Kate 3 7.75 1
## 3 Connaghton, Mr. Michael 3 7.75 1
## 4 Youseff, Mr. Gerious 3 7.22 1
## 5 Lemore, Mrs. (Amelia Milley) 2 10.5 2
## 6 Lievens, Mr. Rene Aime 3 9.5 2
## 7 Larsson, Mr. August Viktor 3 9.48 2
## 8 Meyer, Mr. August 2 13 3
## 9 Lindell, Mr. Edvard Bengtsson 3 15.6 3
## 10 Jerwan, Mrs. Amin S (Marie Marthe Thuillard) 2 13.8 3
## 11 Chapman, Mr. Charles Henry 2 13.5 3
## 12 Blackwell, Mr. Stephen Weart 1 35.5 4
## 13 Moran, Miss. Bertha 3 24.2 4
## 14 Collyer, Mrs. Harvey (Charlotte Annie Tate) 2 26.2 4
## 15 Chip, Mr. Chang 3 56.5 5
## 16 Chaffee, Mr. Herbert Fuller 1 61.2 5
## 17 Bissette, Miss. Amelia 1 136. 5
## 18 Endres, Miss. Caroline Louise 1 228. 5
## 19 Allison, Mrs. Hudson J C (Bessie Waldo Danie~ 1 152. 5
## 20 Carter, Master. William Thornton II 1 120 5
# tablici `titanic` dodajte stupac `EmbarkationPort` u kojem će pisati puno ime luke ukrcaja
# (Southampton, Queenstown ili Cherbourg)
# koristite `mutate` i dva `ifelse`
mutate(titanic, EmbarkationPort = ifelse(Embarked == 'S', 'Southampton',
ifelse(Embarked == 'C', 'Cherbourg', 'Queenstown'))) -> titanic
# ispišite prvih 10 redaka tablice `titanic`
head(titanic, 10)
## # A tibble: 10 x 14
## PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare
## <int> <fct> <fct> <chr> <fct> <dbl> <int> <int> <chr> <dbl>
## 1 1 0 3 Brau~ male 22 1 0 A/5 2~ 7.25
## 2 2 1 1 Cumi~ fema~ 38 1 0 PC 17~ 71.3
## 3 3 1 3 Heik~ fema~ 26 0 0 STON/~ 7.92
## 4 4 1 1 Futr~ fema~ 35 1 0 113803 53.1
## 5 5 0 3 Alle~ male 35 0 0 373450 8.05
## 6 6 0 3 Mora~ male NA 0 0 330877 8.46
## 7 7 0 1 McCa~ male 54 0 0 17463 51.9
## 8 8 0 3 Pals~ male 2 3 1 349909 21.1
## 9 9 1 3 John~ fema~ 27 0 2 347742 11.1
## 10 10 1 2 Nass~ fema~ 14 1 0 237736 30.1
## # ... with 4 more variables: Cabin <chr>, Embarked <fct>,
## # hadRelativesOnBoard <lgl>, EmbarkationPort <chr>
11.5 Ogledni podatkovni skup: Houston flights
Učitajmo paket dplyr
i podatkovni okvir hflights
koji se nalazi u paketu istog imena i može ga se dohvatiti iz CRAN repozitorija. Nakon učitavanja paketa podatkovni okvir možemo prebaciti u globalnu okolinu uz pomoć funkcije data
.
Zadatak 11.6 - podatkovni skup hflights
# učitajte paket `hflights`
# ukoliko je potrebno instalirajte ga sa CRAN repozitorija
# uz pomoć funkcije `data` učitajte okvir u globalnu okolinu
# pretvorite okvir u `hflights` u "tibble"
# upoznajte se s podatkovnim skupom `hflights`
# pored funkcija glimpse/head/itd. možete pogledati i dokumentaciju
# skupa uz pomoć naredbe `?hflights`
#install.packages("hflights") # ukoliko je potrebno
#library(dplyr) # ako već nisu učitani
#library(hflights)
data(hflights)
hflights <- as_tibble(hflights)
glimpse(hflights)
head(hflights)
## Observations: 227,496
## Variables: 21
## $ Year <int> 2011, 2011, 2011, 2011, 2011, 2011, 2011, 20...
## $ Month <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
## $ DayofMonth <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1...
## $ DayOfWeek <int> 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6,...
## $ DepTime <int> 1400, 1401, 1352, 1403, 1405, 1359, 1359, 13...
## $ ArrTime <int> 1500, 1501, 1502, 1513, 1507, 1503, 1509, 14...
## $ UniqueCarrier <chr> "AA", "AA", "AA", "AA", "AA", "AA", "AA", "A...
## $ FlightNum <int> 428, 428, 428, 428, 428, 428, 428, 428, 428,...
## $ TailNum <chr> "N576AA", "N557AA", "N541AA", "N403AA", "N49...
## $ ActualElapsedTime <int> 60, 60, 70, 70, 62, 64, 70, 59, 71, 70, 70, ...
## $ AirTime <int> 40, 45, 48, 39, 44, 45, 43, 40, 41, 45, 42, ...
## $ ArrDelay <int> -10, -9, -8, 3, -3, -7, -1, -16, 44, 43, 29,...
## $ DepDelay <int> 0, 1, -8, 3, 5, -1, -1, -5, 43, 43, 29, 19, ...
## $ Origin <chr> "IAH", "IAH", "IAH", "IAH", "IAH", "IAH", "I...
## $ Dest <chr> "DFW", "DFW", "DFW", "DFW", "DFW", "DFW", "D...
## $ Distance <int> 224, 224, 224, 224, 224, 224, 224, 224, 224,...
## $ TaxiIn <int> 7, 6, 5, 9, 9, 6, 12, 7, 8, 6, 8, 4, 6, 5, 6...
## $ TaxiOut <int> 13, 9, 17, 22, 9, 13, 15, 12, 22, 19, 20, 11...
## $ Cancelled <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ CancellationCode <chr> "", "", "", "", "", "", "", "", "", "", "", ...
## $ Diverted <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## # A tibble: 6 x 21
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## <int> <int> <int> <int> <int> <int> <chr> <int>
## 1 2011 1 1 6 1400 1500 AA 428
## 2 2011 1 2 7 1401 1501 AA 428
## 3 2011 1 3 1 1352 1502 AA 428
## 4 2011 1 4 2 1403 1513 AA 428
## 5 2011 1 5 3 1405 1507 AA 428
## 6 2011 1 6 4 1359 1503 AA 428
## # ... with 13 more variables: TailNum <chr>, ActualElapsedTime <int>,
## # AirTime <int>, ArrDelay <int>, DepDelay <int>, Origin <chr>,
## # Dest <chr>, Distance <int>, TaxiIn <int>, TaxiOut <int>,
## # Cancelled <int>, CancellationCode <chr>, Diverted <int>
11.6 Grupiranje i agregacija uz group_by
i summarise
U literaturi koja se bavi analizom podataka često ćemo naići na tzv. SAC paradigmu (engl. Split-Apply-Combine). Radi se o strategiji koja se svodi na rastavljanje velikog zadatka na manje dijelove, obavljanje određenog posla na svakom od dijelova te konačno kombiniranje svih rezultata u jedinstvenu cjelinu. Potrebu za ovom paradigmom nailazimo u različitim scenarijima analize - u eksploratornoj analizi podataka htjet ćemo izračunati različite statistike ili stvoriti nove varijable zasebno za različite podskupove podataka (npr. ovisno o nekoj kategorijskoj varijabli); kod obrade iznimno velikih količina podataka često želimo ubrzati proces obrade na način da podatke razbijemo u manje skupove koji će se svaki obrađivati zasebno (poznati princip Map-Reduce).
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 bi 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
):
Isprobajmo ovu funkciju na našem okviru hflights
.
Zadatak 11.7 - grupirana tablica
# stvorite varijablu `flight815` koja će sadržavati retke iz tablice `hflights`
# vezane uz let broj 815
# stvorite varijablu `grouped815` koja će sadržavati retke iz `flight815`
# grupirane po mjesecu
# provjerite klasu varijabli `flight815` i `grouped815`
# uz pomoć funkcije `glimpse` pogledajte strukture varijabli `flight815` i `grouped815`
# stvorite varijablu `flight815` koja će sadržavati retke iz tablice `hflights`
# vezane uz let broj 815
flight815 <- filter(hflights, FlightNum == 815)
# stvorite varijablu `grouped815` koja će sadržavati retke iz `flight815`
# grupirane po mjesecu
grouped815 <- group_by(flight815, Month)
# provjerite klasu varijabli `flight815` i `grouped815`
class(flight815)
class(grouped815)
## [1] "tbl_df" "tbl" "data.frame"
## [1] "grouped_df" "tbl_df" "tbl" "data.frame"
# uz pomoć funkcije `glimpse` pogledajte strukture varijabli `flight815` i `grouped815`
glimpse(flight815)
print("------------------------") # radi preglednijeg ispisa na konzoli
glimpse(grouped815)
## Observations: 95
## Variables: 21
## $ Year <int> 2011, 2011, 2011, 2011, 2011, 2011, 2011, 20...
## $ Month <int> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,...
## $ DayofMonth <int> 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, ...
## $ DayOfWeek <int> 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 7, 6,...
## $ DepTime <int> 2116, 2109, 2111, 2112, 2124, 2145, 2114, 21...
## $ ArrTime <int> 2255, 2252, 2255, 2326, 2321, 2327, 2324, 23...
## $ UniqueCarrier <chr> "CO", "CO", "CO", "CO", "CO", "CO", "CO", "C...
## $ FlightNum <int> 815, 815, 815, 815, 815, 815, 815, 815, 815,...
## $ TailNum <chr> "N74856", "N57852", "N78866", "N57863", "N57...
## $ ActualElapsedTime <int> 219, 223, 224, 254, 237, 222, 250, 231, 229,...
## $ AirTime <int> 194, 191, 190, 197, 198, 200, 192, 187, 186,...
## $ ArrDelay <int> 12, 6, 9, 40, 35, 41, 38, 18, 19, 10, 29, 33...
## $ DepDelay <int> 6, -1, 1, 2, 14, 35, 4, 0, 6, 9, 6, 17, 38, ...
## $ Origin <chr> "IAH", "IAH", "IAH", "IAH", "IAH", "IAH", "I...
## $ Dest <chr> "LAX", "LAX", "LAX", "LAX", "LAX", "LAX", "L...
## $ Distance <int> 1379, 1379, 1379, 1379, 1379, 1379, 1379, 13...
## $ TaxiIn <int> 10, 13, 15, 18, 12, 10, 14, 19, 12, 12, 20, ...
## $ TaxiOut <int> 15, 19, 19, 39, 27, 12, 44, 25, 31, 13, 39, ...
## $ Cancelled <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ CancellationCode <chr> "", "", "", "", "", "", "", "", "", "", "", ...
## $ Diverted <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## [1] "------------------------"
## Observations: 95
## Variables: 21
## Groups: Month [6]
## $ Year <int> 2011, 2011, 2011, 2011, 2011, 2011, 2011, 20...
## $ Month <int> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,...
## $ DayofMonth <int> 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, ...
## $ DayOfWeek <int> 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 7, 6,...
## $ DepTime <int> 2116, 2109, 2111, 2112, 2124, 2145, 2114, 21...
## $ ArrTime <int> 2255, 2252, 2255, 2326, 2321, 2327, 2324, 23...
## $ UniqueCarrier <chr> "CO", "CO", "CO", "CO", "CO", "CO", "CO", "C...
## $ FlightNum <int> 815, 815, 815, 815, 815, 815, 815, 815, 815,...
## $ TailNum <chr> "N74856", "N57852", "N78866", "N57863", "N57...
## $ ActualElapsedTime <int> 219, 223, 224, 254, 237, 222, 250, 231, 229,...
## $ AirTime <int> 194, 191, 190, 197, 198, 200, 192, 187, 186,...
## $ ArrDelay <int> 12, 6, 9, 40, 35, 41, 38, 18, 19, 10, 29, 33...
## $ DepDelay <int> 6, -1, 1, 2, 14, 35, 4, 0, 6, 9, 6, 17, 38, ...
## $ Origin <chr> "IAH", "IAH", "IAH", "IAH", "IAH", "IAH", "I...
## $ Dest <chr> "LAX", "LAX", "LAX", "LAX", "LAX", "LAX", "L...
## $ Distance <int> 1379, 1379, 1379, 1379, 1379, 1379, 1379, 13...
## $ TaxiIn <int> 10, 13, 15, 18, 12, 10, 14, 19, 12, 12, 20, ...
## $ TaxiOut <int> 15, 19, 19, 39, 27, 12, 44, 25, 31, 13, 39, ...
## $ Cancelled <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ CancellationCode <chr> "", "", "", "", "", "", "", "", "", "", "", ...
## $ Diverted <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
# ispišite prvih pet redaka varijable `flight815`
head(flight815)
print("------------------------")
# ispišite prvih pet redaka varijable `grouped815`
head(grouped815)
## # A tibble: 6 x 21
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## <int> <int> <int> <int> <int> <int> <chr> <int>
## 1 2011 4 30 6 2116 2255 CO 815
## 2 2011 4 29 5 2109 2252 CO 815
## 3 2011 4 28 4 2111 2255 CO 815
## 4 2011 4 27 3 2112 2326 CO 815
## 5 2011 4 26 2 2124 2321 CO 815
## 6 2011 4 25 1 2145 2327 CO 815
## # ... with 13 more variables: TailNum <chr>, ActualElapsedTime <int>,
## # AirTime <int>, ArrDelay <int>, DepDelay <int>, Origin <chr>,
## # Dest <chr>, Distance <int>, TaxiIn <int>, TaxiOut <int>,
## # Cancelled <int>, CancellationCode <chr>, Diverted <int>
## [1] "------------------------"
## # A tibble: 6 x 21
## # Groups: Month [1]
## Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum
## <int> <int> <int> <int> <int> <int> <chr> <int>
## 1 2011 4 30 6 2116 2255 CO 815
## 2 2011 4 29 5 2109 2252 CO 815
## 3 2011 4 28 4 2111 2255 CO 815
## 4 2011 4 27 3 2112 2326 CO 815
## 5 2011 4 26 2 2124 2321 CO 815
## 6 2011 4 25 1 2145 2327 CO 815
## # ... with 13 more variables: TailNum <chr>, ActualElapsedTime <int>,
## # AirTime <int>, ArrDelay <int>, DepDelay <int>, Origin <chr>,
## # Dest <chr>, Distance <int>, TaxiIn <int>, TaxiOut <int>,
## # Cancelled <int>, CancellationCode <chr>, Diverted <int>
Vidimo da grupiranjem nismo “izgubili” nikakvu informaciju - grupirani podatkovni okvir i dalje izgleda identično originalnom, “negrupiranom” okviru. Zapravo jedina naznaka da je nešto drugačije jest nova, nasljeđena klasa te redak Groups:..
u ispisu redaka. Ovo znači da je grupiranje okvira samo naznaka da se neke daljnje (najčešće agregacijske) operacije ne izvode nad cijelim okvirom, već nad pojedinim grupama. Isto tako, ukoliko želimo, okvir uvijek možemo lako “odgrupirati” uz pomoć funkcije ungroup
.
Za agregaciju se koristimo funkcijom summarise
koja prima podatke (grupiranu tablicu) a potom kombinacije agregacijskih funkcija i stupaca nad kojim se iste izvršavaju, npr.:
Za agregacijsku funkciju u pravilu možemo koristiti bilo koju funkciju koja vektor vrijednosti svodi na jednu vrijednost (npr. mean
, max
, sd
itd.), a često korištene funkcije koje nudi sam paket dplyr
su:
first
,last
,nth
- prvi, zadnji,n
-ti element grupen
,n_distinct
- broj (jedinstvenih) vrijednosti
Zadatak 11.8 - funkcija summarise
# izračunajte prosječno kašnjenje u dolasku leta za varijable `flight815` i `grouped815`
# koristite funkciju `summarise`
summarise(flight815, meanDelay = mean(ArrDelay))
print("------------------------")
summarise(grouped815, meanDelay = mean(ArrDelay))
## # A tibble: 1 x 1
## meanDelay
## <dbl>
## 1 21.6
## [1] "------------------------"
## # A tibble: 6 x 2
## Month meanDelay
## <int> <dbl>
## 1 4 21.1
## 2 5 20.9
## 3 6 28.0
## 4 8 -27.5
## 5 9 14
## 6 10 -13
Uočite da je poželjno imenovati agregirane stupce.
U praksi ne čuvamo referencu na grupirane tablice već unutar jedne naredbe provodimo cijeli proces odabira redaka i stupaca, grupiranja i provođenja agregacije. Ako koristimo operator %>%
, onda to može izgledati ovako:
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 iskoristiti sva dosadašnja saznanja o paketu dplyr
i riješiti sljedeće primjere. Svi zadaci vezani su uz cijeli podatkovni skup hflights
.
Zadatak 11.9 - složeni upiti
# ispišite koliko je bilo otkazanih letova zbog
# lošeg vremena u svakom mjesecu
# tablica rezultata mora imati stupce
# `Month` i `BadWeatherCancellations`
# ispišite prosječno vrijeme kašnjenja u dolasku
# kod odlaznih letova u zrakoplovne luke
# LAX, JFK i LGA u različitim danima u tjednu
# novi stupac nazovite 'MeanArrDelay'
# ignorirajte retke sa NA vrijednostima vremena kašnjenja
# ispis poredajte po vremenu kašnjenja silazno
# ispišite koliko je bilo otkazanih letova zbog
# lošeg vremena u svakom mjesecu
# tablica rezultata mora imati stupce
# `Month` i `BadWeatherCancellations`
filter(hflights, CancellationCode == 'B') %>%
group_by(Month) %>%
summarise(BadWeatherCancellations = n())
## # A tibble: 12 x 2
## Month BadWeatherCancellations
## <int> <int>
## 1 1 149
## 2 2 929
## 3 3 50
## 4 4 40
## 5 5 106
## 6 6 49
## 7 7 29
## 8 8 108
## 9 9 38
## 10 10 46
## 11 11 13
## 12 12 95
# ispišite prosječno vrijeme kašnjenja u dolasku
# kod odlaznih letova u zrakoplovne luke
# LAX, JFK i LGA u različitim danima u tjednu
# novi stupac nazovite 'MeanArrDelay'
# ignorirajte retke sa NA vrijednostima vremena kašnjenja
# ispis poredajte po vremenu kašnjenja silazno
filter(hflights, Dest %in% c('LAX', 'JFK', 'LGA')) %>% group_by(DayOfWeek, Dest) %>%
summarise(MeanArrDelay = round(mean(ArrDelay,
na.rm = T) , 2)) %>%
ungroup() %>% arrange(desc(MeanArrDelay))
## # A tibble: 21 x 3
## DayOfWeek Dest MeanArrDelay
## <int> <chr> <dbl>
## 1 4 LGA 21.3
## 2 4 JFK 17.2
## 3 5 LGA 13.6
## 4 1 LGA 13.5
## 5 7 JFK 10.8
## 6 1 LAX 10.7
## 7 3 LGA 10.5
## 8 4 LAX 10.3
## 9 2 LGA 9.85
## 10 6 JFK 9.76
## # ... with 11 more rows
Zadatak 11.10 - složeni upiti (2)
# u zadacima koji slijede zamislite da ste prvo napisali
# "vlastitu" funkciju koja pronalazi najčešću kategoriju:
mostFreqValue <- function(x) table(x) %>% sort(decreasing = T) %>% names %>% `[`(1)
# u tablici `hflights` podijelite vrijeme kašnjenja
# u dolasku u 10 kategorija uz pomoć funkcije `ntile`
# novi stupac nazovite `ArrDelayCatId`
# za svaku tu kategoriju ispišite identifikator
# kategorije, ukupan broj letova unutar kategorije,
# minimalno i maksimalno vrijeme kašnjenja u dolasku
# te koja zrakoplovna luka se najčešće
# pojavljuje u toj kategoriji
# ponovite prethodni primjer ali umjesto funkcije
# `ntile` isprobajte funkciju `cut`
# u tablici `hflights` podijelite vrijeme kašnjenja
# u dolasku u 10 kategorija uz pomoć funkcije `ntile`
# novi stupac nazovite `ArrDelayCatId`
# za svaku tu kategoriju ispišite identifikator
# kategorije, ukupan broj letova unutar kategorije,
# minimalno i maksimalno vrijeme kašnjenja u dolasku
# te koja zrakoplovna luka se najčešće
# pojavljuje u toj kategoriji
mutate(hflights, ArrDelayCatId = ntile(ArrDelay, 10)) %>%
group_by(ArrDelayCatId) %>% summarise(totalFlights = n(), minArrDelay = min(ArrDelay), maxArrDelay = max(ArrDelay), mostFreqDest = mostFreqValue(Dest))
## # A tibble: 11 x 5
## ArrDelayCatId totalFlights minArrDelay maxArrDelay mostFreqDest
## <int> <int> <dbl> <dbl> <chr>
## 1 1 22388 -70 -14 ATL
## 2 2 22387 -14 -9 ATL
## 3 3 22388 -9 -6 DAL
## 4 4 22387 -6 -3 DAL
## 5 5 22387 -3 0 DAL
## 6 6 22388 0 3 DAL
## 7 7 22387 3 8 DAL
## 8 8 22388 8 15 DAL
## 9 9 22387 15 32 DAL
## 10 10 22387 32 978 DAL
## 11 NA 3622 NA NA DAL
# ponovite prethodni primjer ali umjesto funkcije
# `ntile` isprobajte funkciju `cut`
mutate(hflights, ArrDelayCat = cut(ArrDelay, 10)) %>%
group_by(ArrDelayCat) %>% summarise(totalFlights = n(), minArrDelay = min(ArrDelay), maxArrDelay = max(ArrDelay), mostFreqDest = mostFreqValue(Dest))
## Warning: Factor `ArrDelayCat` contains implicit NA, consider using
## `forcats::fct_explicit_na`
## # A tibble: 11 x 5
## ArrDelayCat totalFlights minArrDelay maxArrDelay mostFreqDest
## <fct> <int> <dbl> <dbl> <chr>
## 1 (-71,34.8] 203101 -70 34 DAL
## 2 (34.8,140] 18551 35 139 DAL
## 3 (140,244] 1849 140 244 ATL
## 4 (244,349] 294 245 346 ATL
## 5 (349,454] 48 350 450 ATL
## 6 (454,559] 16 458 556 ATL
## 7 (559,664] 4 579 663 DFW
## 8 (664,768] 4 685 766 DFW
## 9 (768,873] 4 775 861 DEN
## 10 (873,979] 3 918 978 DFW
## 11 <NA> 3622 NA NA DAL
11.7 Spajanje podatkovnih okvira uz join funkcije
Spajanje podatkovnih okvira jest operacija poznata svim programerima koji imaju iskustvo u radu sa relacijskim bazama podataka i SQL-om. Kod pohrane u relacijsku bazu podataka tablice se često dekomponiraju u više tablica postupkom koji se naziva “normalizacija”. Svrha normalizacije je poglavito uklanjanje nepotrebne redundancije - svaka tablica zadržava dovoljno podataka kako bi se prema potrebi operacijom spajanja mogli rekonstruirati originalni podaci tj. skupovi obzervacija.
Iako postoje različiti oblici spajanja tablica, daleko najčešće je tzv. “prirodno” spajanje kod koje u jednoj tablici imamo jedinstvene identifikatore vezane uz podatke koji se nalaze u drugoj tablici. Na primjer - zamislimo da imamo tablice Korisnik i Mjesto, gdje tablica korisnika sustava može imati stupac koji pohranjuje poštanski broj mjesta boravišta korisnika, dok se ostali podaci vezani uz mjesta nalaze u drugoj tablici. Pohraniti naziv mjesta u tablicu s korisnicima bilo bi redundantno, budući da poštanski broj kao takav jedinstveno identificira mjesto, a ukoliko želimo kod ispisa korisnika vidjeti i nazive mjesta, obavljamo operaciju prirodnog spajanja dvije tablice po poštanskom broju mjesta koje je tzv. “strani ključ” u tablici Korisnik a istovremeno i “primarni ključ” u tablici Mjesto.
Stvorimo ovakve podatkovne okvire (sa tek nekoliko redaka kako bi koncept bio jasniji).
# inicijalizacija podatkovnih okvira `korisnik` i `mjesto`
korisnik <- data.frame( id = c(1:3), prezime = c("Ivic", "Peric", "Anic"),
pbrBoravista = c(10000, 31000, 10000))
mjesto <- data.frame( pbr = c(10000, 21000, 31000),
naziv = c("Zagreb", "Split", "Osijek"))
Ako bi htjeli imati okvir sa stupcima (id, prezime, pbrBoravista, naziv), moramo prirodno spojiti ova dva okvira. Paket dplyr
za ovo nudi funkciju inner_join
:
gdje su df1
i df2
podatkovni okviri koje spajamo, a nizovi znakova “s1” i “s2” označavaju imena stupaca “lijevog” i “desnog” okvira prema kojima provodimo spajanje (uočite samo jedan znak jednakosti!). Ukoliko stupac kojeg koristimo ima isto ime u obje tablice možemo navesti samo ime tog stupca (ili znakovni vektor više stupaca ako spajamo preko tzv. “kompozitnog” stranog ključa), ili taj parametar potpuno ispustiti (ako su stupci prema kojima provodimo spajanje jedini stupci čiji se nazivi poklapaju).
Zadatak 11.11 - prirodno spajanje
# stvorite okvir `korisnikMjesto` koji će biti rezultat
# prirodnog spajanja okvira `korisnik` i `mjesto`
# ispišite okvir `korisnikMjesto`
## id prezime pbrBoravista naziv
## 1 1 Ivic 10000 Zagreb
## 2 2 Peric 31000 Osijek
## 3 3 Anic 10000 Zagreb
Vodite računa da je spajanje vrlo “skupa” operacija; ako spajate okvire sa vrlo velikim brojem redaka operacija bi mogla trajati vrlo dugo i zauzeti dosta memorije. U slučaju da često dolazi do potrebe za ovakvim “velikim” spajanjima, onda se snažno preporučuje korištenje paketa data.table koji implementira algoritme sa znatno boljim performansama kod operacija spajanja (korištenjem indeksiranja), ili - ukoliko radimo s podacima iz relacijske baze - provesti spajanja na strani baze podataka i onda u R povući rezultat, umjesto da se spajanje ostavlja na odgovornost R-u. U idućem poglavlju prikazati ćemo kako se ovo potencijalno može izvesti i bez potrebe za pisanjem zasebnih SQL naredbi.
Ako pogledamo rezultat gornjeg primjera, možemo vidjeti da smo “izgubili” jedan redak tablice mjesto. Naime, redak (21000, ‘Split’) nije imao pripadajućeg korisnika te je “nestao” iz rezultata. Ovo je potpuno očekivani rezultat prirodnog spajanja, no postoje slučajevi kada želimo zadržati sve retke iz jedne od tablica koje spajamo. U tom slučaju koristimo tzv. “vanjsko spajanje” koje radi identično već viđenom, ali zadržava sve retke iz jedne ili obje od tablica na način da retci koji se ne uspiju spojiti ostaju u rezultatu, ali sa NA
vrijednostima sa suprotne strane. Razlikujemo “lijevo”, “desno”, i “puno” vanjsko spajanje, u ovisnosti želimo li zadržati sve retke iz lijeve, desne ili obje tablice. Paket dplyr
nudi funkcije koje obavljaju sve ove tipove spajanja, one imaju potpis identičan gore viđenoj funkciji a zovu se left_join
, right_join
i full_join
.
Zadatak 11.12 - vanjsko spajanje
# stvorite okvir `korisnikMjesto2` koji će biti rezultat
# prirodnog spajanja okvira `korisnik` i `mjesto`
# ali će zadržati sve retke iz tablice 'mjesto`
# ispišite okvir `korisnikMjesto2`
# retke poredajte po identifikatoru korisnika
korisnikMjesto2 <- right_join(korisnik, mjesto, by = c("pbrBoravista" = "pbr"))
korisnikMjesto2 %>% arrange(id)
## id prezime pbrBoravista naziv
## 1 1 Ivic 10000 Zagreb
## 2 2 Peric 31000 Osijek
## 3 3 Anic 10000 Zagreb
## 4 NA <NA> 21000 Split
11.8 Integracija sa relacijskim bazama podataka
Relacijske baze česti su izvor podataka za analize koje radimo u jeziku R. Jedna od standardnih procedura pripreme podataka za analitički proces je tzv. “izvoženje” (engl. export) podataka iz baze u CSV datoteku, koja se potom učitava u R. Često se prije stvaranja CSV datoteke prvo podaci iz baze moraju prikupiti i pripremiti na adekvatan način, što znači da cjelokupni proces pripreme podataka incijalno zahtjeva rad u SQL-u nad bazom podataka. Ovo je pogotovo bitno ako radimo sa većim skupovima podataka koji bi nepotrebno opteretili stroj nad kojim radimo analize u R-u, a koji bi se mogli znatno učinkovitije i brže mogli obraditi u bazi podataka.
Gore navedeno ne znači nužno da nam za prikupljanje podataka iz baze treba zaseban alat pomoću kojeg ćemo izvoditi SQL upite. R sadrži niz paketa za direktno spajanje i rad sa popularnijim bazama podataka, kao što su npr. MySQL ili PostgreSQL. Uz pomoć ovih paketa možemo unutar R skripte uspostaviti konekciju sa bazom i izvoditi SQL upite, a sve uz pomoć R naredbi pri čemu kao konačni rezultat dobivamo podatkovni okvir spreman za daljnje korake analize. Npr. rad direktno nad MySQL bazom može izgledati ovako:
install.packages("RMySQL")
library(RMySQL)
# unijeti stvarne parametre kod realnog scenarija!
conn = dbConnect(MySQL(), user='user', password='password', dbname='database_name', host='host')
df = dbSendQuery(conn, "SELECT * FROM MY_TABLE")
Vidimo da prvo moramo učitati paket s podrškom za spajanje na odabranu bazu, potom uspostavljamo konekciju sa bazom (parametre nam obično daje administrator same baze), a onda uz pomoć odgovarajućih funkcija postavljamo SQL upite). Iako ovakav pristup nije kompliciran i relativno se često koristi, on neumitno zahtijeva miješanje programskog koda u SQL-u i programskog koda u R-u.
Paket dplyr može ovdje pomoći na način da rastereti programera od potrebe pisanja SQL upita unutar R skripte. Naime, dplyr
sadrži integriranu podršku za automatsku konverziju izraza koji koriste dplyr
funkcije direktno u SQL upit, a što je transparentno za korisnika. Drugim riječima, do izvjesne mjere možemo na isti način raditi nad relacijskom bazom podataka kao da se radi o podatkovnim okvirima u memoriji računala. Ovdje trebamo biti oprezni jer ne možemo očekivati da će nam sve funkcionalnosti dplyr
-a moći biti automatski prevedene u SQL, ali neke standardne pripremne operacije poput spajanje i filtriranja se na ovaj način mogu raditi direktno u bazi, a kroz čisti R programski kod.
Kako bi mogli ovo isprobati u praksi, potrebna nam je relacijska baza podataka. Budući da nije praktično u ovom trenutku instalirati ili pripremati postojeću relacijsku bazu podataka samo za potrebe nekolicine primjera, učinit ćemo ono što radi većina R programera kada želi razvijati programski kod vezan uz relacijske baze, ali izbjeći potrebu za pristupom konkretnoj bazi podataka - koristiti ćemo se tzv. SQLite bazom. Ovo je jednostavna baza koja ne zahtijeva posebnu instalaciju, ne koristi poslužitelj i sve podatke drži u jednoj datoteci.
U sljedećem programskom odsječku učitavamo paket RSQLite, stvaramo novu bazu kreiranjem nove datoteke (ili se spajamo na postojeću ako datoteka ne postoji) te spremamo okvir mtcars
u tablicu AUTI što će nam glumiti “podatke u bazi”.
# instalacija i učitavanje paketa `RSQLite` (izvesti ako je potrebno)
#install.packages("RSQLite")
#library(RSQLite)
# otvaramo konekciju sa bazom "SQLite"
# (zapravo stvaramo datoteku 'moja.baza')
conn <- dbConnect(drv=SQLite(), dbname="moja.baza")
# stvaramo tablicu `AUTI` u koju pohranjujemo
# prvih 100 redaka tablice `mtcars`
# ovo simulira "tablicu u bazi"
dbWriteTable(conn, "AUTI", mtcars[1:100,], overwrite = T)
# provjeramo popis svih tablica
dbListTables(conn)
## [1] "AUTI"
Uobičajeni način prikupljanja podataka iz baze jest ugradnja SQL upita direktno u R programski kod i pohrana rezultata u varijablu.
# ispisujemo podatke o maksimalnoj brzini i težini svih automobila sa 6 cilindara
dbGetQuery(conn, "SELECT mpg, wt FROM AUTI WHERE cyl = 6")
## mpg wt
## 1 21.0 2.620
## 2 21.0 2.875
## 3 21.4 3.215
## 4 18.1 3.460
## 5 19.2 3.440
## 6 17.8 3.440
## 7 19.7 2.770
Ako dobro poznajemo SQL, onda ovakav način prikupljanja podataka vjerojatno ne izgleda pretjerano kompleksno i uistinu, veliki broj programera i analitičara rado kombinira SQL i R kako bi optimizirali proces prikupljanja i pripreme podataka. No ako nismo “jaki” u SQL-u, ili jednostavno želimo “čistiji” kod, možemo se koristiti alatima koje nam nudi paket dplyr
.
U idućem primjeru ponoviti ćemo dio gore prikazanog postupka (spojiti ćemo se na SQLite bazu nazvanu moja.baza i ispisati podatke iz tablice AUTI), ali ćemo sve izvesti uz pomoć dplyr
funkcija.
# povezujem se sa postojećom SQLite bazom
conn2 <- src_sqlite("moja.baza", create = FALSE)
# pogledajmo da li je tablica AUTI u bazi
src_tbls(conn2)
# stvaram referencu na ovu tablicu
# (ništa se ne učitava dok ne pristupam podacima!)
auti <- tbl(conn2, "AUTI")
# ispisujemo podatke o maksimalnoj brzini i težini svih automobila sa 6 cilindara
auti %>% filter(cyl == 6) %>% select(mpg, wt) %>% collect
## [1] "AUTI"
## # A tibble: 7 x 2
## mpg wt
## <dbl> <dbl>
## 1 21 2.62
## 2 21 2.88
## 3 21.4 3.22
## 4 18.1 3.46
## 5 19.2 3.44
## 6 17.8 3.44
## 7 19.7 2.77
Ključno je znati da dplyr
neće ništa izvoditi do trenutka kada mi eksplicitno zatražimo tražene podatke, a i tada će ih dohvaćati “u porcijama” kako nam ne bi zagušio memoriju. Upravo zbog toga smo u gornjem primjeru koristili funkciju collect
, koja zapravo znači “dohvati cijeli rezultat i pohrani ga u podatkovni okvir” (u našem slučaju mi smo ga jednostavno ispisali na zaslon). Na ovaj način R programer može znatno rasteretiti svoju R okolinu prebacujući odgovornost “teških” proračuna na bazu podataka, a sve uz korištenje istih funkcija koje koristi za upravljanje podatkovnim skupovima pohranjenim u memoriji računala u obliku podatkovnih okvira.
Zadaci za vježbu
U zadacima za vježbu poslužiti ćemo se proširenom 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.
Za 10 biljojeda koji najdulje spavaju ispišite ime, koliko dnevno spavaju i prosječnu tjelesnu, težinu u kg. Ispis poredajte po duljini spavanja silazno.
Ispišite prosječno, najdulje i najkraće vrijeme spavanja životinja ovisno o njihovom tipu prehrane.
Podijelite ukupno vrijeme spavanja u 5 razreda jednoliko po ukupnoj duljini dnevnog spavanja. Za svaki razred ispišite ukupan broj životinja koje pripadaju razredu, a potom posebno ukupan broj pripadnika razreda koji nisu biljojedi. Ispis poredajte od razreda životinja koje najmanje spavaju naviše. Stupce nazovite smisleno i pripazite da konačna tablica nema NA vrijednosti.
Sljedeći okvir sadrži šifre statusa očuvanja životinja i njihove opise:
conservationStatus <- data.frame(
code = c("ex", "ew", "cr", "en", "vu", "nt", "cd", "lc"),
description = c("extinct", "extinct in the wild",
"critically endangered", "endangered",
"vulnerable", "near threatened",
"conservation dependent", "least concern"))
Dodajte okviru msleep
stupac conservationDesc
koji će sadržavati pripadajuće opise preuzete iz gornjeg okvira. Pripazite da kod proširenja ne izgubite nijedan redak iz okvira msleep
.
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/