class: center, middle, inverse, title-slide # OBRADA PODATAKA ## Predavanje 5: Manipulacija i prilagodba podataka (data.table) ### Luka Sikic, PhD ### Fakultet hrvatskih studija |
OP
--- name: toc <style type="text/css"> .large4 { font-size: 400% } .large2 { font-size: 200% } .small90 { font-size: 90% } .small75 { font-size: 75% } </style> # Pregled predavanja 1. [Set-up](#prologue) 2. [Uvod](#intro) 3. [data.table osnove](#basics) 4. [Manipulacija redovima: DT[i, ]](#i) 5. [Manipulacija kolonama: DT[, j]](#j) 6. [Grupiranje: DT[, , by]](#by) 7. [Ključevi](#keys) 8. [Spajanje podataka](#merge) 9. [Preoblikovanje](#reshape) 10. [data.table + tidyverse](#tidyverse) 10. [Sažetak](#summary) --- class: inverse, center, middle name: prologue # Set-up <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Postavke za rad sa data.table paketom!) --- # Checklist Ove pakete ćemo koristit u predavanju: - Već instalirani: **dplyr**, **ggplot2**, **nycflights13** - Novi: **data.table**, **tidyfast**, **dtplyr**, **microbenchmark** -- Ovaj kod će instalirati (ako je potrebno) i učitati sve potrebne pakete za predavanje. ```r if (!require(pacman)) install.packages('pacman', repos = 'https://cran.rstudio.com') ``` ``` ## Warning: package 'pacman' was built under R version 4.0.3 ``` ```r pacman::p_load(dplyr, data.table, dtplyr, tidyfast, microbenchmark, ggplot2, nycflights13) options(dplyr.summarise.inform = FALSE) ## Isključi dplyr group_by poruke ``` --- class: inverse, center, middle name: intro # Uvod <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Osnova za korištenje data.table paketa!) --- # Zašto uopće data.table? **tidyverse** je sjajan način za manipulaciju podatcima. Također, može se koristiti za rad sa Big Data podatcima (SQL databases, Spark, etc.) -- Zašto je potrebna druga sintaksa za manipulaciju podatcima? -- Nekoliko je razloga za **data.table**: 1. Koncizna sintaksa 2. Nevjerojatna brznia 3. Memorijska efikasnost 4. Mnoštvo mogućnosti (+ stabilnost) 5. Nema zavisnosti (o drugim paketima, instalacijama, etc.) -- Prije detalja, pogledajte nekoliko primjera... --- # Zašto uopće data.table? (dalje) ### 1) Konciznost Ova dva koda postižu istu stvar: ```r # library(dplyr) ## Učitano # data(starwars, package = "dplyr") ## Uvezi podatke u GEnvir starwars %>% filter(species=="Human") %>% group_by(homeworld) %>% summarise(mean_height=mean(height)) ``` vs ```r # library(data.table) ## Učitano starwars_dt = as.data.table(starwars) starwars_dt[species=="Human", mean(height), by=homeworld] ``` --- name:fast # Zašto uopće data.table? (dalje) ### 2) Brzina .small90[ ```r collapse_dplyr = function() { storms %>% group_by(name, year, month, day) %>% summarize(wind = mean(wind), pressure = mean(pressure), category = dplyr::first(category)) } storms_dt = as.data.table(storms) collapse_dt = function() { storms_dt[, .(wind = mean(wind), pressure = mean(pressure), category = first(category)), by = .(name, year, month, day)] } microbenchmark(collapse_dplyr(), collapse_dt(), times = 10) ``` ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## collapse_dplyr() 121.4271 136.5441 147.48845 143.75100 153.2597 190.6388 10 ## collapse_dt() 2.3583 2.6939 3.82609 2.83105 3.3484 8.7030 10 ``` ] -- .small90[ **Rezultat:** data.table je 75x brža! ] --- # Zašto uopće data.table? (dalje) ### 3) Efikasnost Mjerenje i usporedba memorijske efikasnosti je relativno komplicirana [stvar](https://stackoverflow.com/a/61376971). Za detalje [pogledajte](https://jangorecki.gitlab.io/r-talks/2019-06-18_Poznan_why-data.table/why-data.table.pdf) (nakon 12-og slide) data.table funkcionalnosti. ### 4) Mogućnosti i 5) Nezavisnost Ova dva čimbenika idu zajedno jer su povezani sa stabilnošću koda. Nezavisnost se ondosi na [na](http://www.tinyverse.org/): ```r tools::package_dependencies("data.table", recursive = TRUE)[[1]] ``` ``` ## [1] "methods" ``` ```r tools::package_dependencies("dplyr", recursive = TRUE)[[1]] ``` ``` ## [1] "ellipsis" "assertthat" "glue" "magrittr" "methods" "pkgconfig" ## [7] "R6" "Rcpp" "rlang" "tibble" "tidyselect" "utils" ## [13] "BH" "plogr" "tools" "cli" "crayon" "fansi" ## [19] "lifecycle" "pillar" "vctrs" "purrr" "grDevices" "utf8" ## [25] "digest" ``` --- # Prije nastavka... Cilj ovog predavanja *nije* pokazati da je data.table superiorniji pristup od tidyverse. (Niti vice versa.) Ljudi imaju različite stavove i to je u redu... Cilj je prikazati još jedan alat kojim se može manipulirati velikim (i malim) skupovima podataka na efikasan način u programskom jeziku R... -- - Poznavanje oba pristupa će vam povećati efikasnost i napraviti od vas boljeg R korisnika/istraživača/podatkovnog znanstvenika/etc. -- Aspekt komplementarnosti ćemo obraditi na kraju predavanja. --- class: inverse, center, middle name: basics # data.table osnove <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Snalaženje u sintaksi paketa!) --- # data.table objekt Već smo vidjeli da `tidyerse` omogućava specifičnu i unaprijeđenu varijantu `data.frame`-a u formi `tibble` . -- Jednako vrijedi i za `data.table` . Sintaksa `data.table` zapravo funkcionira samo na objektima koji su prvo pretvoreni u `data.table`(slično kao `tibble`), a specijalna interna struktura `data.table` objekta je glavni razlog za iznimnu brzinu. (Više pogledajte [ovdje](https://rdatatable.gitlab.io/data.table/articles/datatable-intro.html#what-is-datatable-1a) i [ovdje](https://twitter.com/matloff/status/1131372631372918784).) -- `data.table` možete naraviti na nekoliko načina: - `fread('mydata.csv')` učitava `CSV` u `R` kao `data.table` (+ extremno brzo).<sup>1</sup> - `data.table(x = 1:10)` stvara novi `data.table` ni iz čega - `as.data.table(df)` pretvara postojeći `data.frame` (dalje: `df`) u `data.table`. - `setDT(df)` pretvara postojeći `df` u `data.table` *prema referenci*; i.e. nije potrebno pripisivanje novom objektu (*reasign*) .footnote[<sup>1</sup> Funkciju `fread()` ćemo detaljnije spominjati u nadolazećim predavanjima.] --- # Što znači "pretvaranje prema referenci"? Upravo je to ono što čini data.table tako izvrsnim: modifikacije se, u slučaju gdje je to moguće, izvode *prema referenci*. -- Što to znači? -- Bez da ulazimo u detalje, kratko objašnjenje se odnosi na to da R ima dva načina za izmjenu i pripisivanje objekata. 1. **Copy-on-modify:** Stvara kopiju podataka. Implicira dodate računalne resurse.<sup>*</sup> 2. **Modify-in-place:** Ne stvara kopiju nego direktno utječe na memoriju. .footnote[<sup>*</sup> Valja spomenuti da je važno napraviti distinkciju između <i>shallow</i> i <i>deep copies</i>.] -- data.table "modificira prema referenci" jer mjenja objekte na **modify-in-place** način. Upravo to povećava efikasnost i smanjuje memorijsku zahtjevnost! -- P.S.Dodatno pogledajte ako vas ovo zanima: (a) [Semantičke reference](https://rdatatable.gitlab.io/data.table/articles/datatable-reference-semantics.html) za data.table vignette-u, (b) [Names and Values](https://adv-r.hadley.nz/names-values.html) poglavlja *Advanced R* (Hadley Wickham), (c) Izvrstan i protočan [blog post](https://tysonbarrett.com//jekyll/update/2019/07/12/datatable/). --- # data.table sintaksa Svi data.table objekti imaju istu osnovnu sintaksu: .center[ .large2[DT[<span style='color: #66C2A5;'>i</span>, <span style='color: #FC8D62;'>j</span>, <span style='color: #8DA0CB;'>by</span>]] ] ![:col_row <span style='color: #66C2A5;'>Po kojim redovima?</span>, <span style='color: #FC8D62;'>Što učiniti?</span>, <span style='color: #8DA0CB;'>Grupiranje prema...</span>] -- .center[dplyr "ekvivalenti":] ![:col_list <span style='color: #66C2A5;'>filter(); slice(); arrange()</span>, <span style='color: #FC8D62;'>select(); mutate()</span>, <span style='color: #8DA0CB;'>group_by()</span>] -- tidyverse izvršava operacije korok po korak, a data.table izvršava sve u jednom koraku. - Na taj je nacin moguće izvršiti kompleksnu naredbu kao jednu fluidnu misao. - Ulančavanje pitem pipe operatora je također moguće. --- # Brzi primjer Detalji sljede nakon kratkog data.table primjera. Ovo je brza paralelna usporedba sa dplyr jer će to motivirati dljnji tok predavanja. Na osnovi *stawars* podataka postavimo pitanje: > Koja je prosječna visina ljudi po spolu? -- .pull-left[ ### dplyr ```r data(starwars, package = "dplyr") starwars %>% filter(species=="Human") %>% group_by(gender) %>% summarise(mean(height, na.rm=T)) ``` ] .pull-right[ ### data.table ```r starwars_dt = as.data.table(starwars) starwars_dt[ species=="Human", mean(height, na.rm=T), by = gender] ``` ] --- # Brzi primjer Detalji sljede nakon kratkog data.table primjera. Brza paralelna usporedba sa dplyr jer će to motivirati predavanje. Na stawars podatcima postavimo pitanje: > Koja je prosječna visina ljudi po spolu? .pull-left[ ### dplyr ```r data(starwars, package = "dplyr") starwars %>% * filter(species=="Human") %>% group_by(gender) %>% summarise(mean(height, na.rm=T)) ``` ] .pull-right[ ### data.table ```r starwars_dt = as.data.table(starwars) starwars_dt[ * species=="Human", ## i mean(height, na.rm=T), by = gender] ``` ] --- # Brzi primjer Detalji sljede nakon kratkog data.table primjera. Brza paralelna usporedba sa dplyr jer će to motivirati predavanje. Na stawars podatcima postavimo pitanje: > Koja je prosječna visina ljudi po spolu? .pull-left[ ### dplyr ```r data(starwars, package = "dplyr") starwars %>% filter(species=="Human") %>% group_by(gender) %>% * summarise(mean(height, na.rm=T)) ``` ] .pull-right[ ### data.table ```r starwars_dt = as.data.table(starwars) starwars_dt[ species=="Human", * mean(height, na.rm=T), ## j by = gender] ``` ] --- # Brzi primjer Detalji sljede nakon kratkog data.table primjera. Brza paralelna usporedba sa dplyr jer će to motivirati predavanje. Na stawars podatcima postavimo pitanje: > Koja je prosječna visina ljudi po spolu? .pull-left[ ### dplyr ```r data(starwars, package = "dplyr") starwars %>% filter(species=="Human") %>% * group_by(gender) %>% summarise(mean(height, na.rm=T)) ``` ] .pull-right[ ### data.table ```r starwars_dt = as.data.table(starwars) starwars_dt[ species=="Human", mean(height, na.rm=T), * by = gender] ## by ``` ] --- # Brzi primjer Detalji sljede nakon kratkog data.table primjera. Brza paralelna usporedba sa dplyr jer će to motivirati predavanje. Na stawars podatcima postavimo pitanje: > Koja je prosječna visina ljudi po spolu? .pull-left[ ### dplyr ```r data(starwars, package = "dplyr") starwars %>% filter(species=="Human") %>% group_by(gender) %>% summarise(mean(height, na.rm=T)) ``` ``` ## # A tibble: 2 x 2 ## gender `mean(height, na.rm = T)` ## <chr> <dbl> ## 1 feminine 160. ## 2 masculine 182. ``` ] .pull-right[ ### data.table ```r starwars_dt = as.data.table(starwars) starwars_dt[ species=="Human", mean(height, na.rm=T), by = gender] ``` ``` ## gender V1 ## 1: masculine 182.3478 ## 2: feminine 160.2500 ``` ] --- class: inverse, center, middle name: i # Manipulacija redovima: DT[i, ] <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Smjer prema dolje!) --- # Indeksiranje po redovima (filter) Indeksiranje po redovima je jednostavno u `data.table`. Sve radi očekivano ako imate iskustvo sa `dplyr`. - `DT[x == "string", ]`: Izdvoji redove x koji su jednaki "string"-u. - `DT[y > 5, ]`: Izdvoji redove u kojima je varijabla y veća od 5. - `DT[1:10, ]`: Izdvoji prvih 10 redova. -- Mnogostruki uvjeti su također dozvoljeni: - `DT[x=="string" & y>5, ]`: Izdvoji redove gdje je x "string" **I** y je veći od 5. -- Primijetite da ne trebamo zareze kada indeksiramo po `i` (i.e. niti `j` niti `by` funkcijske argumente). - `DT[x=="string"]` je isto kao `DT[x=="string", ]` - `DT[1:10]` je isto kao `DT[1:10, ]` - etc. --- # Indeksiranje po redovima (filter) (*dalje*) Ovdje je prethodni primjer indeksiranja na *starwars* `data.table` podatcima (objektu). ```r starwars_dt[height>190 & species=='Human'] ``` ``` ## name height mass hair_color skin_color eye_color birth_year ## 1: Darth Vader 202 136 none white yellow 41.9 ## 2: Qui-Gon Jinn 193 89 brown fair blue 92.0 ## 3: Dooku 193 80 white fair brown 102.0 ## 4: Bail Prestor Organa 191 NA black tan brown 67.0 ## sex gender homeworld species ## 1: male masculine Tatooine Human ## 2: male masculine <NA> Human ## 3: male masculine Serenno Human ## 4: male masculine Alderaan Human ## films ## 1: The Empire Strikes Back,Revenge of the Sith,Return of the Jedi,A New Hope ## 2: The Phantom Menace ## 3: Attack of the Clones,Revenge of the Sith ## 4: Attack of the Clones,Revenge of the Sith ## vehicles starships ## 1: TIE Advanced x1 ## 2: Tribubble bongo ## 3: Flitknot speeder ## 4: ``` --- # Posloži po redovima (arrange) ```r starwars_dt[order(birth_year)] ## (privremeno) sortiraj od najmlađeg prema najstarijem starwars_dt[order(-birth_year)] ## (privremeno) sortiraj od najstarijeg prema najmlađem ``` -- `data.table` također sadrži optimiziranu `setorder()` funkciju uz slaganje *prema referenci* (*by reference*). -- ```r setorder(starwars_dt, birth_year, na.last = TRUE) starwars_dt[1:5, name:birth_year] ## Prikaži samo dio podataka ``` ``` ## name height mass hair_color skin_color eye_color birth_year ## 1: Wicket Systri Warrick 88 20 brown brown brown 8 ## 2: IG-88 200 140 none metal red 15 ## 3: Luke Skywalker 172 77 blond fair blue 19 ## 4: Leia Organa 150 49 brown light brown 19 ## 5: Wedge Antilles 170 77 brown fair hazel 21 ``` --- class: inverse, center, middle name: j # Manipulacija kolonama: DT[, j] <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Smjer desno!) --- # j: jedno pravilo za sve Prisjetimo se nekih `dplyr` funkcija: - `select()` - `mutate()` - `summarise()` - `count()` -- `data.table` prepoznaje sve pobrojane funkcije... >"*Učini nešto sa ovom varijablom u mojem podatkovnom skupu!*" ... i omogućava da se sve napravi na jednom mjestu: u `j` slot-u. -- Ipak, potrebno je par sintaktičkih hack-ova vezanih uz način kako pripisujemo varijable u podatkovnom skupu. - Neki će smatrati ovo odbojnim (ili barem, čudnim) kada se prvi put susretnu sa` data.table`. - Zapravo se ne radi ni o čemu posebno kompleksnom, a za uzvrat daje *mnoštvo* funkcionalnosti. --- # Modifikacija kolona sa := Za dodavanje, brisanje, mijenjanje u data.table se koristi **`:=`** operator. - Riječ je o *walrus* operatoru. [Pogledaj](https://psmag.com/environment/how-do-you-take-down-two-tons-of-blubber-and-tusks)! -- Na primjer, - `DT[, xsq := x^2]`: Napravi novu kolonu (`xsq`) od postojeće (`x`). - `DT[, x := as.character(x)]`: Promijeni postojeću kolonu. -- **Važno:** `:=` je *modifikacija prema referenci*, i.e. "na mjestu". Zbog toga nije potrebno pripisivati promjene novom objektu. -- Zbog toga također promjene na objektu nisu vidljive ukoliko to eksplicitno ne tražimo od R. ```r DT = data.table(x = 1:2) # DT[, xsq := x^2] ## Promjena na mjestu bez prikaza DT[, x_sq := x^2][] ## Dodaj [] za prikaz razultata ``` ``` ## x x_sq ## 1: 1 1 ## 2: 2 4 ``` --- # Modifikacija kolona sa := (dalje) *Modifikacija prema referenci* ima važne implikacije za manipulaciju podatcima. Razmislite što se događa ako kopiramo `data.table` i nakon toga izbrišemo kolonu. ```r DT_copy = DT DT_copy[, x_sq := NULL] ``` -- Očito, "x_sq" je izbrisan iz `DT_copy` objekta. ...ali što se dogodilo sa originalnim `DT` objektom? -- ```r DT ``` ``` ## x ## 1: 1 ## 2: 2 ``` --- # Modifikacija kolona sa := (dalje) Također izbrisano...baš kao što modifikacija prema referenci nalaže. Kako bismo izbjegli ovakvo "ponašanje", moguće je koristiti [`data.table::copy()`](https://rdatatable.gitlab.io/data.table/reference/copy.html) funkciju. Probajte izvšiti sljedeću naredbu: ```r DT[, x_sq := x^2] DT_copy = copy(DT) DT_copy[, x_sq := NULL] DT ## x_sq je još uvijek tu! ``` --- # Modifikacija kolona sa := (dalje) ### Pod-pripisivanje prema referenci Fantastična funkcionalnost `:=` je `data.table` [sub-assign by reference](https://rdatatable.gitlab.io/data.table/articles/datatable-reference-semantics.html#ref-i-j) funkcionalnost. Za primjer razmotrimo jedan (lažni) skup podataka. ```r DT2 = data.table(a = -2:2, b = LETTERS[1:5]) ``` -- Zamislite da želimo locirati redove gdje je "a" negativan i zamijeniti pripadajuću "b" ćeliju sa NA. -- - U `dplyr` je potrebno napraviti `...mutate(b = ifelse(a < 0, NA, b))`. -- - U `data.table`, jednstavno specificirajte redove (`i`) i onda pod-pripišite (`j`) direktno. ```r DT2[a < 0, b := NA][] ## Dodajte [] za prikaz na eranu ``` ``` ## a b ## 1: -2 <NA> ## 2: -1 <NA> ## 3: 0 C ## 4: 1 D ## 5: 2 E ``` --- # Modifikacija kolona sa := (dalje) Dvije su mogućnosti za manipulaciju više kolna odjednom. 1. LHS `:=` RHS forma: `DT[, c("var1", "var2") := .(val1, val2)]` 2. Funkcionalna forma: `DT[, ':=' (var1=val1, var2=val2)]` -- Osobno preporučam funkcionalnu formu pa ćemo to koristiti dalje. E.g. ```r DT[, ':=' (y = 3:4, y_name = c("three", "four"))] DT ## Drugi način za print pored [] ``` ``` ## x x_sq y y_name ## 1: 1 1 3 three ## 2: 2 4 4 four ``` -- Dinamičko pripisivanje zavisnih kolona u jednom koraku (kao kod `dplyr::mutate`) ne funkcionira. ```r DT[, ':=' (z = 5:6, z_sq = z^2)][] ``` ``` ## Error in eval(jsub, SDenv, parent.frame()): object 'z' not found ``` --- # Dodatak: Ulančavanje data.table operacija Zadnji primjer je ne pokazuje da nije moguće raditi ulančane operacije sa data.table!!! -- Prirodni `data.table` način je dodavanje sukcesivnih `[]` operatora. ```r DT[, z := 5:6][, z_sq := z^2][] ``` ``` ## x x_sq y y_name z z_sq ## 1: 1 1 3 three 5 25 ## 2: 2 4 4 four 6 36 ``` -- Ako preferirate **magrittr** pipe operator...dodajte prefiks na `.` na svakom koraku: ```r # library(magrittr) ## Nije potrebno jer je %>% već učitan via dplyr DT %>% .[, xyz := x+y+z] %>% .[, xyz_sq := xyz^2] %>% .[] ``` ``` ## x x_sq y y_name z z_sq xyz xyz_sq ## 1: 1 1 3 three 5 25 9 81 ## 2: 2 4 4 four 6 36 12 144 ``` --- # Modifikacija kolona sa := (dalje) Za brisanje kolone u podatkovnom skupu možete koristiti NULL. ```r DT[, y_name := NULL] DT ``` ``` ## x x_sq y z z_sq xyz xyz_sq ## 1: 1 1 3 5 25 9 81 ## 2: 2 4 4 6 36 12 144 ``` --- # Indeksiranje po kolonama (select) Moguće je koristiti `j` slot za indeksiranje podataka po kolonama. Pogledajmo *starwars* podatke za primjere... -- Indeksiraj prema poziciji kolone: ```r starwars_dt[1:2, c(1:3, 10)] ``` ``` ## name height mass homeworld ## 1: Wicket Systri Warrick 88 20 Endor ## 2: IG-88 200 140 <NA> ``` -- ...ili po nazivu: ```r # starwars_dt[, c("name", "height", "mass", "homeworld")] ## također radi # starwars_dt[, list(name, height, mass, homeworld)] ## i ovo isto starwars_dt[1:2, .(name, height, mass, homeworld)] ``` ``` ## name height mass homeworld ## 1: Wicket Systri Warrick 88 20 Endor ## 2: IG-88 200 140 <NA> ``` --- # Dodatak: Zašto pak .()? Već smo vidjeli `.()`na par mjesta, e.g prethodni i [ovaj](#fast) slide ako se sjećate!? - `.()` je samo data.table kratica za `list()`. Obilato ćemo koristiti `.()` kada krenemo sa indeksiranjem i/ili grupiranjem više varijabli od jednom. Možete si objasniti da su to sintaktičke osobine `data.table` ali zapravo je riječ o pristupu koristiti ovu eintaksu izmjenično u `data.table`: - `.(var1, var2, ...)` - `list(var1, var2, ...)` - `c("var1", "var2", ...)` -- Meni je `.()` sintaksa izvrsna — manje tipkanja! — ali svatko kako voli! -- Vratimo se indeksiranju kolona... --- # Indeksiranje po kolonama (select) Kolone također možete obrisati negacijom. Isprobajte sljedeći kod: ```r starwars_dt[, !c("name", "height")] ``` -- ### Preimenovanje kolona Preimenovanje kolona/e prema referenci. Isprobajte sljedeći kod sami: ```r setnames(starwars_dt, old = c("name", "homeworld"), new = c"(alias", "crib"))[] ## Promijeni nazad, možda će varijable "name" ili "homeworld" trebati za poslje setnames(starwars_dt, old = c("alias", "crib"), new = c("name", "homeworld")) ``` -- `setnames()` pruža neke prednosti u performansama, npr. kada želite dinamički preimenovati kolone pri indeksiranju. Primjerice: ```r starwars_dt[1:2, .(alias = name, crib = homeworld)] ``` ``` ## alias crib ## 1: Wicket Systri Warrick Endor ## 2: IG-88 <NA> ``` --- # Indeksiranje po kolonama (select) Također valja napomenuti da `dplyr` gramatika radi i na `data.table` objektima. Probajte izvšiti sljedeći kod. (Dobiti ćete upozorenje o gubitku efikasnosti.) ```r starwars_dt[1:5, ] %>% select(crib = homeworld, everything()) ``` -- Vratiti ćemo se još na `dplyr` + `data.table` funkcionalnost na kraju predavanja.... --- # Agregacija Moguće su i operacije agregiranja na `j`. ```r starwars_dt[, mean(height, na.rm=T)] ``` ``` ## [1] 174.358 ``` -- Sjetite se da ne zadržavamo ništa ako ne pripišemo rezultat novom objektu. Za dodavanje nove kolone sa rezultatima originalnom podatkovnom skupu koristite `:=`. ```r starwars_dt[, mean_height := mean(height, na.rm=T)] %>% ## Dodaj prosječnu visinu kao kolonu .[1:5, .(name, height, mean_height)] ## Zadrži sve ``` ``` ## name height mean_height ## 1: Luke Skywalker 172 174.358 ## 2: C-3PO 167 174.358 ## 3: R2-D2 96 174.358 ## 4: Darth Vader 202 174.358 ## 5: Leia Organa 150 174.358 ``` --- # Agregacija (dalje) `data.table` također omogućava [specijalne simbole](https://rdatatable.gitlab.io/data.table/reference/special-symbols.html) za uobičajne agregacijske operacije na `j`. Na primjer, moguće je izbrojati opervacije sa `.N`. ```r starwars_dt[, .N] ``` ``` ## [1] 87 ``` -- Naravno, to je poprilično stiliziran primjer jer ćemo dobiti samo ukupni broj redova u podatcima. Kao i druge agregacijske funkcije, `.N` je puno interesantniji kada je primjenjen na grupirane podatke. - Ovo je uvod u narednu temu... --- name: by class: inverse, center, middle # Grupiranje: DT[, , by] <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Jako korisno!) --- # by data.table `by` argument funkcinira slično kao `dplyr::group_by` ekvivalent. Isprobajte sljedeće naredbe u vlastitoj R konzoli: - `starwars_dt[, mean(height, na.rm=T), by = species]`: Kolapsiraj po varijabli. - `starwars_dt[, .(species_height = mean(height, na.rm=T)), by = species]`: Kao gore ali po nazivu summary varijable. - `starwars_dt[, mean(mass, na.rm=T), by = height>190]`: Uvjeti također rade. - `starwars_dt[, species_n := .N, by = species][]`: Dodaj agregiranu kolonu podatcima (ovdje: broj opservacija po grupama). -- Za agregaciju po više varijabli možete koristiti `.()` sintaksu. ```r starwars_dt[, .(mean_height = mean(height, na.rm=T)), by = .(species, homeworld)] %>% head(4) ## stane na slide ``` ``` ## species homeworld mean_height ## 1: Human Tatooine 179.2500 ## 2: Droid Tatooine 132.0000 ## 3: Droid Naboo 96.0000 ## 4: Human Alderaan 176.3333 ``` --- # Efikasno indeksiranje sa .SD Vidjeli smo kao grupirati više varijabli. Što ako želimo *summarise* više varijabli, nevezano uz to kako grupiramo? Jedno rješenje je ponovno`.()` Raspišite sve: ```r ## isprobajte sami starwars_dt[, .(mean(height, na.rm=T), mean(mass, na.rm=T), mean(birth_year, na.rm=T)), by = species] ``` -- Ovo može postati zamorno! Zamislite još varijabli...da li stvarno moramo pisati `mean(..., na.rm=T)` za svaku varijablu? -- Odgovor je naravno, "ne". data.table ima `.SD` simbol za **s**ubsetting **d**ata. `.SD` može i [mnogo više](https://rdatatable.gitlab.io/data.table/articles/datatable-sd-usage.html) nego što ćemo prikazati, ali ovako bismo to primijenili u trenutnom slučaju... .right[*Vidi sljedeći slide.*] --- # Efikasno indeksiranje sa .SD (nastavak) ```r starwars_dt[, lapply(.SD, mean, na.rm=T), .SDcols = c("height", "mass", "birth_year"), by = species] %>% head(2) ## Zadrži sve na jednom slide ``` ``` ## species height mass birth_year ## 1: Ewok 88.0 20.00 8.00000 ## 2: Droid 131.2 69.75 53.33333 ``` --- count: false # Efikasno indeksiranje sa .SD (nastavak) ```r starwars_dt[, * lapply(.SD, mean, na.rm=T), .SDcols = c("height", "mass", "birth_year"), by = species] %>% head(2) ## adrži sve na jednom slide ``` ``` ## species height mass birth_year ## 1: Ewok 88.0 20.00 8.00000 ## 2: Droid 131.2 69.75 53.33333 ``` Prvo, specificirajmo što želimo *učiniti* na dijelu podataka (i.e. `.SD`). U ovom slučaju želimo prosjek za svaku varijablu, što postižemo sa base R funkcijom `lapply()`.<sup>1</sup> .footnote[ <sup>1</sup> Riječ je o iteracijskoj funkciji. Dio R koji se bavi radom sa funkcijama. ] --- count: false # Efikasno indeksiranje sa .SD (nastavak) ```r starwars_dt[, lapply(.SD, mean, na.rm=T), * .SDcols = c("height", "mass", "birth_year"), by = species] %>% head(2) ## Zadrži sve na slide ``` ``` ## species height mass birth_year ## 1: Ewok 88.0 20.00 8.00000 ## 2: Droid 131.2 69.75 53.33333 ``` Prvo, specificirajmo što želimo *učiniti* na dijelu podataka (i.e. `.SD`). U ovom slučaju želimo prosjek za svaku varijablu, što postižemo sa base R funkcijom `lapply()`.<sup>1</sup> Potom specificiramo *koje kolone* želimo indeksirati sa `.SDcols` argumentom. .footnote[ <sup>1</sup> Riječ je o iteracijskoj funkciji. Dio R koji se bavi radom sa funkcijama. ] --- count: false # Efikasno indeksiranje sa .SD (nastavak) ```r starwars_dt[, lapply(.SD, mean, na.rm=T), .SDcols = c("height", "mass", "birth_year"), by = species] %>% head(2) ## Just keep everything on the slide ``` ``` ## species height mass birth_year ## 1: Ewok 88.0 20.00 8.00000 ## 2: Droid 131.2 69.75 53.33333 ``` Prvo, specificirajmo što želimo *učiniti* na dijelu podataka (i.e. `.SD`). U ovom slučaju želimo prosjek za svaku varijablu, što postižemo sa base R funkcijom `lapply()`.<sup>1</sup> Potom specificiramo *koje kolone* želimo indeksirati sa `.SDcols` argumentom. P.S. `.()` ne funkcionira sa `.SDcols`. Ipak,moguće je korisiti navodnike, e.g. `.SDcols = height:mass`. [Vidi za detalje](https://rdatatable.gitlab.io/data.table/articles/datatable-intro.html#how-can-we-specify-just-the-columns-we-would-like-to-compute-the-mean-on). .footnote[ <sup>1</sup> Riječ je o iteracijskoj funkciji. Dio R koji se bavi radom sa funkcijama. ] --- # Efikasno indeksiranje sa .SD (nastavak) Dodatak: `.SDcols` je potrebno specificirati samo ako želimo indeksirati dio podataka. (Ovakve naredbe su također dozvoljene `.SDcols = is.numeric` ili `.SDcols = patterns('abc')`.) Ako želimo istu funkciju primijeniti na *sve* varijable u podatkovnom skupu, tada će `.SD` biti dovoljno. -- Za brzi primjer se prisjetite `DT` objekta koji ima samo numeričke varijable. ```r DT ``` ``` ## x x_sq y z z_sq xyz xyz_sq ## 1: 1 1 3 5 25 9 81 ## 2: 2 4 4 6 36 12 144 ``` -- Prosjeke za svaku varijablu možemo dobiti na sljedeći način. ```r DT[, lapply(.SD, mean)] ``` ``` ## x x_sq y z z_sq xyz xyz_sq ## 1: 1.5 2.5 3.5 5.5 30.5 10.5 112.5 ``` --- # keyby Zadnja stvar koju treba spomenuti vezano uz `by` je srodna funkcija: `keyby`. `keyby` argument funkcionira isto kao `by` — također se može koristiti i *drop-in* zamjena — osim što poreda opservacije, stvara i **key**. - Postavljanje ključa za `data.table` će omogućiti razne (i često sjajne) funkcionalnosti i poboljšanje performansi.<sup>1</sup> - Ključevi su toliko važni (i korisni) da ćemo ih obraditi detaljnije u sljedećem dijelu... .footnote[<sup>1</sup> Nećete vidjeti trenutno poboljšanje performansi sa `keyby`, ali naknadne operacije će "profitirati". (Trenutno poboljšanje je ipak mogue ako unaprijed postavite ključ, ali to ćemo objasniti na sljedećem slide-u...)] --- name: keys class: inverse, center, middle # Ključevi <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Ključevi za brzinu!) --- # Što su *ključevi*? *Ključevi* predstavljaju način aranžiranja podataka koji omogućava *ekstremno* brzo indeksiranje. `data.table` [vignette](https://rdatatable.gitlab.io/data.table/articles/datatable-keys-fast-subset.html) ih opisuje kao "supercharged rownames". Iako to zvuči pomalo apstraktno, osnovna ideja je sljedeća... -- Zamislite da želimo filtrirati podatkovni skup po nekoj vrijednosti (npr. pronaći sve ljudske karakteristike u *starwars* podatcima). - Standardni način bi bio pretraga po opservacijama dok ne nađemo sve koji odgovaraju kriteriju. - No ukoliko zadamo ključ, podatci su već posloženi na način da računalo mora pretražiti mnogo manji dio podataka. -- **Analogija:** Razmislite o načinu ispunjavanja ormara dokumentima: Dokumenti koji počinju sa "ABC" idu u gornju policu, "DEF" u nižu policu, etc. Da bismo našli *Anin* dokument, potrebno je pretražiti samo gornju policu. Za *Franka* samo drugu, i tako dalje. -- Ne samo da je ovo puno brži pristup, nego se i ista ideja odnosi na *sve ostale* varijante manipulacije podataka koje uključuju indeksiranje (agregacija po grupama, spajanje, etc.) -- P.S. doći ćemo do toga kasnije ali *ključevi* su "tajni sastojak" i u bazama podataka. --- # Kako postaviti *key* *(ključ)*? *Key* je moguće postaviti kada stvaramo` data.table` objekt. npr. - `DT = data.table(x = 1:10, y = LETTERS[1:10], key = "x")` - `DT = as.data.table(DF, key = "x")` - `setDT(DF, key = "x")` -- ...ili, *key* je moguće postaviti na već postojeći `data.table` objekt sa `setkey()`funkcijom. - `setkey(DT, x)`: Primijetite da *key* ne mora biti u navodnicima u ovom slučaju. -- **Važno:** Pošto *keys* samo opisuju određeni način aranžiranja podataka, moguće ih je postaviti na *više* kolona (Više o tome pogledajte [ovdje](https://rdatatable.gitlab.io/data.table/articles/datatable-keys-fast-subset.html#key-properties).) Npr - `DT = as.data.table(DF, key = c("x", "y"))` - `setkey(DT, x, y)`: Još jednom, navodnici ovdje nisu potrebni! -- </br> P.S. Koristite `key()` funkciju za postavljanje u `data.table` objektu. Moguće je imati samo jedan ključ za jedan data.table objekt u jednom trenutku. Ključeve je vrlo jednostavno promijeniti na maločas opisan način. --- # Primjer Sjetite se [benchmark brzine](#fast) sa početka predavanja: `data.table` je otprilike 75x brža od `dplyr` u uobičajenim slučajevima. -- Provjerimo ovaj benchmark, ali ovaj put sa pred-pripisanim *key*-em. Za optimalne performanse, ključ bi trebao odgovarati varijablama na kojima radimo grupiranje/indeksiranje. - Još jednom se podsjetimo, *key* može biti postavljen za više varijabli, iako je glavna grupacijska varijabla (u donjem slučaju: "name") najvažnija. -- .small90[ ```r ## Stvori storms data.table objekt koji ima postavljene ključeve. ## Primijetite da su podatci grupirani sa 'by' na dolje navedenim varijablama. storms_dt_key = as.data.table(storms, key = c("name", "year", "month", "day")) ## Kolapsiraj funkciju za ovaj (keyed) data.table. Sve ostalo je isto. collapse_dt_key = function() { storms_dt_key[, .(wind = mean(wind), pressure = mean(pressure), category = first(category)), by = .(name, year, month, day)] } ## Izvrši benchmark. microbenchmark(collapse_dplyr(), collapse_dt(), collapse_dt_key(), times = 10) ``` ] .right[*Rezultati na sljedećem slide-u*] --- # Primjer (nastavak) ``` ## Unit: milliseconds ## expr min lq mean median uq max ## collapse_dplyr() 114.1986 118.7084 126.35483 120.81490 127.3391 168.0286 ## collapse_dt() 2.3566 2.4222 3.63297 3.22265 4.2166 7.4705 ## collapse_dt_key() 1.5008 1.6584 2.38242 1.96500 2.0896 6.7864 ## neval ## 10 ## 10 ## 10 ``` -- `data.table` verzija sa ključem je sada **61** (!!!) brža nego `dplyr.` -- - To je to... jako brzo! -- Ovo nije samo "vulgaris" primjer. U praktičnom radu sa podatcima, korištenje *key*-eva skoro uvijek doprinosi brzini...a tu su na djelu i rastući prinosi na opseg...kako raste veličina podataka... -- **Zaključak:** `data.table` je super brza,a orištenje ključeva je izvanredno brzo. --- name: merge class: inverse, center, middle # Spajanje podataka <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Različiti izvori...) --- # Operacije (aka join) spajanja `data.table` nudi dva načina za spajanje pdataka. - `DT1[DT2, on = "id"]` - `merge(DT1, DT2, by = "id")` -- Preporučam drugi način zbog dodanih funkcionalnosti (vidi `?merge.data.table`), ali neka svatko odluči za sebe.<sup>1</sup> .footnote[<sup>1</sup> Za izvrstan pregled operacija spajanja (left, right, full, anti, etc.) koristeći ove dvije metode, a također i dplyr ekvivalente, pogledajte [ovdje](https://atrebas.github.io/post/2019-03-03-datatable-dplyr/#joinbind-data-sets).] -- Prisjetimo se spajanja podataka iz prethodnog predavanja gdje smo obradili [dplyr](hhttps://raw.githack.com/BrbanMiro/Obrada-podataka/main/Predavanja/05_MANIPULACIJA_tidy.html#54). Ovdje koristimo **nycflights13** paket. ```r # library(nycflights13) ## Učitano flights_dt = as.data.table(flights) planes_dt = as.data.table(planes) ``` --- # Left join primjer Pogledajte usporedbu sa dplyr ekvivalentom. Izvršite u konzoli za provjeru. (Redosljed redova se može razlikovati!) .pull-left[ **dplyr** ```r left_join( flights, planes, by = "tailnum" ) ``` ] .pull-right[ **data.table** ```r merge( flights_dt, planes_dt, all.x = TRUE, ## makni za inner join by = "tailnum") ``` ] -- Ako izvršite ove naredbe, vidjeti ćete da se konflikti oko naziva kolona "year" rješavaju stvaranjem "year.x" i "year.y" varijanti. U `dplyr` smo izbjegli ovaj problem korištenjem `rename()` funkcije. Kako možemo izbjeći isto u `data.table`? -- <b>Mogući odgovor:</b> Koristi `setnames()`. ```r merge( * setnames(flights_dt, old = "year", new = "year_built"), planes_dt, all.x = TRUE, by = "tailnum") ``` --- # Korištenje ključeva za izvanredno brzo spajanje Valja još dodati da [ključevi](#keys) jako povećavaju brzinu kod spajanje `data.table` objekata. Pogledajmo slučaj sa inner join. ```r merge_dt = function() merge(flights_dt, planes_dt, by = "tailnum") flights_dt_key = as.data.table(flights, key = "tailnum") planes_dt_key = as.data.table(planes, key = "tailnum") merge_dt_key = function() merge(flights_dt_key, planes_dt_key, by = "tailnum") microbenchmark(merge_dt(), merge_dt_key(), times = 10) ``` ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## merge_dt() 64.0946 72.5997 100.91993 78.67835 140.3384 149.5627 10 ## merge_dt_key() 34.0703 35.0698 50.29729 43.48270 49.4745 125.6161 10 ``` -- Rezultat je 2x veća brzina u ovom jednostavnom slučaju. Za velike skupove podataka i komplicirana spajanja, korištenje ključeva uistinu čini razliku. (Ista stvar vrijedi i za `dplyr`. Pogledajte [ovdje](https://tysonbarrett.com//jekyll/update/2019/10/11/speed_of_joins/).) --- name: reshape class: inverse, center, middle # Preoblikovanje podataka <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (*engl.Reshaping data*) --- # Opcije za preoblikovanje sa data.table U `tidyverse` predavanju smo vidjeli kako koristiti `tidyr::pivot*` funkcije. `data.table` ima spacifične funkcije za fleksibilno preoblikovanje podataka: - `melt()`: prebaci iz wide u long - `dcast()`: prebaci iz long u wide -- Ovdje je korisno spomenuti i [**tidyfast**](https://tysonbarrett.com/tidyfast/index.html) paket od Tyson Barrett-a, koji implementira `data.table` verzije `tidyr::pivot*` funkcija (i još neke druge stvari). - `tidyfast::dt_pivot_longer()`: wide u long - `tidyfast::dt_pivot_wider()`: long u wide -- Pogledajet obje opcije na (*falšanim*) "stocks" podatcima: ```r stocks = data.table(time = as.Date('2009-01-01') + 0:1, X = rnorm(2, 0, 1), Y = rnorm(2, 0, 2), Z = rnorm(2, 0, 4)) ``` --- # Preoblikovanje od wide u long Naši podatci su trenutno u wide formatu. ```r stocks ``` ``` ## time X Y Z ## 1: 2009-01-01 -0.9023656 0.9551291 -0.9163725 ## 2: 2009-01-02 0.5213462 -4.3692013 -0.5253797 ``` Za prebacivanje u long format, možete koristiti sljedeće naredbe: .pull-left[.small90[ ```r # Vidi ?melt.data.table za mogućnosti melt(stocks, id.vars ="time") ``` ``` ## time variable value ## 1: 2009-01-01 X -0.9023656 ## 2: 2009-01-02 X 0.5213462 ## 3: 2009-01-01 Y 0.9551291 ## 4: 2009-01-02 Y -4.3692013 ## 5: 2009-01-01 Z -0.9163725 ## 6: 2009-01-02 Z -0.5253797 ``` ]] .pull-right[.small90[ ```r stocks %>% dt_pivot_longer(X:Z, names_to="stock", values_to="price") ``` ``` ## time stock price ## 1: 2009-01-01 X -0.9023656 ## 2: 2009-01-02 X 0.5213462 ## 3: 2009-01-01 Y 0.9551291 ## 4: 2009-01-02 Y -4.3692013 ## 5: 2009-01-01 Z -0.9163725 ## 6: 2009-01-02 Z -0.5253797 ``` ]] --- # Problikovanje od long u wide Spremimo *long-format stocks data.table*. Pogledajte kako `melt()` pristup daje neke dodatne opcije za preimenovanje varijabli: ```r stocks_long = melt(stocks, id.vars ="time", variable.name = "stock", value.name = "price") stocks_long ``` ``` ## time stock price ## 1: 2009-01-01 X -0.9023656 ## 2: 2009-01-02 X 0.5213462 ## 3: 2009-01-01 Y 0.9551291 ## 4: 2009-01-02 Y -4.3692013 ## 5: 2009-01-01 Z -0.9163725 ## 6: 2009-01-02 Z -0.5253797 ``` -- .pull-left[.small90[ ```r dcast(stocks_long, time ~ stock, value.var = "price") ``` ``` ## time X Y Z ## 1: 2009-01-01 -0.9023656 0.9551291 -0.9163725 ## 2: 2009-01-02 0.5213462 -4.3692013 -0.5253797 ``` ]] .pull-right[.small90[ ```r stocks_long %>% dt_pivot_wider(names_from=stock, values_from=price) ``` ``` ## time X Y Z ## 1: 2009-01-01 -0.9023656 0.9551291 -0.9163725 ## 2: 2009-01-02 0.5213462 -4.3692013 -0.5253797 ``` ]] --- name: tidyverse class: inverse, center, middle # data.table + tidyverse kombinacije <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Konvergencijski pristup!) --- #Izaberite najbolji pristup za svoje potrebe R uistinu pruža postoji obilje funkcionalnosti u radu sa podatcima! Postoje dva sjajna pristupa: - **tidyverse** (esp. **dplyr** i **tidyr**) - **data.table** U prethodna dva predavanja smo istražili glavne karakteristike ovih pristupa. Korisnici će najčešće preferirati jedan ili drugi. - Netko voli izražajnost i modularnost `tidyverse` pristupa. - Drugi vole jednostavnost i moć `data.table` pristupa. -- ...baš kao što smo spomenuli u prethodnom dijelu predavanja: najbolje je koristiti oba ekosustava! Naredni slide-ovi daju nekoliko savjeta kako kombinirati `data.table` i `tidyverse` na optimalan način. --- # Odaberi i izaberi Prvi način je očit: `tidyverse` se sastoji od mnoštva paketa. Iako možda preferirate `data.table` umjesto `dplyr`+`tidyr` za manipulaciju podatcima, to ne znači da ne možete koristiti funkcionalnosti iz drugih `tidyverse` paketa. npr. gotovo svaki HC `data.table` korisnik je također i HC **ggplot2** korisnik. .pull-left[ .small75[ ```r ## library(ggplot2) # učitano storms_dt[, .(wind = mean(wind), pressure = mean(pressure), category = first(category)), by = .(name, year, month, day)] %>% ggplot(aes(x = pressure, y = wind, col=category)) + geom_point(alpha = 0.3) + theme_minimal() ``` ]] .pull-right[ <img src="05_MANIPULACIJA_dt_files/figure-html/storms_ggplot-1.png" style="display: block; margin: auto;" /> ] --- # Nemojte biti (radikalni) fanatik Vezano uz prvi način: Nemojte na silu svaki manipulacijski problem gurati u `tidyverse` ili `data.table` okvir. - Sjetite se da je kombinacija `tidyverse` i `base `R često najbolje rješenje. Jednostavno dodajte `data.table` u tu priču. -- Postoje neke operacije u kojima je `tidyverse` (`dplyr` + `tidyr`) jednostavno bolji i druge u kojima je `data.table` bolji pristup. - Ako nađete dobro rješenje na StackOverflow koje koristi "drugi" paket...primjenite ga. -- Dodatno, kao što smo već spomenuli prije `tidyverse` funkcije je *moguće* koristiti i sa `data.table`. Isprobajte: ```r starwars_dt %>% group_by(homeworld) %>% summarise(height = mean(height, na.rm=T)) ``` .footnote[<sup>1</sup> Ovo će [izazvati](https://stackoverflow.com/a/27513921) penalizaciju performansi. Bolje rješenje na sljedećem slide-u...] -- **Zaključak:** Bez fanatizma. Inzistiranje na određenom ekosistemu nije nabolji način... --- # dtplyr Sviđa vam se `dplyr` sintaksa, ali želite `data.table` performanse? -- Može i to!! -- Hadley Wickham- ov paket **dtplyr** omogućuje `data.table` "back-end" za` dplyr`. - Pišite kod kao da radite sa `dplyr` i on će automatski biti preveden (i evaluiran) u `data.table`. -- Ako ovo zvuči primamljivo (a trebalo bi!) pogledajte [dokumentaciju paketa](https://dtplyr.tidyverse.org/) za detalje. Pogledajmo još jedan brzi primjer: .font80[ ```r # library(dtplyr) ## učitano storms_dtplyr = lazy_dt(storms) ## dtplyr moraju biti postavljeni kao "lazy" data.table collapse_dtplyr = function() { storms_dtplyr %>% group_by(name, year, month, day) %>% summarize(wind = mean(wind), pressure = mean(pressure), category = first(category)) %>% as_tibble() } ## usporedite dtplyr sa dplyr i data.table verzijama(i.e. bez ključeva) microbenchmark::microbenchmark(collapse_dplyr(), collapse_dt(), collapse_dtplyr(), times = 10) ``` ] .right[*Rezultati na sljdećem slide-u*] --- # dtplyr (dalje) ``` ## Unit: milliseconds ## expr min lq mean median uq max ## collapse_dplyr() 127.4010 127.7612 134.61065 131.85095 134.0036 168.1635 ## collapse_dt() 2.8034 3.1070 3.71065 3.31620 3.6284 7.1798 ## collapse_dtplyr() 4.3457 4.7902 6.41724 5.06125 5.8229 14.5550 ## neval ## 10 ## 10 ## 10 ``` -- Ne baš tako brzo kao `data.table`, ali ipak >30x povećanje brzine! -- **Dodatak:** `dtplyr` automatski ispisuje `data.table` prijevod kao output. Ovo može biti od pomoći ako u data.table dolazte iz tidyverse. --- # dtplyr (dalje) ```r lazy_dt(starwars) %>% filter(species=="Human") %>% group_by(gender) %>% summarise(height = mean(height, na.rm=TRUE)) ``` ``` ## Source: local data table [?? x 2] ## Call: `_DT2`[species == "Human"][, .(height = mean(height, na.rm = TRUE)), ## keyby = .(gender)] ## ## gender height ## <chr> <dbl> ## 1 feminine 160. ## 2 masculine 182. ## ## # Use as.data.table()/as.data.frame()/as_tibble() to access results ``` --- name: summary class: inverse, center, middle # Sažetak <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Prošli smo stvarno dosta toga!) --- # Sažetak `data.table` je moćan paket za manipulaciju podatcima koji ima konciznu sintaksu i fascinantne performanse. Također je riječ o vrlo lightweight paketu koji istovremeno ima mnogo opcija. Osnovna sintaksa je `DT[i, j, by]` - `i` Na kojim redovima? - `j` Što raditi? - `by` Grupiranje po...? `data.table` uvodi neke nove ideje kao modifikacije prema referenci (npr. `:=`), kao i sintaksu (npr. `.()`, `.SD` `.SDcols`, etc.). - Sve ovo podržava glavne ideje i ciljeve `data.table`: Maksimizacija performansi i fleksibilnosti uz zadržavanje koncizne i funkcionalne sintakse. Vrijedi to naučiti! Savjet: Koristite ključeve za galaktičke brzine! `tidyverse` i `data.table` se često promatraju kao substituti, ali možete dosta profitirati od njihovih kombinacija... čak i ako preferirate jednu u manipulaciji podataka. --- # Neki dodatni resursi Postoji mnoštvo `data.table` funkcionalnosti koje nismo stigli spomenuti. Neke ćemo vidjeti u narednim predavanjima (npr. brzo učitavanje `fread()` i `fwrite()` CSV I/O funkcije). Druge možete potražiti sami. Ovdje su neki korisni izvori: - http://r-datatable.com (Službeni web. posebno pogledajte vignette!) - https://github.com/Rdatatable/data.table#cheatsheets (Cheatsheet.) - https://atrebas.github.io/post/2019-03-03-datatable-dplyr (Izvrsno, pregledne usporedbe `data.table` i `dplyr` operacija.) - https://brooksandrew.github.io/simpleblog/articles/advanced-data-table/ (Neki napredni `data.table` trikovi.) Povezani paketi: - https://tysonbarrett.com/tidyfast - https://dtplyr.tidyverse.org --- class: inverse, center, middle # HVALA NA PAŽNJI! <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> (Sljedeće predavanje: Web Scraping)