16.2 Tvorba a úprava obsahu

Balík dplyr obsahuje dvě základní funkce pro vytváření a agregaci obsahu v tabulkách: mutate() a summarise()

16.2.1 Tvorba nových sloupců s mutate()

Funkce mutate() vytváří nové sloupce, proměnné, v tabulce. Zachovává tedy počet řádků v tabulce a přidává nové sloupce. Syntax mutate() je podobně jako u dalších funkcí z tidyverse poměrně střídmá:

mutate(.data, ...)

Funkce přijímá vstupní tabulku a specifikaci sloupců, které se mají vytvořit v .... Fungování mutate() může být ilustrováno následujícím (mírně zjednodušujícím) schématem:

Tvorba nových sloupců s mutate()

mutate() může být použito i pro modifikaci stávajících sloupců. V tomto případě však mutate() interně nejprve vytvoří nový sloupec a až následně jím nahradí sloupec původní. Při modifikaci sloupce na opravdu velkých tabulkách tak může mutate() spotřebovávat nečekané množství systémových zdrojů.

Praktické využití mutate() je možné ilustrovat na příkladu. Například můžeme chtít pro každé pozorování (řádek, letadlo) v tabulce planes spočítat, kolik sedadel připadá na jeden motor a zjistit, zda se jedná o vrtulové letadlo:

planes %>% 
    mutate(
        seats_per_engine = (seats/engines) %>% round(),
        turbo_prop_plane = engine == "Turbo-prop"
    ) #%>% 
## # A tibble: 3,322 × 11
##    tailnum  year type          manufacturer   model  engines seats speed engine 
##    <chr>   <int> <chr>         <chr>          <chr>    <int> <int> <int> <chr>  
##  1 N10156   2004 Fixed wing m… EMBRAER        EMB-1…       2    55    NA Turbo-…
##  2 N102UW   1998 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  3 N103US   1999 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  4 N104UW   1999 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  5 N10575   2002 Fixed wing m… EMBRAER        EMB-1…       2    55    NA Turbo-…
##  6 N105UW   1999 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  7 N107US   1999 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  8 N108UW   1999 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  9 N109UW   1999 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
## 10 N110UW   1999 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
## # … with 3,312 more rows, and 2 more variables: seats_per_engine <dbl>,
## #   turbo_prop_plane <lgl>
    #select(seats_per_engine, turbo_prop_plane, everything())

mutate() vytvořilo dva nové sloupce. Sloupec seats_per_engine obsahuje zaokrouhlený počet sedadel na motor. Za povšimnutí stojí způsob, jakým byl jeho výpočet ve funkci mutate() specifikován. Na levé straně je jméno nově vytvářeného sloupce. Na pravé straně od “=” je postup, který se má použít pro vytvoření jejího obsahu. Jména sloupců z tabulky se přitom používají jako proměnné. Příklad také ukazuje, že v mutate() je možné používat komplikované výrazy včetně trubek %>%.

V jednom volání mutate() je možné vytvořit více nových sloupců. Jednotlivé specifikace jsou ve volání odděleny čárkou. Druhý vytvořený sloupec ukazuje příklad vytvoření logické proměnné. Ohledně typu zpracovávaných nebo výsledných proměnných nemá mutate() žádné omezení.

mutate() přidává nově vytvořené sloupce na konec tabulky. Proto je v příkladu použita funkce select(), která je přesunuje na začátek tabulky. Verze 1.0.0 umožňuje nastavit, kde se v tabulce nové sloupce vytvoří, nicméně tato funkcionalita je stále ve fázi vývoje.

U popisu fungování mutate() je výše zmíněná možnost modifikace stávajících sloupců. V praxi se taková operace provede jednoduše. Předpokládejme, že chceme sloupec year nahradit jeho vlastním logaritmem:

planes %>% 
    mutate(
        year = log(year)
    )
## # A tibble: 3,322 × 9
##    tailnum  year type          manufacturer   model  engines seats speed engine 
##    <chr>   <dbl> <chr>         <chr>          <chr>    <int> <int> <int> <chr>  
##  1 N10156   7.60 Fixed wing m… EMBRAER        EMB-1…       2    55    NA Turbo-…
##  2 N102UW   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  3 N103US   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  4 N104UW   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  5 N10575   7.60 Fixed wing m… EMBRAER        EMB-1…       2    55    NA Turbo-…
##  6 N105UW   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  7 N107US   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  8 N108UW   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
##  9 N109UW   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
## 10 N110UW   7.60 Fixed wing m… AIRBUS INDUST… A320-…       2   182    NA Turbo-…
## # … with 3,312 more rows

Pokud jméno nového sloupce odpovídá některému sloupci, který již je v tabulce obsažen, je tento novým sloupcem nahrazen.

mutate() umí pracovat i s proměnnými, které nejsou součástí tabulky. V následujícím případě je nově vytvořený sloupec this_is_true naplněn konstantou přiřazenou do proměnné x.

x <- TRUE

planes %>% 
    mutate(
        this_is_true = x
    ) %>% 
    select(this_is_true, everything())
## # A tibble: 3,322 × 10
##    this_is_true tailnum  year type       manufacturer  model engines seats speed
##    <lgl>        <chr>   <int> <chr>      <chr>         <chr>   <int> <int> <int>
##  1 TRUE         N10156   2004 Fixed win… EMBRAER       EMB-…       2    55    NA
##  2 TRUE         N102UW   1998 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  3 TRUE         N103US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  4 TRUE         N104UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  5 TRUE         N10575   2002 Fixed win… EMBRAER       EMB-…       2    55    NA
##  6 TRUE         N105UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  7 TRUE         N107US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  8 TRUE         N108UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  9 TRUE         N109UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## 10 TRUE         N110UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## # … with 3,312 more rows, and 1 more variable: engine <chr>

Stejného výsledku by bylo dosaženo, kdyby byla konstanta definována přímo v mutate() tj. this_is_true = TRUE.

Na proměnnou, která takto do mutate() vstupuje zvnějšku je uvaleno omezení: musí mít délku jedna, nebo délku odpovídající počtu řádků tabulky. Tato podmínka není v následujícím příkladu splněna (vektor x má délku 3):

x <- c(TRUE, TRUE, TRUE)

planes %>% 
    mutate(
        this_is_true = x
    ) %>% 
    select(this_is_true, everything())
## Error: Problem with `mutate()` column `this_is_true`.
## ℹ `this_is_true = x`.
## ℹ `this_is_true` must be size 3322 or 1, not 3.

Pokud má vektor x délku 1, potom je tato jedna hodnota přiřazena ke každému řádku. Pokud je délka x právě rovna počtu řádků, potom je ke každému řádku přiřazena hodnota na odpovídající pozici:

x <- 1:nrow(planes)

planes %>% 
    mutate(
        new_variable = x
    ) %>% 
    select(new_variable, everything())
## # A tibble: 3,322 × 10
##    new_variable tailnum  year type       manufacturer  model engines seats speed
##           <int> <chr>   <int> <chr>      <chr>         <chr>   <int> <int> <int>
##  1            1 N10156   2004 Fixed win… EMBRAER       EMB-…       2    55    NA
##  2            2 N102UW   1998 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  3            3 N103US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  4            4 N104UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  5            5 N10575   2002 Fixed win… EMBRAER       EMB-…       2    55    NA
##  6            6 N105UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  7            7 N107US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  8            8 N108UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  9            9 N109UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## 10           10 N110UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## # … with 3,312 more rows, and 1 more variable: engine <chr>

Naprosto stejná pravidla platí pro funkce. V příkladu je použita funkce rnorm(n), která vrací n výběrů z normálního rozdělení. První dva příklady jsou vyhodnoceny bez problémů. Poslední je nekorektní a skončí chybou, protože rnorm(3) vrací vektor o délce 3.

planes %>% 
    mutate(
        new_variable = rnorm(1)
    ) %>% 
    select(new_variable, everything())
## # A tibble: 3,322 × 10
##    new_variable tailnum  year type       manufacturer  model engines seats speed
##           <dbl> <chr>   <int> <chr>      <chr>         <chr>   <int> <int> <int>
##  1        0.328 N10156   2004 Fixed win… EMBRAER       EMB-…       2    55    NA
##  2        0.328 N102UW   1998 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  3        0.328 N103US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  4        0.328 N104UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  5        0.328 N10575   2002 Fixed win… EMBRAER       EMB-…       2    55    NA
##  6        0.328 N105UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  7        0.328 N107US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  8        0.328 N108UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  9        0.328 N109UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## 10        0.328 N110UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## # … with 3,312 more rows, and 1 more variable: engine <chr>
planes %>% 
    mutate(
        new_variable = rnorm(nrow(planes))
    ) %>% 
    select(new_variable, everything())
## # A tibble: 3,322 × 10
##    new_variable tailnum  year type       manufacturer  model engines seats speed
##           <dbl> <chr>   <int> <chr>      <chr>         <chr>   <int> <int> <int>
##  1       3.02   N10156   2004 Fixed win… EMBRAER       EMB-…       2    55    NA
##  2      -0.147  N102UW   1998 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  3      -0.544  N103US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  4      -1.02   N104UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  5       0.0999 N10575   2002 Fixed win… EMBRAER       EMB-…       2    55    NA
##  6       0.563  N105UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  7       0.876  N107US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  8      -0.130  N108UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  9      -0.619  N109UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## 10       1.05   N110UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## # … with 3,312 more rows, and 1 more variable: engine <chr>
planes %>% 
    mutate(
        new_variable = rnorm(3)
    ) %>% 
    select(new_variable, everything())
## Error: Problem with `mutate()` column `new_variable`.
## ℹ `new_variable = rnorm(3)`.
## ℹ `new_variable` must be size 3322 or 1, not 3.

16.2.1.1 Úskalí mutate()

Výše byl použit příklad, ve kterém byla při stanovení hodnoty použita proměnná definovaná mimo tabulku. Při troše smůly se může stát, že jméno této proměnné se bude shodovat se jménem některého sloupce. V souladu s logikou R dostane přednost obsah sloupce:

tailnum <- TRUE

planes %>% 
    mutate(
        this_is_true = tailnum
    ) %>% 
    select(this_is_true, everything())
## # A tibble: 3,322 × 10
##    this_is_true tailnum  year type       manufacturer  model engines seats speed
##    <chr>        <chr>   <int> <chr>      <chr>         <chr>   <int> <int> <int>
##  1 N10156       N10156   2004 Fixed win… EMBRAER       EMB-…       2    55    NA
##  2 N102UW       N102UW   1998 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  3 N103US       N103US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  4 N104UW       N104UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  5 N10575       N10575   2002 Fixed win… EMBRAER       EMB-…       2    55    NA
##  6 N105UW       N105UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  7 N107US       N107US   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  8 N108UW       N108UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
##  9 N109UW       N109UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## 10 N110UW       N110UW   1999 Fixed win… AIRBUS INDUS… A320…       2   182    NA
## # … with 3,312 more rows, and 1 more variable: engine <chr>

Další úskalí v použití spočívá v tom, že mutate() pracuje nad celou tabulkou a ne nad jednotlivými řádky. Toto chování lze změnit pomocí vhodného zgrupování, ale je potřeba ho mít na paměti. Co to znamená v praxi:

planes %>% 
    mutate(
        mean_year = mean(year, na.rm = TRUE)
    ) %>% 
    select(mean_year, everything())
## # A tibble: 3,322 × 10
##    mean_year tailnum  year type    manufacturer model engines seats speed engine
##        <dbl> <chr>   <int> <chr>   <chr>        <chr>   <int> <int> <int> <chr> 
##  1     2000. N10156   2004 Fixed … EMBRAER      EMB-…       2    55    NA Turbo…
##  2     2000. N102UW   1998 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
##  3     2000. N103US   1999 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
##  4     2000. N104UW   1999 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
##  5     2000. N10575   2002 Fixed … EMBRAER      EMB-…       2    55    NA Turbo…
##  6     2000. N105UW   1999 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
##  7     2000. N107US   1999 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
##  8     2000. N108UW   1999 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
##  9     2000. N109UW   1999 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
## 10     2000. N110UW   1999 Fixed … AIRBUS INDU… A320…       2   182    NA Turbo…
## # … with 3,312 more rows

mutate() v tomto případě vypočítal průměrnou hodnotu ze všech roků a tu přiřadil ke všem sloupcům. Opět je to dáno tím, že mean neprodukuje vektor o délce odpovídající počtu řádků, ale vektor o délce 1.

16.2.2 Agregace proměnných se summarise()

Podstatou agregace je shrnutí obsahu tabulky (jednoho nebo více sloupců) a vytvoření nové tabulky, která obsahuje tyto agregované hodnoty (typicky statistiky jako průměr, medián, minimum, atp.).

Agregace obsahu tabulky se summarise()

Pro tyto účely slouží v dplyr funkce summarise(). Její použití se v logice velmi podobá mutate(). To ilustruje následující příklad:

planes %>% 
    summarise(
        min_year = min(year, na.rm = TRUE),
        max_year = max(year, na.rm = TRUE),
        min_engines = min(engines, na.rm = TRUE),
        max_engines = max(engines, na.rm = TRUE)
    )
## # A tibble: 1 × 4
##   min_year max_year min_engines max_engines
##      <int>    <int>       <int>       <int>
## 1     1956     2013           1           4

V tomto volání funkce summarise() jsou vytvořeny 4 agregované hodnoty: maxima a minima ze sloupců year a engines. Výsledkem je tabulka, která podle stanovených pravidel shrnuje celou tabulku do jediného řádku.