16.4 Zgrupované operace: slovesa na steroidech
Všechny předchozí funkce lze s různou mírou elegance nahradit funkcemi ze základního R. Grupované operace však lze nahradit jen obtížně a v žádném případě ne elegantně. Podstatou zgrupované operace je vyhodnocení funkce nad jednotlivými segmenty tabulky. Na obrázku je zgrupovaná operace provedena funkce summarise()
:
summarise()
je vykonáno nad jednotlivými barvenými grupami. Výsledky za jednotlivé grupy jsou následně složeny do nové tabulky.
V praktickém nasazení nás například může zajímat minimální, maximální a průměrný počet sedadel v letadlech jednotlivých výrobců.
V prvním kroku je potřeba pomocí funkce group_by()
vytvořit grupování. Následně je možné volat funkci summarise()
:
planes %>%
group_by(manufacturer) %>%
summarise(
min_seats = min(seats, na.rm = TRUE),
mean_seats = mean(seats, na.rm = TRUE),
max_seats = max(seats, na.rm = TRUE)
)
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 35 x 4
## manufacturer min_seats mean_seats max_seats
## <chr> <int> <dbl> <int>
## 1 AGUSTA SPA 8 8 8
## 2 AIRBUS 100 221. 379
## 3 AIRBUS INDUSTRIE 145 187. 379
## 4 AMERICAN AIRCRAFT INC 2 2 2
## 5 AVIAT AIRCRAFT INC 2 2 2
## 6 AVIONS MARCEL DASSAULT 12 12 12
## 7 BARKER JACK L 2 2 2
## 8 BEECH 9 9.5 10
## 9 BELL 5 8 11
## 10 BOEING 100 175. 450
## # … with 25 more rows
Protože nás zajímají počty sedadel v letadlech “jednotlivých výrobců” je pro zgrupování použita proměnná manufacturer
. group_by
však umí vytvořit i grupy tvořené kombinací více proměnných. Například by bylo možné zjistit počty sedadel pro skupinu vymezenou výrobcem a typem letounu:
planes %>%
group_by(manufacturer, type) %>%
summarise(
min_seats = min(seats, na.rm = TRUE),
mean_seats = mean(seats, na.rm = TRUE),
max_seats = max(seats, na.rm = TRUE)
)
## `summarise()` regrouping output by 'manufacturer' (override with `.groups` argument)
## # A tibble: 37 x 5
## # Groups: manufacturer [35]
## manufacturer type min_seats mean_seats max_seats
## <chr> <chr> <int> <dbl> <int>
## 1 AGUSTA SPA Rotorcraft 8 8 8
## 2 AIRBUS Fixed wing multi engine 100 221. 379
## 3 AIRBUS INDUSTRIE Fixed wing multi engine 145 187. 379
## 4 AMERICAN AIRCRAFT INC Fixed wing single engi… 2 2 2
## 5 AVIAT AIRCRAFT INC Fixed wing single engi… 2 2 2
## 6 AVIONS MARCEL DASSAULT Fixed wing multi engine 12 12 12
## 7 BARKER JACK L Fixed wing single engi… 2 2 2
## 8 BEECH Fixed wing multi engine 9 9.5 10
## 9 BELL Rotorcraft 5 8 11
## 10 BOEING Fixed wing multi engine 100 175. 450
## # … with 27 more rows
V group_by()
je možné použít proměnné všech typů (jakkoliv u double to asi příliš často nedává smysl).
Grupované operace pochopitelně nejsou omezeny pouze na summarise()
. Následující příklad ukazuje použití s mutate()
. Jako cvičení můžete kód analyzovat a zjistit, co dělá.
planes %>%
group_by(manufacturer) %>%
mutate(
year_diff = year - mean(year, na.rm = TRUE)
) %>%
select(tailnum, manufacturer, year, year_diff) %>%
arrange(manufacturer, year)
## # A tibble: 3,322 x 4
## # Groups: manufacturer [35]
## tailnum manufacturer year year_diff
## <chr> <chr> <int> <dbl>
## 1 N365AA AGUSTA SPA 2001 0
## 2 N186US AIRBUS 2002 -5.20
## 3 N187US AIRBUS 2002 -5.20
## 4 N188US AIRBUS 2002 -5.20
## 5 N338NB AIRBUS 2002 -5.20
## 6 N339NB AIRBUS 2002 -5.20
## 7 N340NB AIRBUS 2002 -5.20
## 8 N341NB AIRBUS 2002 -5.20
## 9 N342NB AIRBUS 2002 -5.20
## 10 N343NB AIRBUS 2002 -5.20
## # … with 3,312 more rows
V prvním kroku kód přidá zgrupování k tabulce planes. Výsledek této operace můžeme vidět pomocí funkce class()
:
## [1] "grouped_df" "tbl_df" "tbl" "data.frame"
Třída tabulky planes
byla rozšířena o grouped_df
. To umožní kompatibilním metodám nakládat s tabulkou speciálním způsobem: provést operaci zgrupovaně. Pokud pro danou funkci není “zgrupovaná” metoda dostupná, provede se funkce jako obvykle nad celou tabulkou:
## tailnum year type manufacturer
## Length:3322 Min. :1956 Length:3322 Length:3322
## Class :character 1st Qu.:1997 Class :character Class :character
## Mode :character Median :2001 Mode :character Mode :character
## Mean :2000
## 3rd Qu.:2005
## Max. :2013
## NA's :70
## model engines seats speed
## Length:3322 Min. :1.000 Min. : 2.0 Min. : 90.0
## Class :character 1st Qu.:2.000 1st Qu.:140.0 1st Qu.:107.5
## Mode :character Median :2.000 Median :149.0 Median :162.0
## Mean :1.995 Mean :154.3 Mean :236.8
## 3rd Qu.:2.000 3rd Qu.:182.0 3rd Qu.:432.0
## Max. :4.000 Max. :450.0 Max. :432.0
## NA's :3299
## engine
## Length:3322
## Class :character
## Mode :character
##
##
##
##
Třída grouped_df()
zůstává u tabulky zachována, dokud není jinou funkcí odstraněna. dplyr umožňuje grupování odstranit funkcí ungroup()
:
## [1] "tbl_df" "tbl" "data.frame"
Podobně však funguje například i as.data.frame()
:
## [1] "data.frame"
Ten však odstraní nejen zgrupování, ale i ztibblování. Výsledkem je tak prostý data.frame
.
V dalším kroku příkladu je volána funkce mutate()
. Ta zvlášť pro každou grupu vypočítá průměrný rok výroby (mean(year, na.rm = TRUE)
) a tuto hodnotu (vektor o délce 1) odečte od všech hodnot v proměnné year
. Výsledkem je tak vektor o délce identické s počtem řádků v grupě. Tento vektor je přidán jako sloupec year_diff
. Tabulka je následně zpřehledněna voláním select()
a mutate()
.
16.4.1 Varianty group_by()
dplyr obsahuje i speciální varianty group_by()
vedle tzv. scoped variant group_by_at()
, group_by_if
a group_by_all()
obsahuje zejména rowwise()
. rowwise()
vytváří skupiny o jednom řádku. Je ho možné použít pro obejití omezení některých funkcí. Pokud je to možné, je rozumné se použití rowwise()
vyhnout. Jeho použití typicky velmi zpomaluje celý kód.
16.4.2 Bezpečné grupování
Zgrupované operace představují mimořádně mocný nástroj, který zásadně zjednodušuje a zpřehledňuje datovou analýzu. Mají však svá rizika a to zejména mezi židlí a klávesnicí. Pokud uživatel zapomene, že ve skutečnosti pracuje se zgrupovanou tabulkou může dostat bez varování zásadně odlišné výsledky. Proto je rozumné tabulku “na konci trubky” odgrupovat.
16.4.3 Pohled do zpětného zrcátka: grupování a tidyr
Balíčky v tidyverse jsou velmi provázány. I funkce z jiných balíků umí pracovat se zgrupovanými tabulkami. Příkladem může být nest()
z tidyr, která má metodu pro zgrupovanou tabulku:
## # A tibble: 35 x 2
## # Groups: manufacturer [35]
## manufacturer data
## <chr> <list>
## 1 EMBRAER <tibble [299 × 8]>
## 2 AIRBUS INDUSTRIE <tibble [400 × 8]>
## 3 BOEING <tibble [1,630 × 8]>
## 4 AIRBUS <tibble [336 × 8]>
## 5 BOMBARDIER INC <tibble [368 × 8]>
## 6 CESSNA <tibble [9 × 8]>
## 7 JOHN G HESS <tibble [1 × 8]>
## 8 GULFSTREAM AEROSPACE <tibble [2 × 8]>
## 9 SIKORSKY <tibble [1 × 8]>
## 10 PIPER <tibble [5 × 8]>
## # … with 25 more rows