5.5 Tabulky třídy tibble

Kromě tabulek třídy data.frame existuje v R ještě několik dalších typů tabulek. Nejpohodlnější z nich je třída tibble, která je součástí tidyverse, skupiny balíků určených pro datovou analýzu, kterými se budeme zabývat v pozdějších částech tohoto textu. Tato třída tabulek má některé velmi příjemné vlastnosti, pro které je programování s tabulkami třídy tibble pohodlnější než programování s tabulkami třídy data.frame.

Tabulku třídy tibble vytvoříte pomocí funkce tibble():

library(tibble)
ds <- tibble(x = 1:1e6, y = 2 * x, zed = x / 3 + 1.5 * y - 7)
ds
## # A tibble: 1,000,000 × 3
##        x     y    zed
##    <int> <dbl>  <dbl>
##  1     1     2 -3.67 
##  2     2     4 -0.333
##  3     3     6  3    
##  4     4     8  6.33 
##  5     5    10  9.67 
##  6     6    12 13    
##  7     7    14 16.3  
##  8     8    16 19.7  
##  9     9    18 23    
## 10    10    20 26.3  
## # … with 999,990 more rows

Pokud potřebujete vytvořit tabulku třídy tibble ručně, existuje i příjemná funkce tribble(), která umožňuje zadávat data po řádcích:

tribble(
    ~name, ~weight, ~height,
    "Adam", 68, 193,
    "Bětka", 55, 163,
    "Cyril", 103, 159
)
## # A tibble: 3 × 3
##   name  weight height
##   <chr>  <dbl>  <dbl>
## 1 Adam      68    193
## 2 Bětka     55    163
## 3 Cyril    103    159

Ke konverzi jiných tříd na tibble slouží funkce as_tibble().

Vytvoření tibble se od vytvoření data.frame v několika ohledech liší: tibble

  1. nemění “nepovolená” jména sloupců na povolená nahrazením divných znaků tečkami,
  2. vyhodnocuje své argumenty postupně, takže můžete později zadaný argument použít při tvorbě dříve zadaného argumentu (jako v příkladu výše),
  3. podporuje jen omezenou recyklaci: všechny zadané vektory musejí mít buď stejnou délku, nebo délku 1,
  4. nepoužívá jména řádků (která jsou ostatně nebezpečná k uchovávání dat) a
  5. převod na tibble pomocí as_tibble() je rychlejší než převod na data.frame pomocí as.data.frame().

Liší se také to, jak tibble vypisuje svou hodnotu do konzole. Oproti data.frame zobrazí tibble navíc rozměr tabulky a typ jednotlivých proměnných. Naopak vypíše jen prvních deset řádků a jen takový počet sloupců, které se vejdou na obrazovku. Počet vypsaných řádků je možné ovlivnit ve funkci print() pomocí parametru n; počet sloupců pomocí parametru width, kde width je maximální počet znaků, které může tibble při tisku použít:

print(ds, n = 5)
## # A tibble: 1,000,000 × 3
##       x     y    zed
##   <int> <dbl>  <dbl>
## 1     1     2 -3.67 
## 2     2     4 -0.333
## 3     3     6  3    
## 4     4     8  6.33 
## 5     5    10  9.67 
## # … with 999,995 more rows

Hlavní rozdíl mezi tibble a data.frame se však týká subsetování: tibble má konzistentnější chování a (na rozdíl od data.frame) vrací vždy stejnou datovou strukturu, což je výhodné zejména při programování. tibble nikdy nezahazuje zbytečné rozměry a ani je nepřidává. To znamená, že [] vždy vrací tibble, zatímco [[]] a $ vždy vrací vektor. Navíc tibble nikdy nepodporuje partial matching:

ds <- ds[1:6, ]  # omezíme ds na prvních 6 řádků
ds[, 1]
## # A tibble: 6 × 1
##       x
##   <int>
## 1     1
## 2     2
## 3     3
## 4     4
## 5     5
## 6     6
ds[[1]]
## [1] 1 2 3 4 5 6
ds$z
## Warning: Unknown or uninitialised column: `z`.
## NULL

Tato příjemná konzistence má však i svá nebezpečí. Technicky je tibble (stejně jako data.frame) seznamem vektorů. Přestože jsme zatím vždy uvažovali atomické vektory, ve skutečnosti mohou být sloupci tibble i seznamy. To znamená, že následují kód ošklivě selže, protože do proměnné vloží celou tabulku:

ds <- tibble(a = 1:3, b = 11:13)
ds$c <- ds[, "a"]  # vybere se tibble s jedním sloupcem a vloží do sloupce b
ds
## # A tibble: 3 × 3
##       a     b   c$a
##   <int> <int> <int>
## 1     1    11     1
## 2     2    12     2
## 3     3    13     3
ds$c
## # A tibble: 3 × 1
##       a
##   <int>
## 1     1
## 2     2
## 3     3
class(ds$c)
## [1] "tbl_df"     "tbl"        "data.frame"

Správný postup je opět použít operátor, který z dat vybere jeden sloupec:

ds$d <- ds$a
ds$e <- ds[["a"]]
ds
## # A tibble: 3 × 5
##       a     b   c$a     d     e
##   <int> <int> <int> <int> <int>
## 1     1    11     1     1     1
## 2     2    12     2     2     2
## 3     3    13     3     3     3
class(ds$d)
## [1] "integer"
class(ds$e)
## [1] "integer"

Možnost využívající dvojité hranaté závorky se hodí zejména v případě, kdy máte jméno sloupce uložené v nějaké proměnné, takže je nemůžete zapsat přímo:

name <- "a"
ds$f <- ds[[name]]
ds
## # A tibble: 3 × 6
##       a     b   c$a     d     e     f
##   <int> <int> <int> <int> <int> <int>
## 1     1    11     1     1     1     1
## 2     2    12     2     2     2     2
## 3     3    13     3     3     3     3

Někdy se nehodí pracovat s tibble (např. proto, že některé funkce očekávají jiný výsledek subsetování). V takovém případě můžete tibble převést na data.frame pomocí konverzní funkce as.data.frame():

as.data.frame(ds)
## Warning in format.data.frame(if (omit) x[seq_len(n0), , drop = FALSE] else x, :
## corrupt data frame: columns will be truncated or padded with NAs
##   a  b                 c d e f
## 1 1 11 # A tibble: 3 × 1 1 1 1
## 2 2 12                 a 2 2 2
## 3 3 13             <int> 3 3 3