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. 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"
    ) %>% 
    select(seats_per_engine, turbo_prop_plane, everything())
## # A tibble: 3,322 x 11
##    seats_per_engine turbo_prop_plane tailnum  year type  manufacturer model
##               <dbl> <lgl>            <chr>   <int> <chr> <chr>        <chr>
##  1               28 FALSE            N10156   2004 Fixe… EMBRAER      EMB-…
##  2               91 FALSE            N102UW   1998 Fixe… AIRBUS INDU… A320…
##  3               91 FALSE            N103US   1999 Fixe… AIRBUS INDU… A320…
##  4               91 FALSE            N104UW   1999 Fixe… AIRBUS INDU… A320…
##  5               28 FALSE            N10575   2002 Fixe… EMBRAER      EMB-…
##  6               91 FALSE            N105UW   1999 Fixe… AIRBUS INDU… A320…
##  7               91 FALSE            N107US   1999 Fixe… AIRBUS INDU… A320…
##  8               91 FALSE            N108UW   1999 Fixe… AIRBUS INDU… A320…
##  9               91 FALSE            N109UW   1999 Fixe… AIRBUS INDU… A320…
## 10               91 FALSE            N110UW   1999 Fixe… AIRBUS INDU… A320…
## # … with 3,312 more rows, and 4 more variables: engines <int>, seats <int>,
## #   speed <int>, engine <chr>

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.

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 x 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 x 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 Fixe… EMBRAER      EMB-…       2    55    NA
##  2 TRUE         N102UW   1998 Fixe… AIRBUS INDU… A320…       2   182    NA
##  3 TRUE         N103US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  4 TRUE         N104UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  5 TRUE         N10575   2002 Fixe… EMBRAER      EMB-…       2    55    NA
##  6 TRUE         N105UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  7 TRUE         N107US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  8 TRUE         N108UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  9 TRUE         N109UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
## 10 TRUE         N110UW   1999 Fixe… AIRBUS INDU… 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()` input `this_is_true`.
## x Input `this_is_true` can't be recycled to size 3322.
## ℹ Input `this_is_true` is `x`.
## ℹ Input `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 x 10
##    new_variable tailnum  year type  manufacturer model engines seats speed
##           <int> <chr>   <int> <chr> <chr>        <chr>   <int> <int> <int>
##  1            1 N10156   2004 Fixe… EMBRAER      EMB-…       2    55    NA
##  2            2 N102UW   1998 Fixe… AIRBUS INDU… A320…       2   182    NA
##  3            3 N103US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  4            4 N104UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  5            5 N10575   2002 Fixe… EMBRAER      EMB-…       2    55    NA
##  6            6 N105UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  7            7 N107US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  8            8 N108UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  9            9 N109UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
## 10           10 N110UW   1999 Fixe… AIRBUS INDU… 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 x 10
##    new_variable tailnum  year type  manufacturer model engines seats speed
##           <dbl> <chr>   <int> <chr> <chr>        <chr>   <int> <int> <int>
##  1        0.442 N10156   2004 Fixe… EMBRAER      EMB-…       2    55    NA
##  2        0.442 N102UW   1998 Fixe… AIRBUS INDU… A320…       2   182    NA
##  3        0.442 N103US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  4        0.442 N104UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  5        0.442 N10575   2002 Fixe… EMBRAER      EMB-…       2    55    NA
##  6        0.442 N105UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  7        0.442 N107US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  8        0.442 N108UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  9        0.442 N109UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
## 10        0.442 N110UW   1999 Fixe… AIRBUS INDU… 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 x 10
##    new_variable tailnum  year type  manufacturer model engines seats speed
##           <dbl> <chr>   <int> <chr> <chr>        <chr>   <int> <int> <int>
##  1      -0.170  N10156   2004 Fixe… EMBRAER      EMB-…       2    55    NA
##  2      -0.320  N102UW   1998 Fixe… AIRBUS INDU… A320…       2   182    NA
##  3       3.80   N103US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  4      -1.01   N104UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  5      -0.489  N10575   2002 Fixe… EMBRAER      EMB-…       2    55    NA
##  6      -0.0148 N105UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  7      -2.09   N107US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  8      -1.37   N108UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  9      -2.13   N109UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
## 10       0.548  N110UW   1999 Fixe… AIRBUS INDU… 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()` input `new_variable`.
## x Input `new_variable` can't be recycled to size 3322.
## ℹ Input `new_variable` is `rnorm(3)`.
## ℹ Input `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 x 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 Fixe… EMBRAER      EMB-…       2    55    NA
##  2 N102UW       N102UW   1998 Fixe… AIRBUS INDU… A320…       2   182    NA
##  3 N103US       N103US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  4 N104UW       N104UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  5 N10575       N10575   2002 Fixe… EMBRAER      EMB-…       2    55    NA
##  6 N105UW       N105UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  7 N107US       N107US   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  8 N108UW       N108UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
##  9 N109UW       N109UW   1999 Fixe… AIRBUS INDU… A320…       2   182    NA
## 10 N110UW       N110UW   1999 Fixe… AIRBUS INDU… 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 x 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.1.2 Varinaty mutate()

dplyr obsahuje mutace mutate(): mutate_if(), mutate_at() a mutate_all(). Jejich použití je podobné jako v případě mutací select(). V principu umožňují transformovat všechny sloupce (_all), vyjmenované sloupce (_at) nebo ty, které splňují určitou podmínku (_if).

V následujícím příkladu je obsah všech textových proměnných transformován do malých písmen:

planes %>% 
    mutate_if(is.character,tolower)
## # A tibble: 3,322 x 9
##    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

Funkce mutate() má ještě jednu mutaci: transmute() a její sestry transmute_if(), transmute_at() a transmute_all(). transmute() poskytuje v podstatě stejnou funkcionalitu jako mutate(), ale s jedním rozdílem. mutate() mění pouze vytvářené sloupce a zbytek tabulky nechává v původní podobě. To neplatí pro transmute(). transmute() ve výstupní tabulce ponechá jen nově vytvořené sloupce (a sloupce použité ke grupování – viz dále):

planes %>% 
    transmute(
        seats_per_engine = (seats/engines) %>% round(),
        turbo_prop_plane = engine == "Turbo-prop"
    ) 
## # A tibble: 3,322 x 2
##    seats_per_engine turbo_prop_plane
##               <dbl> <lgl>           
##  1               28 FALSE           
##  2               91 FALSE           
##  3               91 FALSE           
##  4               91 FALSE           
##  5               28 FALSE           
##  6               91 FALSE           
##  7               91 FALSE           
##  8               91 FALSE           
##  9               91 FALSE           
## 10               91 FALSE           
## # … with 3,312 more rows

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 x 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.

16.2.2.1 Varianty summarise()

dplyr obsahuje mutace summarise(): summarise_if(), summarise_at() a summarise_all(). Jejich použití je podobné jako v případě mutací mutate(). V principu umožňují transformovat všechny sloupce (_all), vyjmenované sloupce (_at) nebo ty, které splňují určitou podmínku (_if).

Užitečné může být například vypočítat průměr ze všech numerických proměnných:

planes %>% 
    summarise_if(is.numeric, mean, na.rm = TRUE)
## # A tibble: 1 x 4
##    year engines seats speed
##   <dbl>   <dbl> <dbl> <dbl>
## 1 2000.    2.00  154.  237.