Co budeme dělat

  • atomické vektory
  • atomické matice
  • seznamy
  • datasety
  • indexování a subsetting
  • (tibble)

Základní datové struktury

Základní datové struktury podle dvou charakteristik:

  • podle jejich dimensionality (1D, 2D, 3D, …)
  • zda jsou všechny jejich prvky stejného datového typu (struktura je homogenní)
dimenze homogenní heterogenní
1 atomický vektor seznam
2 matice dataset
více pole \(\strut\)

Atomické vektory

Nejzákladnější datovou strukturou je v R atomický vektor (R nemá skalár – skalár je atomický vektor s jediným prvkem).

Atomický vektor je vektor hodnot, jehož všechny prvky mají stejný typ (např. celé číslo).

Atomické vektory se vytvářejí funkcí c() (od "concatenate"), která "slepí" jednotlivé hodnoty dohromady, přičemž provede automatickou konverzi (pokud je potřeba).

x <- c(1, 2, 3, 17)
print(x)
## [1]  1  2  3 17

Funkce c()

Pomocí funkce c() je možné "slepit" i celé vektory:

x1 <- c(1, 2, 3)
x2 <- c(4, 5, 6, NA)
x <- c(x1, x2)
print(x)
## [1]  1  2  3  4  5  6 NA

Všechny prvky atomického vektoru musejí mít stejný typ. Pokud tedy při tvorbě atomického vektoru smícháte proměnné různých typů, dojde k automatické konverzi:

c(TRUE, 1, "ahoj")  # výsledek je vektor tří řetězců
## [1] "TRUE" "1"    "ahoj"

Funkce c(): pojmenované prvky (1)

Jednotlivé prvky atomických vektorů mohou mít jména – atribut names. Je možné je zadat ve funkci c(), pomocí funkce attr() nebo pomocí funkce names():

x <- c(a = 1, "good parameter" = 7, c = 17)
x
##              a good parameter              c 
##              1              7             17
attr(x, "names") <- c("A", "Good Parameter", "C")
x
##              A Good Parameter              C 
##              1              7             17

Funkce c(): pojmenované prvky (2)

names(x) <- c("aa", "bb", "cc")
names(x)
## [1] "aa" "bb" "cc"
x
## aa bb cc 
##  1  7 17

Délka vektoru

Délku vektoru je možné zjistit pomocí funkce length():

x
## aa bb cc 
##  1  7 17
length(x)
## [1] 3

Vektor může mít nulovou délku

Atomický vektor (i další datové struktury) mohou mít v R nulové rozměry (nulovou délku, nulový počet řádků apod.):

x <- 1:9
y <- x[x > 10]  # vybereme prvky větší než 10, viz dále
length(y)
## [1] 0
y
## integer(0)

Konstrukce prázdného numerického vektoru:

z <- numeric(0)  # parametr je délka vektoru

K procvičení

Vytvořte vektory:

  • logický v1 = (T, F, T)
  • reálný v2 = (1, 2, 3)

Spojte je dohromady: v = (v1, v2).

Je vektor v atomický? Pokud ano, jaký má typ?

Jakou má vektor v délku?

Sekvence (1)

1:10   # vektor celých čísel 1 až 10
##  [1]  1  2  3  4  5  6  7  8  9 10
10:1   # vektor celých čísel sestupně 10 až 1
##  [1] 10  9  8  7  6  5  4  3  2  1
1:0    # pozor!
## [1] 1 0

Problematické:

for (k in 1:length(x)) udělej_něco_s(x[k])

Sekvence (2)

seq(from = 1, to = 10, by = 3)  # s daným krokem
## [1]  1  4  7 10
seq(from = 1, to = 10, length.out = 4)  # s danou délkou výsledku
## [1]  1  4  7 10
seq_along(c(1, 3, 17, 31))  # celá čísla od 1 do délky zadaného vektoru
## [1] 1 2 3 4
seq_len(7)  # celá čísla od 1 nahoru se zadanou nezápornou délkou
## [1] 1 2 3 4 5 6 7

Sekvence (3)

# opakování hodnot ve vektoru
rep(c(1, 3), times = 5)  # celý vektor 5 krát
##  [1] 1 3 1 3 1 3 1 3 1 3
rep_len(c(1, 3), length.out = 5)  # celý vektor do délky 5
## [1] 1 3 1 3 1
rep(c(1, 3), each = 3)  # každý prvek 3 krát
## [1] 1 1 1 3 3 3
rep(1:6, each = 2, times = 3)
##  [1] 1 1 2 2 3 3 4 4 5 5 6 6 1 1 2 2 3 3 4 4 5 5 6 6 1 1 2 2 3 3 4 4 5 5 6
## [36] 6

K procvičení

Vytvořte vektory

  • (10, 9, 8, 7, …, -3)
  • (0, 0.01, 0.02, 0.03, …, 1)
  • vektor od 0 do 1 s ekvidistantním krokem a délkou 7
  • (1, 2, 3, 1, 2, 3, …) – sekvence 1, 2, 3 se opakuje \(10\times\)

Výpis vektorů na obrazovku

Jednotlivé prvky se skládají vedle sebe do řádku. Pokud se všechny hodnoty na řádek nevejdou, začne se vypisovat na dalším řádku.

1:31
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
## [24] 24 25 26 27 28 29 30 31

I když se vektory vypisují na obrazovku po řádcích, nejsou ani řádkové (ani sloupcové).

Vektorové operace

Veškeré aritmetické a logické operace se na vektorech provádí po prvcích:

x <- 1:6
y <- 2:7
x * y
## [1]  2  6 12 20 30 42
x ^ 2
## [1]  1  4  9 16 25 36

Vektorové operace: recyklace

Pokud se délka vektorů liší, pak R automaticky "recykluje" kratší vektor. (R vydá varování jen v případě, že délka delšího vektoru není celočíselným násobkem délky kratšího vektoru):

x <- 1:2
y <- 1:6
x + y
## [1] 2 4 4 6 6 8
y <- 1:7
x + y
## Warning in x + y: longer object length is not a multiple of shorter object
## length
## [1] 2 4 4 6 6 8 8

Test na atomický vektor

K otestování, zda je proměnná atomický vektor slouží funkce is.atomic():

is.atomic(x)

Funkce is.vector() vrací logickou hodnotu TRUE pro atomický vektor i pro neatomický vektor (seznam):

is.atomic(list(1, TRUE, "ahoj"))
## [1] FALSE
is.vector(list(1, TRUE, "ahoj"))
## [1] TRUE

Atomické matice

Atomická matice je matice (tj. dvourozměrná tabulka), jejíž všechny prvky mají stejný datový typ (např. celé číslo).

Lze vytvořit pomocí funkce matrix():

matrix(1:9, nrow = 3)
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9

Atomické matice (2)

Data se recyklují:

matrix(1:9, nrow = 3, ncol = 4, byrow = TRUE)
## Warning in matrix(1:9, nrow = 3, ncol = 4, byrow = TRUE): data length [9]
## is not a sub-multiple or multiple of the number of columns [4]
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9    1    2    3

Testování a konverze

Otestovat, zda je objekt matice, je možné pomocí funkce is.matrix().

Převést data na matici je možné pomocí konverzní funkce as.matrix().

Rozměry matice (1)

m <- matrix(1:12, nrow = 3)
m
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
nrow(m)
## [1] 3
ncol(m)
## [1] 4

Rozměry matice (2)

m
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
dim(m)
## [1] 3 4
length(m) # délka podkladového vektoru
## [1] 12

(Pro vektory vrací nrow(), ncol() a dim() hodnotu NULL.)

Skládání vektorů a matic (1)

Matice a podobné objekty je možné skládat pomocí funkcí rbind() a cbind():

A <- matrix(1:12, nrow = 3)
B <- matrix(101:112, nrow = 3)
rbind(A, B)  # položí dvě matice pod sebe
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
## [4,]  101  104  107  110
## [5,]  102  105  108  111
## [6,]  103  106  109  112

Skládání vektorů a matic (2)

cbind(A, B) # položí dvě matice vedle sebe
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]    1    4    7   10  101  104  107  110
## [2,]    2    5    8   11  102  105  108  111
## [3,]    3    6    9   12  103  106  109  112

Skládání vektorů a matic (3)

Vektory se složí do matic:

x1 <- 1:3
x2 <- 4:6
rbind(x1, x2) # pod sebe jako řádkové vektory
##    [,1] [,2] [,3]
## x1    1    2    3
## x2    4    5    6
cbind(x1, x2) # vedle sebe jako sloupcové vektory
##      x1 x2
## [1,]  1  4
## [2,]  2  5
## [3,]  3  6

K procvičení

Vytvořte

  • jednotkovou matici M1 \(3 \times 3\)
  • nulovou matici M0 \(3 \times 3\)

Z obou matic složte blokovou matici tak, že výsledkem bude bloková matice M \(6 \times 6\):

\[\left(\begin{matrix} M1 & M0 \\ M0 & M1 \end{matrix}\right) = \left(\begin{matrix} 1 & 1 & 1 & 0 & 0 & 0 \\ 1 & 1 & 1 & 0 & 0 & 0 \\ 1 & 1 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 1 & 1 \\ 0 & 0 & 0 & 1 & 1 & 1 \\ 0 & 0 & 0 & 1 & 1 & 1 \end{matrix}\right)\]

Atributy matic (1)

Matice mohou mít následující atributy:

  • dim je celočíselný vektor rozměrů
  • rownames jsou jména řádků
  • colnames jsou jména sloupců
  • dimnames jména dimenzí, řádků a sloupců
A <- matrix(1:12, nrow = 3)
rownames(A) <- c("a", "b", "c")
colnames(A) <- c("alpha", "beta", "gamma", "delta")
A
##   alpha beta gamma delta
## a     1    4     7    10
## b     2    5     8    11
## c     3    6     9    12

Atributy matic (2)

A <- matrix(1:12, nrow = 3)
dimnames(A) <- list(id = c("A", "B", "C"),
                    variables = c("Alpha", "Beta", "Gamma", "Delta"))
A
##    variables
## id  Alpha Beta Gamma Delta
##   A     1    4     7    10
##   B     2    5     8    11
##   C     3    6     9    12
attributes(A)
## $dim
## [1] 3 4
## 
## $dimnames
## $dimnames$id
## [1] "A" "B" "C"
## 
## $dimnames$variables
## [1] "Alpha" "Beta"  "Gamma" "Delta"

Atributy matic (3)

Atomická matice je implementována jako atomický vektor, který má přiřazený atribut dim, který je celočíselný vektor délky dva:

M <- 1:12
M
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12
dim(M) <- c(3, 4)
M
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12

Atributy matic (4)

is.matrix(M)
## [1] TRUE

Zrušením atributu dim převést matici zpět na vektor (matice se vektorizuje po sloupcích):

dim(M) <- NULL
M
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12

Matice je atomická, ale ne vektor

M <- matrix(1:12, nrow = 3)
is.atomic(M)
## [1] TRUE
is.vector(M)
## [1] FALSE

Maticová aritmetika

  • násobení po prvcích (*)
  • dělení po prvcích (/)
  • umocňování po prvcích (^)
  • maticové násobení (%*%)
  • inverzní matice (funkce solve())
  • diagonála (funkce diag())
  • transpozice (funkce t())

Rozměry matic přitom musejí být správné.

Atomická pole

Atomické pole je vícerozměrná tabulka (tabulka, která má víc než dva rozměry), jehož všechny prvky mají stejný datový typ.

array(1:27, dim = c(2, 2, 2))
## , , 1
## 
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
## 
## , , 2
## 
##      [,1] [,2]
## [1,]    5    7
## [2,]    6    8

Neatomické vektory (seznamy)

Neatomické vektory (seznamy) jsou vektory, jejichž jednotlivé prvky mohou mít různé datové typy:

l <- list(1:3, "ahoj", list(1, "bum"))
l
## [[1]]
## [1] 1 2 3
## 
## [[2]]
## [1] "ahoj"
## 
## [[3]]
## [[3]][[1]]
## [1] 1
## 
## [[3]][[2]]
## [1] "bum"

Seznamy s pojmenovanými prvky

Seznamy mohou mít atribut names:

l <- list(a = 1, b = "ahoj", c = 1:3, d = list(1:3, "ahoj"))
names(l)
## [1] "a" "b" "c" "d"
l
## $a
## [1] 1
## 
## $b
## [1] "ahoj"
## 
## $c
## [1] 1 2 3
## 
## $d
## $d[[1]]
## [1] 1 2 3
## 
## $d[[2]]
## [1] "ahoj"

Délka seznamu

Délku seznamu zjistíme pomocí funkce length():

length(l)
## [1] 4

Test na vektor

K otestování, zda je proměnná seznam, slouží funkce is.list(l).

is.list(l)
## [1] TRUE
is.vector(l)
## [1] TRUE

Pozor: Funkce is.vector() vrací hodnotu TRUE i pro seznamy.

K procvičení

Vytvořte atomický vektor v a seznam l, do kterých umístíte pojmenované položky:

  • a = TRUE
  • b = 1
  • c = 1:3
  • d = "ahoj"

Jak se v a l liší?

Datasety

Dataset je nejdůležitější datová struktura pro analýzu dat.

Dataset je tabulka, jejichž řádky představují jednotlivá pozorování a sloupce jednotlivé proměnné.

Technicky se jedná o seznam atomických vektorů o stejné délce, které jsou spojené vedle sebe. Každý vektor (sloupec tabulky) může obsahovat proměnné jiného typu.

Dataset: příklad

Chceme zaznamenat údaje o subjektech experimentu. Pro každý subjekt pozorujeme jeho id, výšku a váhu:

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 datasetu 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 pozorování).

Rozměry datasetu

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

Dataset: recykluje proměnné

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),
                         stringsAsFactors = FALSE)
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

Při zadání dat do dataset pomocí funkce data.frame() R převede všechny řetězce na faktory. Této konverzi zabrání parametr stringsAsFactors = FALSE.

Atributy datasetů (1)

Datasety mají standardně tři atributy:

  • class (jméno třídy – data set je totiž objekt)
  • names jména sloupců (tj. jednotlivých proměnných)
  • row.names obsahuje jména jednotlivých řádků
attributes(experiment)
## $names
## [1] "id"     "gender" "vyska"  "vaha"   "zdravy"
## 
## $row.names
## [1] 1 2 3 4
## 
## $class
## [1] "data.frame"

Atributy datasetů (2)

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ů nepoužívejte.

Třídu objektu neměňte.

Převod datasetu na matici

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
## Warning in data.matrix(experiment): NAs introduced by coercion
##      id sex height weight healthy
## [1,]  1  NA    158     51       1
## [2,]  2  NA    174    110       1
## [3,]  3  NA    167     68       0
## [4,] 41  NA    203     97       1

Převod matice na dataset

Matici lze převést na dataset pomocí funkcí as.data.frame() i data.frame(). Pokud má matice pojmenované sloupce, jejich jména jsou v datasetu 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)
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

K procvičení

Vytvořte dataset pupils, který bude obsahovat

  • jména tří žáků (Karel, Luboš, Pepa)
  • jejich IQ (78, 131, 103)
  • jejich známky z dějepisu (E, B, A)

Jména by měla být uložená jako řetězce, známky jako faktor s hodnotami "A" až "F".

Dataset všech kombinací hodnot

Někdy se hodí vytvořit dataset, který obsahuje všechny možné kombinace hodnot nějakého vektoru:

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

Zjištění obsahu datové strukury (1)

Ke zjištění struktury a obsahu datové struktury slouží funkce str():

n <- 1:12
str(n)
##  int [1:12] 1 2 3 4 5 6 7 8 9 10 ...
l <- list(a = 1, b = 1:3, "ahoj", d = list(1, 1:2, TRUE))
str(l)
## List of 4
##  $ a: num 1
##  $ b: int [1:3] 1 2 3
##  $  : chr "ahoj"
##  $ d:List of 3
##   ..$ : num 1
##   ..$ : int [1:2] 1 2
##   ..$ : logi TRUE

Zjištění obsahu datové strukury (2)

d <- data.frame(experiment)
str(d)
## 'data.frame':    4 obs. of  5 variables:
##  $ id     : num  1 2 3 41
##  $ sex    : chr  "muž" "muž" "muž" "muž"
##  $ height : num  158 174 167 203
##  $ weight : num  51 110 68 97
##  $ healthy: logi  TRUE TRUE FALSE TRUE

Výběry částí datových struktur

Chceme vybrat jen vybrané prvky datové struktury.

K základnímu subsetování slouží hranaté závorky ([]). V nich se určí indexy prvků, které je třeba vybrat:

  • pomocí svých indexů
  • pomocí svých jmen
  • pomocí logických hodnot

Hranaté závorky vrací objekt stejné třídy, jako je původní objekt.

Operátor dvojitých hranatých závorek ([[]]), který extrahuje prvky ze seznamů, datasetů a podobných struktur. Podobnou funkci plní i operátor dolar ($).

Subsetování lze použít nejen k získání vybraných prvků z datové struktury, ale také k jejich nahrazení nebo doplnění.

Výběr pomocí číselných indexů (1)

Prvky jsou číslované přirozenými čísly \(1,\ldots,N\).

  • výběr vybraných prvků (kladné indexy)
  • výběr kromě vynechaných prvků (záporné indexy)

Indexování pomocí kladných a záporných čísel nelze míchat. Index 0 se tiše ignoruje.

# vektor letters obsahuje 26 malých písmen anglické abecedy
x <- letters[1:12]  # prvních dvanáct písmen abecedy
x
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l"

Výběr pomocí číselných indexů (2)

x
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l"
x[1]  # první prvek
## [1] "a"
x[3]  # třetí prvek
## [1] "c"
x[length(x)]  # poslední prvek
## [1] "l"

Výběr pomocí číselných indexů (3)

x[3:6]  # třetí až šestý prvek včetně
## [1] "c" "d" "e" "f"
x[c(2, 3, 7)]  # druhý, třetí a sedmý prvek
## [1] "b" "c" "g"
x[c(-1, -3)]  # vynechají se první a třetí prvek
##  [1] "b" "d" "e" "f" "g" "h" "i" "j" "k" "l"

Výběr pomocí jmen prvků

Pokud mají prvky jména, je možné vybírat pomocí vektoru jejich jmen (minus nefunguje – R  nemá záporné řetězce):

x <- c(c = 1, b = 2, a = 3)
x
## c b a 
## 1 2 3
x["a"]  # prvek s názvem a, tj. zde poslední prvek
## a 
## 3
x[c("b", "c")]  # prvky s názvy b a c
## b c 
## 2 1

Výběr pomocí logických hodnot (1)

R vybere prvky, které jsou indexovány logickou hodnotou TRUE a vynechá ostatní.

Pokud je logický vektor kratší než subsetovaný vektor, pak se recykluje!

x <- 1:12
x
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12
x[c(TRUE, TRUE, FALSE)]
## [1]  1  2  4  5  7  8 10 11

Výběr pomocí logických hodnot (2)

Výběr pomocí logických hodnot je užitečný zejména v situaci, kdy chceme vybrat prvky, které splňují nějakou podmínku:

x[x > 3 & x < 11]  # vybere prvky větších než 3 a menších než 11
## [1]  4  5  6  7  8  9 10
x[x < 3 | x > 11]  # vybere prvky menší než 3 nebo větší než 11
## [1]  1  2 12

Výběr k nahrazení prvků (1)

Subsetování lze využít k nahrazení prvků jednoduše tak, že se do výběru uloží nová hodnota, která nahradí starou:

x <- c(1:3, NA, 5:7)
x
## [1]  1  2  3 NA  5  6  7
x[7] <- Inf  # nahrazení poslední hodnoty nekonečnem
x
## [1]   1   2   3  NA   5   6 Inf

Výběr k nahrazení prvků (2)

x[is.na(x)] <- 0  # nahrazení všech hodnot NA nulou
x
## [1]   1   2   3   0   5   6 Inf
x[length(x) + 1] <- 8  # přidání nové hodnoty za konec vektoru
x
## [1]   1   2   3   0   5   6 Inf   8

Pozor: postupné rozšiřování datových struktur vždy o několik málo prvků je výpočetně velmi neefektivní. Lepší je naráz alokovat velký blok paměti, do něj postupně uložit hodnoty a blok na konci případně zkrátit, viz lecture notes.

K procvičení

Vytvořte vektor v čísel 1 až 26 a prvky pojmenujte (pomocí names() a letters malými písmeny anglické abecedy).

Vyberte

  • první a poslední prvek
  • všechny prvky s hodnotou nejméně 10 a nejvýše 20
  • prvky se jmény "b", "c" a "e"
  • každý druhý prvek pomocí kladných indexů
  • každý druhý prvek pomocí záporných indexů

K procvičení (pokrač.)

Nahraďte

  • pět prvních a pět posledních prvků hodnotou NA
  • všechny hodnoty NA hodnotou 0

Vynechte z vektoru všechny nulové hodnoty.

Subsetování matic (1)

Matice se subsetují pomocí dvou indexů: první index vybírá řádky, druhý sloupce:

M <- matrix(1:12, nrow = 3)
M
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
M[2, 3]  # prvek ve druhém řádku a třetím sloupci
## [1] 8

Subsetování matic (2)

M[1:2, c(1,4)]  # prvky na prvních dvou řádcích a v prvním a čtvrtém sloupci
##      [,1] [,2]
## [1,]    1   10
## [2,]    2   11
M[-1, -1]  # matice bez prvního řádku a sloupce
##      [,1] [,2] [,3]
## [1,]    5    8   11
## [2,]    6    9   12

Subsetování matic (3)

Pokud je jeden z indexů prázdný, vybírá celý řádek nebo sloupec:

M[1:2, ]  # celé první dva řádky
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
M[, c(1, 3)]  # první a třetí sloupec
##      [,1] [,2]
## [1,]    1    7
## [2,]    2    8
## [3,]    3    9

Subsetování matic (4)

M[M[, 1] >= 2, ]  # všechny řádky, ve kterých je prvek v prvním sloupci >= 2
##      [,1] [,2] [,3] [,4]
## [1,]    2    5    8   11
## [2,]    3    6    9   12
M[M[, 1] >= 2, M[1, ] < 6]  # submatice
##      [,1] [,2]
## [1,]    2    5
## [2,]    3    6

Subsetování matic (5)

M[ , ]  # celá matice M
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12

Subsetování matic: konverze na vektor

Pokud se matice indexuje jen jedním indexem, R ji tiše převede na jeden vektor (spojí sloupce matice za sebe) a vybere prvky z takto vzniklého vektoru:

M[4]  # vrací 1. prvek ve 2. sloupci, protože je to 4. prvek vektoru
## [1] 4
M[c(1, 4:7)]
## [1] 1 4 5 6 7

Změna pořadí hodnot (1)

Subsetování může měnit pořadí prvků.

Funkce order() vrací indexy uspořádané podle velikosti původního vektoru. Umožňuje setřídit hodnoty všech sloupců matice podle jednoho sloupce:

M <- matrix(c(8, 5, 7, 2, 3, 11, 6, 12, 1, 4, 9, 10), nrow = 3)
M
##      [,1] [,2] [,3] [,4]
## [1,]    8    2    6    4
## [2,]    5    3   12    9
## [3,]    7   11    1   10

Změna pořadí hodnot (1)

# indexy prvků 1. sloupce matice M seřazené podle velikosti prvků,
# tj. na 1. místě je 2. prvek původního vektoru (5), pak 3. prvek (7) atd.
order(M[, 1]) 
## [1] 2 3 1
M[order(M[, 1]), ]  # řádky matice seřazené podle prvního sloupce
##      [,1] [,2] [,3] [,4]
## [1,]    5    3   12    9
## [2,]    7   11    1   10
## [3,]    8    2    6    4

Výběr náhodných hodnot

Funkce sample() náhodně permutuje zadaná čísla. Lze jí tak mimo jiné využít k náhodné permutaci sloupců matice:

o <- sample(ncol(M))  # čísla 1:ncol(M) v náhodném pořadí
o
## [1] 1 2 3 4
M[, o]
##      [,1] [,2] [,3] [,4]
## [1,]    8    2    6    4
## [2,]    5    3   12    9
## [3,]    7   11    1   10

Redukce rozměrů

Subsetování se vždy snaží snížit rozměry matice – pokud počet řádků nebo sloupců klesne na 1, matice se změní ve vektor. Pokud tomu chceme zabránit, je třeba přidat parametr drop = FALSE.

M[, 1]
## [1] 8 5 7
M[, 1, drop = FALSE]
##      [,1]
## [1,]    8
## [2,]    5
## [3,]    7

K procvičení

Z vektoru 1:16 vytvořte matici M \(4 \times 4\).

Vyberte submatici, která obsahuje

  • první řádek (jako vektor)
  • první sloupec (jako vektor)
  • první sloupec (jako matici)
  • druhý a třetí řádek a druhý a třetí sloupec

Změňte pořadí sloupců tak, že hodnoty v prvním řádků budou uspořádané od nejvyšší po nejnižší.

Nahraďte hodnoty ve druhém řádku hodnotami NA.

Subsetování seznamů (1)

Subsetování pomocí hranatých závorek zachovává mód proměnné:

  • v případě atomických vektorů a matic je výsledkem atomický vektor nebo matice
  • v případě seznamů je výsledkem subsetování je opět seznam, který obsahuje dané prvky, nikoli prvek jako takový

Subsetování seznamů (2)

l <- list(a = 1, b = 1:3, c = "ahoj")
l
## $a
## [1] 1
## 
## $b
## [1] 1 2 3
## 
## $c
## [1] "ahoj"

Subsetování seznamů (3)

l[1]
## $a
## [1] 1
is.list(l[1])
## [1] TRUE
l[1:2]
## $a
## [1] 1
## 
## $b
## [1] 1 2 3

Subsetování seznamů (4)

Pokud chceme získat vlastní prvek seznamu, musíme použít dvojité hranaté závorky:

l[[2]]
## [1] 1 2 3
is.list(l[[2]])
## [1] FALSE
is.numeric(l[[2]])
## [1] TRUE

Subsetování seznamů (5)

Pokud je argumentem vektor, nevrací dvojité hranaté závorky vektor hodnot, nýbrž se vektor přeloží na rekurentní volání dvojitých hranatých závorek:

l[[2]][[3]]  # třetí prvek vektoru, který je druhým prvkem seznamu
## [1] 3
l[[2:3]]     # totéž
## [1] 3
l[[2]][3]    # v tomto případě totéž
## [1] 3

Subsetování seznamů (6)

Pokud jsou prvky seznamu pojmenované, nabízí R zkratku ke dvojitým hranatým závorkám: operátor dolar ($): l[["b"]] je totéž jako l$b:

l[["b"]]  # prvek se jménem b
## [1] 1 2 3
l$b       # totéž (uvozovky se zde neuvádějí)
## [1] 1 2 3

Subsetování seznamů (7)

Použití dolaru od dvojitých závorek v jednom ohledu liší: pokud máme jméno prvku, který chceme získat uloženo v proměnné, je třeba použít hranaté závorky – operátor dolar zde nelze použít:

element <- "c"
# element není v uvozovkách, protože vybíráme hodnotu,
# která je v něm uložená:
l[[element]]
## [1] "ahoj"

Subsetování seznamů (8)

Pokud indexujeme jménem prvek seznamu, který v seznamu chybí, dostaneme hodnotu NULL. Pokud jej však indexujeme číselným indexem, dostaneme chybu:

l[[4]]

Error in l[[4]] : subscript out of bounds

l[["d"]]
## NULL
l$d
## NULL

Subsetování seznamů (9)

Seznamy umožňují "partial matching":

l <- list(prvni_prvek = 1, druhy_prevk = 2)
l$p  # s dolarem je zapnutý
## [1] 1
l[["p"]]  # s hranatými závorkami vypnutý
## NULL
l[["p", exact = FALSE]]  # ale jde zapnout
## [1] 1

Partial matching nepoužívejte – je zdrojem špatně dohledatelných chyb!

K procvičení

Vytvořte seznam l s prvky a = 1, b = 1:3 a c = letters.

Získejte

  • seznam, který obsahuje druhý prvek seznamu l
  • seznam, který obsahuje první a třetí prvek seznamu l
  • vektor, který je druhým prvkem seznamu l
  • vektor c pomocí závorek
  • vektor c pomocí dolaru
    1. až 7. prvek vektoru c

Nahraďte 5. až 7. prvek vektoru c v seznamu l mezerami.

Subsetování datasetů (1)

Datasety 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 seznam (po sloupcích) a [ vrátí dataset.

Pokud použijete dva indexy, pak je indexujete jako matice a [ vrátí

  • dataset (pokud se vybere více sloupců)
  • vektor (pokud se vybere jen jeden sloupec)

Dolar vrací jeden sloupec, tj. vektor.

Subsetování datasetů (2)

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

Subsetování datasetů (3)

Výsledek je vektor:

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

Subsetování datasetů (4)

Výsledek je dataset s jedním sloupcem:

d["x"]   # dataset s jediným sloupcem
##   x
## 1 1
## 2 2
## 3 3
## 4 4
## 5 5
## 6 6
## 7 7
d[1]     # opět dataset s jediným sloupcem
##   x
## 1 1
## 2 2
## 3 3
## 4 4
## 5 5
## 6 6
## 7 7

Subsetování datasetů (5)

Výsledek je vektor, ledaže bychom zakázali redukci rozměrů:

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]  # dataset složený z prvních dvou hodnot vektoru x
##   x
## 1 1
## 2 2

Subsetování datasetů (6)

Výsledek je dataset:

d[1:2, 1:2]              # dataset složený z prvních dvou řádků
##   x y
## 1 1 3
## 2 2 1
d[1:2, c("x", "y")]      # dataset složený z prvních dvou řádků
##   x y
## 1 1 3
## 2 2 1
d[1:2, ]                 # dataset složený z prvních dvou řádků
##   x y
## 1 1 3
## 2 2 1

Subsetování datasetů (7)

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

Subsetování datasetů (8)

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

Vyřazení neúplných hodnot z datasetu

K vyřazení neúplných hodnot z datasetu a podobných struktur slouží funkce complete.cases(). V případě datasetu vrací vektor logických hodnot, který je TRUE pro každý řádek datasetu, 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

Přidání proměnné do datasetu

Do existujícího datasetu 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.

Odstranění proměnné z datasetu

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

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ů datasetu.

K procvičení

Vytvořte dataset pupils, který bude obsahovat

  • jména tří žáků (Karel, Luboš, Pepa)
  • jejich IQ (78, 131, 103)
  • jejich známky z dějepisu (E, B, A)

Vyberte z něj:

  • vektor IQ studentů
  • dataset studentů, kteří mají nadprůměrné IQ vyšší než 100

Přidejte do datasetu sloupec smart s logickou hodnotou, která bude TRUE u studentů s IQ vyšší než 100 a FALSE u ostatních.

Upoutávka: dplyr

Později se podíváme na balík dplyr, který (mimo jiné) výrazně zjednodušuje subsetování datasetů, tvorbu nových proměnných a mnoho jiných kouzel.

Tibble

Kromě data.frame existuje v ještě několik typů datasetů.

Později budete potřebovat ještě typ tibble, do kterého převádí datasety balíky dplyr a tidyr.

Tento typ datasetu má některé velmi příjemné vlastnosti.

Detailní popis této třídy najdete zde:

Vytvoření tibble (1)

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.6666667
## 2      2     4 -0.3333333
## 3      3     6  3.0000000
## 4      4     8  6.3333333
## 5      5    10  9.6666667
## 6      6    12 13.0000000
## 7      7    14 16.3333333
## 8      8    16 19.6666667
## 9      9    18 23.0000000
## 10    10    20 26.3333333
## # ... with 999,990 more rows

Vytvoření tibble (2)

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

Ke konverzi tibble na data.frame slouží funkce as.data.frame().

Vytvoření tibble (3)

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. nepoužívá jména řádků (která jsou ostatně nebezpečná k uchovávání dat)
  5. převod na tibble pomocí as_tibble() je rychlejší než převod na data.frame pomocí as.data.frame()

Výpis tibble

Oproti data.frame zobrazí tibble navíc rozměr datasetu 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 bud ve funkci print(), nebo v options(), viz viněta:

print(ds, n = 5)
## # A tibble: 1,000,000 × 3
##       x     y        zed
##   <int> <dbl>      <dbl>
## 1     1     2 -3.6666667
## 2     2     4 -0.3333333
## 3     3     6  3.0000000
## 4     4     8  6.3333333
## 5     5    10  9.6666667
## # ... with 1e+06 more rows

Subsetování tibble (1)

Při subsetování tibble nikdy nezahazuje zbytečné rozměry a ani je nepřidává, tj. [] 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

Subsetování tibble (2)

ds[[1]]
## [1] 1 2 3 4 5 6
ds$z
## Warning: Unknown column 'z'
## NULL

Volba datové struktury

Vstupní data budou většinou dataset. Vámi zpracovaná data můžete umístit do jakékoli struktury. Tuto strukturu byste si měli dopředu pořádně rozmyslet.

Ve většině případů doporučuji používat datasety:

  • snadno se ukládají, čtou a převádí do jiného software
  • snadno se z nich dělají výběry
  • snadno se transformují
  • snadno se vizualizují jako tabulky nebo grafy
  • R má mnoho balíků pro práci s datasety (zejména tidyr, dplyr a ggplot2)

Práce s ostatními datovými strukturami je mnohem méně standardizovaná, takže si víc kódu budete muset napsat sami.

Domácí úkol

Máte dvě matice se záznamy ze dvou lékařských studií. Obě matice mají stejný formát. Vašimi úkoly je:

  1. obě matice spojit a převést na data.frame
  2. nastavit jména sloupců datasetu
  3. změnit pořadí sloupců
  4. sloupec s výškou a váhou převedete z řetězce na reálná čísla
  5. přidáte nový sloupec s BMI jako poslední sloupec
  6. všechna pozorování, kde je BMI vyšší roven 30
  7. výsledek setřídíte podle jména podle abecedy vzestupně
  8. výsledek setřídíte podle BMI sestupně
  9. do proměnné num_obese uložíte počet obézních lidí
  10. spočítáte medián BMI všech lidí v původním vzorku dat