5.4 Tabulky třídy data.frame

Pro analýzu dat jsou nejdůležitější datovou strukturou tabulky. Každý sloupec tabulky může obsahovat proměnné jiného typu, v rámci sloupce však musí být typ stejný. Tím se tabulky liší od atomických matic, které musí mít všechny prvky stejného typu. Obvyklé použití tabulek je takové, že řádky tabulky představují jednotlivá pozorování a sloupce jednotlivé proměnné.

R má několik implementací tabulek. Základní třída tabulek, které se budeme věnovat v tomto oddíle, se nazývá data.frame. Technicky je implementovaná jako seznamy atomických vektorů o stejné délce, které jsou spojené vedle sebe. V příštím oddíle se podíváme na poněkud příjemnější variantu tabulek třídy tibble.

Tabulky třídy data.frame se tvoří pomocí funkce data.frame(). Řekněme, že chceme zaznamenat údaje o subjektech, které se zúčastnily nějakého experimentu. Pro každý subjekt pozorujeme jeho identifikační číslo id, výšku a váhu. Pokud máme čtyři subjekty, můžeme vytvořit tabulku např. takto:

experiment <- data.frame(id = c(1, 2, 3, 41),
                         vyska = c(158, 174, 167, 203),
                         vaha = c(51, 110, 68, 97))
experiment
##   id vyska vaha
## 1  1   158   51
## 2  2   174  110
## 3  3   167   68
## 4 41   203   97

Při zadávání vektorů do tabulek můžeme zadat jejich jména, která pak R vypíše. R samo přidá jména řádků (automaticky jim dá přirozená čísla od 1 do počtu proměnných).

I při konstrukci tabulek R recykluje proměnné, pokud není zadáno dost hodnot. Pokud jsou všechny naše subjekty muži, stačí zadat tuto hodnotu jen jednou – R ji zrecykluje.

experiment <- data.frame(id = c(1, 2, 3, 41),
                         gender = "muž",
                         vyska = c(158, 174, 167, 203),
                         vaha = c(51, 110, 68, 97),
                         zdravy = c(TRUE, TRUE, FALSE, TRUE))
experiment
##   id gender vyska vaha zdravy
## 1  1    muž   158   51   TRUE
## 2  2    muž   174  110   TRUE
## 3  3    muž   167   68  FALSE
## 4 41    muž   203   97   TRUE

Poznamka- Starsi verze R pri zadani dat do tabulek pomoci funkce data.frame() automaticky prevedly vsechny retezce na faktory, viz oddil 6.1. Teto konverzi slo zabranit nastavenim parametru stringsAsFactors = FALSE. Od verze R 4.0 ma parametr stringsAsFactors hodnotu FALSE implicitne. Pokud tedy chceme v novejsim R prevest retezce na faktory, musime nastavit parametr stringsAsFactors na hodnotu TRUE nebo je prevest pomoci funkce factor(), coz je zrejme rozumnejsi, viz oddil 6.1.

Někdy se hodí vytvořit tabulku, která obsahuje všechny možné kombinace hodnot nějakého vektoru. K tomu slouží funkce expand.grid():

expand.grid(x = 1:3, y = factor(c("male", "female")), z = c(TRUE, FALSE))
##    x      y     z
## 1  1   male  TRUE
## 2  2   male  TRUE
## 3  3   male  TRUE
## 4  1 female  TRUE
## 5  2 female  TRUE
## 6  3 female  TRUE
## 7  1   male FALSE
## 8  2   male FALSE
## 9  3   male FALSE
## 10 1 female FALSE
## 11 2 female FALSE
## 12 3 female FALSE

Počet řádků tabulky zjistíme pomocí funkce nrow(), počet sloupců funkcí ncol() nebo length(); funguje i funkce dim():

nrow(experiment)    # počet řádků
## [1] 4
ncol(experiment)    # počet sloupců
## [1] 5
length(experiment)  # počet sloupců
## [1] 5
dim(experiment)     # vektor počtu řádků a sloupců
## [1] 4 5

Tabulky mají standardně tři atributy: class (jméno třídy – data set je totiž objekt), names obsahuje jména sloupců (tj. jednotlivých proměnných) a row.names obsahuje jména jednotlivých řádků (tj. pozorování, implicitně mají hodnoty 1, 2 atd.).

attributes(experiment)
## $names
## [1] "id"     "gender" "vyska"  "vaha"   "zdravy"
## 
## $class
## [1] "data.frame"
## 
## $row.names
## [1] 1 2 3 4

Jména řádků můžete zjistit i změnit pomocí funkcí rownames() a row.names(), jména sloupců pomocí funkcí colnames() nebo names():

colnames(experiment) <- c("id", "sex", "height", "weight", "healthy")
experiment
##   id sex height weight healthy
## 1  1 muž    158     51    TRUE
## 2  2 muž    174    110    TRUE
## 3  3 muž    167     68   FALSE
## 4 41 muž    203     97    TRUE

Jména řádků vypadají na první pohled jako dobrý způsob, jak uložit nějakou identifikaci pozorování, např. id subjektu v experimentu. Nedělejte to! Veškeré informace o pozorování ukládejte přímo do tabulky. Je to filosoficky správnější a i praktičtější: některé funkce, které se používají ke zpracování tabulek, jména řádků odstraní. Stejně tak některé formáty, do kterých se data ukládají, jména řádků nepodporují, takže byste přišli o důležité informace. Naproti tomu jména sloupců (tj. proměnných) jsou bezpečná.

Pozor: R vám dovolí změnit i třídu objektu tím, že přepíšete atribut class (buď pomocí funkce attr() nebo funkce class()). Pak se však budou pro daný objekt volat jiné funkce (metody) a výsledek může být podivný. Nedělejte to, pokud nevíte, co děláte.

Někdy je užitečné moci převést tabulku na matici a matici na tabulku. K převodu tabulky na matici slouží funkce as.matrix() a data.matrix(). První převede všechny sloupce tabulky automatickou konverzí na stejný typ, a pak na matici. Druhá převede logické hodnoty a faktory na celá čísla, řetězce na faktory a ty na celá čísla. Pokud jsou po této konverzi všechny sloupce typu integer, má výsledná matice tento typ, jinak má typ double. Při automatické konverzi můžeme skončit s řetězci, což nemusí být žádoucí; s explicitní konverzí na reálná čísla můžeme řetězce ztratit a faktory mohou být zavádějící (faktory se převedou na čísla jako při konverzi na celé číslo).

as.matrix(experiment)    # použije automatickou konverzi na stejný typ
##      id   sex   height weight healthy
## [1,] " 1" "muž" "158"  " 51"  "TRUE" 
## [2,] " 2" "muž" "174"  "110"  "TRUE" 
## [3,] " 3" "muž" "167"  " 68"  "FALSE"
## [4,] "41" "muž" "203"  " 97"  "TRUE"
data.matrix(experiment)  # použije explicitní konverzi na reálná čísla
##      id sex height weight healthy
## [1,]  1   1    158     51       1
## [2,]  2   1    174    110       1
## [3,]  3   1    167     68       0
## [4,] 41   1    203     97       1

Matici lze převést na tabulku pomocí funkcí as.data.frame() i data.frame(). Pokud má matice pojmenované sloupce, jejich jména jsou v tabulce zachována; v opačném případě je R samo pojmenuje V1, V2 atd nebo X1, X2 atd.

M <- matrix(1:12, nrow = 3)
as.data.frame(M)
##   V1 V2 V3 V4
## 1  1  4  7 10
## 2  2  5  8 11
## 3  3  6  9 12
data.frame(M)
##   X1 X2 X3 X4
## 1  1  4  7 10
## 2  2  5  8 11
## 3  3  6  9 12
colnames(M) <- c("a", "b", "c", "d")
as.data.frame(M)
##   a b c  d
## 1 1 4 7 10
## 2 2 5 8 11
## 3 3 6 9 12
data.frame(M)
##   a b c  d
## 1 1 4 7 10
## 2 2 5 8 11
## 3 3 6 9 12

5.4.1 Subsetování tabulek třídy data.frame

Tabulky jsou “kříženec” mezi seznamy a maticemi, takže je na ně možné je subsetovat jako matice i jako seznamy. Pokud použijete jeden index, pak je indexujete jako seznamy, pokud dva indexy, pak je indexujete jako matice. V prvním případě tedy [ vrátí tabulku, ve druhém může vrátit tabulku (pokud se vybere více sloupců), nebo vektor (pokud se vybere jen jeden sloupec). Dolar i dvojité hranaté závorky vrací jeden sloupec, tj. vektor:

d <- data.frame(x = 1:7,
                y = c(3, 1, NA, 7, 5, 12, NA))
d
##   x  y
## 1 1  3
## 2 2  1
## 3 3 NA
## 4 4  7
## 5 5  5
## 6 6 12
## 7 7 NA
d$x      # vektor x
## [1] 1 2 3 4 5 6 7
d[["x"]] # totéž
## [1] 1 2 3 4 5 6 7
d[[1]]   # totéž
## [1] 1 2 3 4 5 6 7
d["x"]   # tabulka s jediným sloupcem
##   x
## 1 1
## 2 2
## 3 3
## 4 4
## 5 5
## 6 6
## 7 7
d[1]     # opět tabulka s jediným sloupcem
##   x
## 1 1
## 2 2
## 3 3
## 4 4
## 5 5
## 6 6
## 7 7
d[1:2, "x"]              # vektor prvních dvou hodnot z vektoru x
## [1] 1 2
d[1:2, 1]                # totéž
## [1] 1 2
d[1:2, 1, drop = FALSE]  # tabulka složená z prvních dvou hodnot vektoru x
##   x
## 1 1
## 2 2
d[1:2, 1:2]              # tabulka složená z prvních dvou řádků
##   x y
## 1 1 3
## 2 2 1
d[1:2, c("x", "y")]      # tabulka složená z prvních dvou řádků
##   x y
## 1 1 3
## 2 2 1
d[1:2, ]                 # tabulka složená z prvních dvou řádků
##   x y
## 1 1 3
## 2 2 1

Samozřejmě je možné použít i indexování pomocí logických hodnot:

d[d[, "y"] < 7, ]  # výběr řádků, kde je hodnota y menší než 7
##       x  y
## 1     1  3
## 2     2  1
## NA   NA NA
## 5     5  5
## NA.1 NA NA
d[d$y < 7 , ]      # totéž
##       x  y
## 1     1  3
## 2     2  1
## NA   NA NA
## 5     5  5
## NA.1 NA NA

Výběr zachová i řádky, kde je hodnota \(y\) NA. To jde vyřešit např. takto:

# vybíráme pouze prvky, kde y zároveň není NA a zároveň je menší než 7 nebo
d[!is.na(d$y) & d$y < 7, ]
##   x y
## 1 1 3
## 2 2 1
## 5 5 5

K vyřazení neúplných hodnot z tabulky a podobných struktur slouží funkce complete.cases(). V případě tabulky vrací vektor logických hodnot, který je TRUE pro každý řádek tabulky, který má všechny hodnoty známé, a FALSE jinak.

complete.cases(d)
## [1]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE
d[complete.cases(d), ]
##   x  y
## 1 1  3
## 2 2  1
## 4 4  7
## 5 5  5
## 6 6 12

Pro slozitejsi vybery z tabulek existuje funkce subset(). Te vsak nebudeme venovat pozornost, protoze se pozdeji naucite mnohem prijemnejsi a rychlejsi funkce implementovane v baliku dplyr, viz kapitola 16.

Do existující tabulky přidáte novou proměnnou (nový sloupec) tak, že do nové proměnné přidáte hodnoty vektoru:

d$z <- letters[1:nrow(d)]
d
##   x  y z
## 1 1  3 a
## 2 2  1 b
## 3 3 NA c
## 4 4  7 d
## 5 5  5 e
## 6 6 12 f
## 7 7 NA g

Nová proměnná se přidá jako poslední sloupec.

Pokud do existující proměnné přiřadíte hodnotu NULL, vyřadíte tím proměnnou z tabulky:

d$z <- NULL
d
##   x  y
## 1 1  3
## 2 2  1
## 3 3 NA
## 4 4  7
## 5 5  5
## 6 6 12
## 7 7 NA

Jiná možnost, jak vynechat proměnnou nebo změnit jejich pořadí, je využít subsetování sloupců tabulky.

Subsetování jde použít i ke změně pořadí řádků nebo sloupců. Řekněme, že chceme řádky tabulky d seřadit podle proměnné “y” a zároveň vyměnit pořadí sloupců. To můžeme udělat např. takto:

d[order(d$y), 2:1]
##    y x
## 2  1 2
## 1  3 1
## 5  5 5
## 4  7 4
## 6 12 6
## 3 NA 3
## 7 NA 7

Hodnoty NA skončí implicitně na konci (jde změnit ve funkci order()).