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 x 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 x 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. nepřevádí řetězce na faktory,
  2. nemění “nepovolená” jména sloupců na povolená nahrazením divných znaků tečkami,
  3. 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),
  4. podporuje jen omezenou recyklaci: všechny zadané vektory musejí mít buď stejnou délku, nebo délku 1,
  5. nepoužívá jména řádků (která jsou ostatně nebezpečná k uchovávání dat) a
  6. 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 x 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 x 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 x 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 x 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 x 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 x 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 x 1 1 1 1
## 2 2 12                 a 2 2 2
## 3 3 13             <int> 3 3 3