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
V tejto kapitole preberieme moderné nástroje na manipuláciu so súbormi takých údajov, ktoré už sú v čistej tabuľkovej forme. (O tom, ako dáta dostať do čistej formy, budeme hovoriť neskôr.) Informácie pochádzajú najmä zo zdrojov (Wickham a Grolemund 2016, kap. 5; Ismay a Kim 2019, kap. 3; Peng 2016, kap. 13).
Základnou dátovou štruktúrou (čiže kontajnerom na údaje) pre ďalšie štatistické spracovanie je dátový rámec (data.frame). V ňom každý riadok predstavuje jedno pozorovanie (meranie, záznam…) a každý stĺpec jednu premennú (veličinu, mieru, vlastnosť, charakteristiku, štatistický znak…). V predošlých kapitolách sme si ukázali základné nástroje na vytváranie a manipuláciu s dátovými rámcami ako napr. výber prvkov ($
, [ ]
, subset), avšak zložitejšie operácie – ako napr. filtrovanie (výber pozorovaní podľa zadaných kritérií), zoraďovanie riadkov či tvorenie súhrnov (agregácia hodnôt premenných) – môžu byť únavné a neprehľadné. Syntax jazyka R totiž nie je veľmi intuitívna. Tieto nevýhody sa snaží odstrániť balík dplyr (Wickham et al. 2022), v ktorom je implementovaná konzistentná „gramatika” (v súlade s názvoslovím databázového jazyka SQL - Structured Query Language) a ktorý je tiež veľmi rýchly. Kľúčovými sú funkcie pre
%>%
(pipe operátor) – ako analógia skladania funkcií v matematike.Balík dplyr 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 v tejto kolekcii zdieľajú niekoľko spoločných vlastností:
Oproti základným nástrojom jazyka R prináša dplyr niekoľko výhod, najmä unifikovanú prácu s dátovými rámcami, jednoduchší výber stĺpcov, špecializáciu, podporu reťazenia a skupín.
Okrem spomínaných obsahuje balík aj množstvo pomocných funkcií, ktoré sú na príkladoch popísané v konkrétnych vignette: opererácie po stĺpcoch, riadkoch, skupinách, nad dvoma tabuľkami, a práca s tzv. window funkciami.
Balík pri načítaní predefinuje niektoré známe funkcie, preto je dobrým zvykom písať funkcie celým menom (aj s príslušnosťou ku balíku), napr. stats::filter()
zavolá funkciu filter z predinštalovaného balíka stats a nie rovnomennú funkciu z balíka dplyr.
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 funkcia select, ktorou sa vyberá podmnožina stĺpcov. V nasledujúcom príklade z datasetu mtcars pre ilustráciu vyberme postupne stĺpce – najprv jednotlivo po mene dojazd a 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í na výber stĺpcov je ešte oveľa viac: ends_with, contains, matches, num_range, all_of, any_of, everything. Spolu s nimi môžme používať známe operátory sekvencie :
, negácie !
, logického súčtu |
, logického súčinu &
a kombinačnú funkciu c()
.
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, a tiež porovnávacie a logické operátory.
Zoradenie riadkov podľa jedného alebo viacerých stĺpcov zabezpečuje funkcia arrange. Prednastavené je vzostupné radenie (angl. ascending). Tu napríklad zoradíme autá primárne podľa počtu valcov zostupne (angl. descending) a sekundárne podľa uloženia valcov vzostupne.
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 funkcie rename používa syntax nové = staré
:
<- 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 je opäť možné použiť viacero pomocných funkcií, napr. recode, if_else, case_when … (pozri nápovedu). V nasledujúcom príklade vytvoríme stĺpec s objemom valcov v metrickej sústave a hneď ho použijeme na vyjadrenie objemu jedného valca:
<- mutate(tmp, # objekt, z ktorého sa vychádza
tmp disp_dm3 = disp_in3 * 16e-3,
disp1cyl_dm3 = disp_dm3 / cyl # stĺpec disp_dm3 už existuje
) head(tmp)
mpg wt_lbs cyl vs disp_in3 hp drat disp_dm3 disp1cyl_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 funkcie summarize. Nasledujúci príkaz vypočíta priemernú hodnotu dojazdu a výkonu.
summarize(tmp, mpg_mean = mean(mpg), hp_mean = mean(hp))
mpg_mean hp_mean
1 20.09062 146.6875
Ak chceme aplikovať jednu alebo viac agregačných funkcií postupne na viacero stĺpcov, pomôžeme si funkciou across:
summarize(mtcars,
across(.cols = c(mpg:hp, -cyl), # výber je podobný ako pri select()
.fns = mean # viac funkcií na aplikovanie sa vloží cez list()
) )
mpg disp hp
1 20.09062 230.7219 146.6875
Funkcia summarize ukáže svoju plnú silu až v kombinácii s funkciou 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" # zruší vnútorné členenie na skupiny
)
# A tibble: 5 × 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 systému tidyverse. Rozdiely oproti data.frame sú také, že
[
vždy vráti tibble a [[
vždy vektor,Na záver si ukážeme reťazenie príkazov pomocou pipe operátora %>%
, ktorým sa dá vyhnúť vytvoreniu pomocných/dočasných objektov vo výpočtovom prostredí, čo výrazne sprehľadňuje zdrojový kód. Princíp je podobný ako pri skladaní matematických funkcií \((g \circ f)(x)\), čiže napr. príkaz x %>% f() %>% g()
vykoná to isté ako g(f(x))
. V prostredí RStudio sa pipe operátor vkladá klávesovou skratkou [Ctrl] + [Shift] + [M].
Pre ilustráciu vypočítajme priemerný dojazd všetkých automobilov s priamou orientáciou valcov a to podľa typu prevodovky a v jednotkách km/l:
%>% # východiskový dátový objekt
mtcars filter(vs == 1) %>% # ponechaj iba riadky so straight engine
mutate(kmpl = 0.43 * mpg) %>% # pridaj stĺpec dojazdu v iných jednotkách
group_by(am) %>% # zoskup podľa typu prevodovky
summarize(priemerny_dojazd = mean(kmpl)) # vypočítaj priemer
# A tibble: 2 × 2
am priemerny_dojazd
<dbl> <dbl>
1 0 8.92
2 1 12.2
So základnými nástrojmi R bez použitia nástrojov balíka 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
Operátor %>%
je v skutočnosti importovaný z balíku magrittr, v ktorom sú definované aj ďalšie príbuzné operátory, napr. %T%
posunie argument funkcii, ale nepočká na jej odpoveď (slúži napríklad na vykreslenie), alebo %$%
nevloží objekt do prvého argumentu, len ho sprístupní, ďalej operátor %<>%
východiskový objekt prepíše výsledkom reťaze príkazov.
library(magrittr)
<- data.frame(A = 2, B = 3)
x # použitie bodky ako zástupného symbolu (za vstup)
%>% cbind(C = sum(.)) x
A B C
1 2 3 5
# krútené zátvorky spolu s `.` zastupujú anonymnú funkciu
%>% { c(D = .$A ^ .$B) } x
D
8
# sprístupňuje prvky vstupného objeku po mene
%$% c(C = A + B) x
C
5
# použitie operátora napr. pre zobrazenie medzivýsledku
%T>% plot() %>% cbind(C = 4) x
A B C
1 2 3 4
# východzí objekt `x` prepíše výsledkom reťaze
%<>% cbind(C = sum(.))
x x
A B C
1 2 3 5
detach("package:magrittr")
V roku 2021 – ako odpoveď na obrovskú popularitu pipe operátora z magritttr – bol do štandardných knižníc jazyka R implementovaný jednoduchý pipe operátor |>
. Kvôli spôsobu jeho definície sú jeho možnosti zatiaľ pomerne obmedzené, neumožňuje napr. viacnásobné použitie zástupného znaku pre vstupný objekt (_
)1, to sa musí obísť pomocou anonymnej funkcie. Menej elegantné je tiež použitie funkcií odvodených od bežných aritmetických operátorov (napr. +
), ktoré musia byť uzavreté do opačne zošikmených apostrofov aj zátvoriek.
# nasledujúci príkaz nefunguje, kým reťaz `2 %>% '+'(3)` áno
2 |> '+'(3)
Error in +3: function '+' not supported in RHS call of a pipe (<input>:2:6)
2 |> (`+`)(3)
[1] 5
# použitie anonymnej funkcie
2 |> (function(x) x + 3)()
[1] 5
# `\(x)` je skrátený zápis konštrukcie `function(x)`
|> (\(df) df$A + df$B)() x
[1] 5
# zástupný znak `_` musí byť umiestnený adresne
2 |> log(8, base = _)
[1] 3
# zástupný znak sa dá použiť aj na výber časti objektu pomocou [, [[, $, @
0:4 |> _[2:3]
[1] 1 2
Hoci balík dplyr sprístupňuje pipe operátor %>%
, tento v ďalších kapitolách bude importovaný nezávisle (pretože nebude načítaný ani balík dplyr ani magrittr), a to pomocou príkazu:
`%>%` <- magrittr::`%>%`
Väčšinou si však vystačíme so vstavaným operátorom. V nastaveniach prostredia RStudio sa mu dá priradiť rovnaká klávesová skratka ako predvolenému pipe operátoru.
:
),-
),:
,-
),Na pozadí je totiž príkaz x |> f()
priamo prepísaný na f(x)
. Ak by vstupoval do viacerých argumentov, musel by sa vykonať viackrát.↩︎