Kapitola 4 Transformácia údajov a súhrny pomocou dplyr
V tejto kapitole preberieme moderné nástroje na manipuláciu so súbormi údajov (ktoré sú už v čistej tabuľkovej forme - viac o tom v ďalšej kapitole), informácie pochádzajú najmä z online verzií kníh (Wickham a Grolemund 2016, kap. 5), (Ismay a Kim 2019, kap. 3) a (R. D. Peng 2016, kap. 13).
4.1 Všeobecne
Základnou dátovou štruktúrou v R (kontajnerom na údaje) pre ďalšie štatistické spracovanie je data.frame, v ktorom každý riadok predstavuje jedno pozorovanie (meranie, záznam…) a každý stĺpec jednu premennú (veličinu, mieru, vlastnosť, charakteristiku, znak…). Ukázali sme si už základné nástroje na vytváranie a manipuláciu s dátovými rámcami ako napr. subsetting ($, [ ], subset), avšak iné operácie ako komplexnejšie filtrovanie (výber pozorovaní podľa zadaných kritérií), zoraďovanie či tvorenie súhrnov môžu byť trochu únavné a neprehľadné, syntax jazyka R totiž nie je veľmi intuitívna. Toto sa snaží odstrániť balík dplyr (Wickham et al. 2020), ktorý implementuje konzistentnú “gramatiku” (v súlade s názvoslovím databázového jazyka SQL - Structured Query Language) a je veľmi rýchly. Kľúčovými funkciami sú:
select - výber stĺpcov
filter - výber riadkov na základe logických podmienok
arrange - zoradenie riadkov
rename - premenovanie stĺpcov (premenných)
mutate - pridanie nových stĺpcov napr. transformáciou iných
summarise, summarize - generovanie podmienených súhrnov pre daný stĺpec
%>% - pipe operátor pre reťazenie príkazov (analógia skladania funkcií v matematike)
Balík je súčasťou ekosystému tidyverse (Wickham et al. 2019), čo je kolekcia balíkov navrhnutých pre data science a vychádzajúcich zo spoločnej filozofie, gramatiky a dátových štruktúr. Funkcie zdieľajú niekoľko spoločných vlastností:
- prvý argument je data frame,
- ďalšie argumenty špecifikujú, čo sa má s dátami z prvého argumentu urobiť, pričom na stĺpce stačí odkázať menom,
- výstupom je opäť data frame,
- dátové rámce musia byť riadne formátované, “čisté” (tidy)
4.2 Prakticky
Balík pri načítaní predefinuje niektoré známe funkcie, preto je dobrým zvykom písať funkcie celým menom, napr. base::filter()
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
Prvou kľúčovou funkciou je select, ktorou sa vyberá podmnožina stĺpcov. Z datasetu mtcars pre ilustráciu vyberme postupne stĺpce - najprv jednotlivo po mene, napr. dojazd, hmotnosť, potom všetky stĺpce začínajúce písmenom “c” okrem carb, ďalej ôsmy stĺpec a nakoniec všetky stĺpce v poradí od zdvihového objemu až po drat:
data(mtcars)
<- select(mtcars, c(mpg, wt), starts_with("c"), - carb, 8, disp:drat)
tmp head(tmp)
## mpg wt cyl vs disp hp drat
## Mazda RX4 21.0 2.620 6 0 160 110 3.90
## Mazda RX4 Wag 21.0 2.875 6 0 160 110 3.90
## Datsun 710 22.8 2.320 4 1 108 93 3.85
## Hornet 4 Drive 21.4 3.215 6 1 258 110 3.08
## Hornet Sportabout 18.7 3.440 8 0 360 175 3.15
## Valiant 18.1 3.460 6 1 225 105 2.76
Týchto pomocných funkcií je ešte oveľa viac: ends_with, contains, matches, num_range, one_of, everything.
Pre zmenu, výber riadkov zabezpečuje funkcia filter. Obmedzme výber na všetky autá s hmotnosťou pod 3000 libier a výkonom nad 150 koní:
filter(tmp, wt < 3.0 & hp > 150)
## mpg wt cyl vs disp hp drat
## Ferrari Dino 19.7 2.77 6 0 145 175 3.62
Na formulovanie podmienok sa dajú použiť aj funkcie ako is.na, between, near.
Zoradenie riadkov podľa jedného alebo viacerých stĺpcov zabezpečuje arrange, napr. tu zoradíme autá primárne podľa počtu valcov zostupne a sekundárne podľa uloženia valcov.
arrange(tmp, desc(cyl), vs)
## mpg wt cyl vs disp hp drat
## Hornet Sportabout 18.7 3.440 8 0 360.0 175 3.15
## Duster 360 14.3 3.570 8 0 360.0 245 3.21
## Merc 450SE 16.4 4.070 8 0 275.8 180 3.07
## Merc 450SL 17.3 3.730 8 0 275.8 180 3.07
## Merc 450SLC 15.2 3.780 8 0 275.8 180 3.07
## Cadillac Fleetwood 10.4 5.250 8 0 472.0 205 2.93
## Lincoln Continental 10.4 5.424 8 0 460.0 215 3.00
## Chrysler Imperial 14.7 5.345 8 0 440.0 230 3.23
## Dodge Challenger 15.5 3.520 8 0 318.0 150 2.76
## AMC Javelin 15.2 3.435 8 0 304.0 150 3.15
## Camaro Z28 13.3 3.840 8 0 350.0 245 3.73
## Pontiac Firebird 19.2 3.845 8 0 400.0 175 3.08
## Ford Pantera L 15.8 3.170 8 0 351.0 264 4.22
## Maserati Bora 15.0 3.570 8 0 301.0 335 3.54
## Mazda RX4 21.0 2.620 6 0 160.0 110 3.90
## Mazda RX4 Wag 21.0 2.875 6 0 160.0 110 3.90
## Ferrari Dino 19.7 2.770 6 0 145.0 175 3.62
## Hornet 4 Drive 21.4 3.215 6 1 258.0 110 3.08
## Valiant 18.1 3.460 6 1 225.0 105 2.76
## Merc 280 19.2 3.440 6 1 167.6 123 3.92
## Merc 280C 17.8 3.440 6 1 167.6 123 3.92
## Porsche 914-2 26.0 2.140 4 0 120.3 91 4.43
## Datsun 710 22.8 2.320 4 1 108.0 93 3.85
## Merc 240D 24.4 3.190 4 1 146.7 62 3.69
## Merc 230 22.8 3.150 4 1 140.8 95 3.92
## Fiat 128 32.4 2.200 4 1 78.7 66 4.08
## Honda Civic 30.4 1.615 4 1 75.7 52 4.93
## Toyota Corolla 33.9 1.835 4 1 71.1 65 4.22
## Toyota Corona 21.5 2.465 4 1 120.1 97 3.70
## Fiat X1-9 27.3 1.935 4 1 79.0 66 4.08
## Lotus Europa 30.4 1.513 4 1 95.1 113 3.77
## Volvo 142E 21.4 2.780 4 1 121.0 109 4.11
Premenovanie stĺpcov pomocou rename:
<- rename(tmp, wt_lbs = wt, disp_in3 = disp)
tmp head(tmp)
## mpg wt_lbs cyl vs disp_in3 hp drat
## Mazda RX4 21.0 2.620 6 0 160 110 3.90
## Mazda RX4 Wag 21.0 2.875 6 0 160 110 3.90
## Datsun 710 22.8 2.320 4 1 108 93 3.85
## Hornet 4 Drive 21.4 3.215 6 1 258 110 3.08
## Hornet Sportabout 18.7 3.440 8 0 360 175 3.15
## Valiant 18.1 3.460 6 1 225 105 2.76
Vytvorenie nových stĺpcov cez funkciu mutate, v ktorej opäť možno použiť množstvo pomocných funkcií, napr. recode, if_else … (pozri nápovedu):
<- mutate(tmp, disp_dm3 = disp_in3 * 16e-3, disp2cyl_dm3 = disp_dm3 / cyl)
tmp head(tmp)
## mpg wt_lbs cyl vs disp_in3 hp drat disp_dm3 disp2cyl_dm3
## Mazda RX4 21.0 2.620 6 0 160 110 3.90 2.560 0.4266667
## Mazda RX4 Wag 21.0 2.875 6 0 160 110 3.90 2.560 0.4266667
## Datsun 710 22.8 2.320 4 1 108 93 3.85 1.728 0.4320000
## Hornet 4 Drive 21.4 3.215 6 1 258 110 3.08 4.128 0.6880000
## Hornet Sportabout 18.7 3.440 8 0 360 175 3.15 5.760 0.7200000
## Valiant 18.1 3.460 6 1 225 105 2.76 3.600 0.6000000
Výpočet štatistických súhrnov je možný pomocou summarize.
summarize(tmp, mpg_mean = mean(mpg), hp_mean = mean(hp))
## mpg_mean hp_mean
## 1 20.09062 146.6875
Táto funkcia však ukáže svoju silu až v kombinácii s group_by. Vypočítajme napríklad priemerný dojazd a výkon motora podľa počtu valcov a ich uloženia.
summarize(group_by(mtcars, cyl, vs),
mpg_mean = mean(mpg), hp_mean = mean(hp), number_obs = n(),
.groups = "drop"
)## # A tibble: 5 x 5
## cyl vs mpg_mean hp_mean number_obs
## * <dbl> <dbl> <dbl> <dbl> <int>
## 1 4 0 26 91 1
## 2 4 1 26.7 81.8 10
## 3 6 0 20.6 132. 3
## 4 6 1 19.1 115. 4
## 5 8 0 15.1 209. 14
Všimnime si, že výsledné dáta sú uložené v dátovej štruktúre podobnej dátovému rámcu, ktorá sa volá tibble a je súčasťou tidyverse. Oproti data.frame a) pri vzniku tibble sa nikdy nezmení názov ani typ premennej, b) vo výpise sa zobrazujú iba stĺpce, ktoré sa zmestia na obrazovku, niekoľko prvých riadkov a dátový typ stĺpca, c) volanie neexistujúceho stĺpca skončí chybou namiesto výsledku Null, d) subsetovanie [
vždy vráti tibble a [[
vždy vektor, e) pri definovaní stĺpca sa zrecykluje iba vektor dĺžky 1, a nakoniec f) tibble nevytvára ani nepoužíva názvy riadkov.
Na záver si predstavíme jeden veľmi užitočný a návykový nástroj, ktorým sa dá vyhnúť vytvoreniu pomocných/dočasných objektov vo výpočtovom prostredí a celkovo sprehľadňuje zdrojový kód. Je ním pipe operátor %>%
importovaný z balíku magrittr (kde má ešte množstvo rôznych modifikácií) a slúži na reťazenie príkazov podobne ako skladáme funkcie, napr. x %>% f() %>% g()
vykoná to isté ako g(f(x))
.
Vypočítajme napr. priemerný dojazd všetkých automobilov s priamou orientáciou valcov a to podľa typu prevodovky a v jednotkách km/l.
%>%
mtcars filter(vs == 1) %>%
mutate(kmpl = 0.43 * mpg) %>%
group_by(am) %>%
summarize(priemerny_dojazd = mean(kmpl)) %>%
ungroup() # to isté ako .groups = "drop"
## # A tibble: 2 x 2
## am priemerny_dojazd
## * <dbl> <dbl>
## 1 0 8.92
## 2 1 12.2
So základnými nástrojmi R bez použitia dplyr by to vyzeralo napr. takto:
<- subset(mtcars, vs == 1)
tmp aggregate(kmpl ~ am,
data = cbind(tmp, kmpl = 0.43*tmp$mpg),
FUN = function(x) mean(x)
)## am kmpl
## 1 0 8.919429
## 2 1 12.199714
Jednoduchšou (a predvídateľnejšou) alternatívou ku %>%
je pipe operátor %>>%
z balíku pipeR.
Hoci balík dplyr sprístupňuje pipe operátor, tento v ďalších kapitolách bude používaný nezávisle (nebude načítaný ani dplyr ani magrittr) a to pomocou príkazu:
`%>%` <- magrittr::`%>%`
4.3 Cvičenie
- Načítajte data frame Cars93 z balíka MASS.
- Vytvorte nový data frame auta93 výberom všetkých premenných, ktoré spĺňajú aspoň jednu z nasledujúcich podmienok
- prvé tri (použite operátor sekvencie
:
) - ich názov obsahuje “Price” ale neobsahuje “.Price” (contains, operator
-
) - ich názov sa začína na “MPG” (start_with)
- všetky od indikátora airbagov až po výkon motora okrem typu pohonu DriveTrain (operátory
:
,-
) - hmotnosť a pôvod vozidla
- prvé tri (použite operátor sekvencie
- Pipe operátorom vytvorte sekvenciu nasledujúcich príkazov
- auta93
- premenovanie premennej EngineSize na CylindersVolume (rename)
- prevod hmotnosti z libier na kilogramy (mutate)
- výber všetkých amerických automobilov s hmotnosťou do 1200 kg (filter)
- zoradenie primárne podľa kategórie auta Type a v druhom rade podľa ceny vzostupne (arrange)
- vypísanie (print)
- združenie podľa kategórie auta a výpočet priemerneho dojazdu v meste (group_by, summarise).