10.2 Iterace nad více vektory současně

Někdy potřebujeme iterovat nad více vektory současně. Můžeme např. chtít vytvořit seznam vektorů tisíce gaussovských náhodných čísel, kde každý vektor bude mít jinou střední hodnotu a směrodatnou odchylku. Pomocí funkce map() bychom to mohli udělat např. takto:

m <- 0:5  # požadované střední hodnoty
std <- 1:6  # požadované směrodatné odchylky
z <- map(seq_along(m), ~ rnorm(1000, mean = m[.], sd = std[.]))
str(z)  # struktura výsledného seznamu
## List of 6
##  $ : num [1:1000] 1.255 -1.146 0.174 0.653 -0.645 ...
##  $ : num [1:1000] -1.34 2.6 3.26 3.21 3.11 ...
##  $ : num [1:1000] 0.727 -3.175 2.605 -0.194 5.22 ...
##  $ : num [1:1000] 1.99 6.053 5.739 -0.447 3.676 ...
##  $ : num [1:1000] 2.36 8.22 10.32 2.87 8.65 ...
##  $ : num [1:1000] 3.57 3.994 -2.985 3.475 -0.825 ...
z %>% map_dbl(mean)  # střední hodnoty jednotlivých vektorů v seznamu
## [1] 0.04067476 1.06803204 1.87239087 3.03947365 4.04402197 5.15427863

Balik purrr vsak pro tyto ucely nabizi prijemnejsi funkce. Pro iterace nad dvema vektory zavadi funkci map2(.x, .y, .f, ...) a odpovidajici zjednodusujici funkce map2_lgl(), map2_int() atd. Vsechny tyto funkce berou vektory .x a .y, nad kterymi maji iterovat, jako sve prvni dva parametry. Treti parametr je jmeno iterovane funkce (musi brat aspon dva parametry). Pripadne dalsi parametry jsou predany funkci .f() jako jeji treti a dalsi parametr. Postup vypoctu ukazuje obrazek 10.2.

Funkce `map2(.x, .y, .f)` aplikuje funkci `.f()` na odpovídající prvky vektorů `.x` a `.y` a vrací seznam stejné délky.

Obrázek 10.2: Funkce map2(.x, .y, .f) aplikuje funkci .f() na odpovídající prvky vektorů .x a .y a vrací seznam stejné délky.

Pokud chceme stejně jako výše vytvořit pět náhodných gaussovsky rozdělených vektorů se středními hodnotami 0, 1 atd. a směrodatnými odchylkami 1, 2 atd., můžeme je sestavit takto (všimněte si, že další parametry, zde délka vytvářených vektorů, musejí být uvedeny až za jménem iterované funkce):

z <- map2(0:5, 1:6, rnorm, n = 1000)

Funkce opět umožňuje zadat místo funkce .f() pravostrannou formuli, kterou na funkci sama převede. Zpracovávaný prvek vektoru .x v zadáme jako .x, prvek vektoru .y jako .y. Řekněme tedy, že chce vytvořit tisíc vektorů náhodných čísel s gaussovským rozdělením a různými středními hodnotami a směrodatnými odchylkami, a z těchto vektorů spočítat jejich střední hodnotu. To můžeme udělat takto:

map2_dbl(0:5, 1:6, ~ mean(rnorm(n = 1000, mean = .x, sd = .y)))
## [1] 0.02265401 0.97913754 1.83592220 2.88389993 3.83290792 4.82803478

Pro iterace nad větším počtem vektorů nabízí purrr funkci pmap(.l, .f, ...) a její zjednodušující varianty pmap_lgl() atd., kde .l je buď seznam nebo tabulka vektorů, nad kterými se má iterovat, a .f je buď funkce, která bere příslušný počet parametrů, nebo pravostranná formule, kterou pmap() převede na funkci. Pokud je .f funkce a jednotlivé vektory v .l nejsou pojmenované, pak se předávají do .f podle svého pořadí. Pokud jsou pojmenované, pak se předávají jménem, takže na jejich fyzickém pořadí v .l nezáleží.

Řekněme, že chceme opět vytvořit seznam náhodných výběrů z gaussovského rozdělení. Každý výběr bude mít různý počet prvků, různou střední hodnotu a různou směrodatnou odchylku. Pokud seznam parametrů nepojmenujeme, musíme mít jednotlivé parametry v seznamu v tom pořadí, v jakém je očekává funkce rnorm(), která vygeneruje náhodná čísla:

n <- (1:5) * 100  # počet pozorování je 100, 200, ..., 500
mu <- 0:4  # střední hodnota je 0, 1, ..., 4
sd <- 1:5  # směrodatná odchylka je 1, 2, ..., 5
pars <- list(n, mu, sd)  # nepojmenovaný seznam parametrů v pořadí
z <- pmap(pars, rnorm)
str(z)  # struktura výsledku
## List of 5
##  $ : num [1:100] -1.2816 -0.0233 0.0121 1.6697 1.3462 ...
##  $ : num [1:200] 1.441 -0.792 -1.797 1.405 4.01 ...
##  $ : num [1:300] 0.6211 5.2318 4.3351 2.1335 0.0525 ...
##  $ : num [1:400] 0.975 6.556 -0.528 2.167 10.363 ...
##  $ : num [1:500] 3.886 0.494 5.506 6.795 4.824 ...

Pokud jednotlivé parametry v seznamu pojmenujeme, na jejich pořadí nezáleží, protože se předají jménem:

pars <- list(sd = sd, mean = mu, n = n)  # pojmenovaný seznam parametrů
z <- pmap(pars, rnorm)
str(z)  # struktura výsledku
## List of 5
##  $ : num [1:100] -0.2821 -0.8671 1.5214 0.0409 0.214 ...
##  $ : num [1:200] 1.169 3.212 -0.106 1.796 5.004 ...
##  $ : num [1:300] 11.8 -4.619 0.449 5.663 4.39 ...
##  $ : num [1:400] 8.652 9.772 6.981 0.651 3.099 ...
##  $ : num [1:500] 9.57 11.97 4.37 11.37 9.19 ...

Pohodlnější je však zadat parametry jako tabulku:

pars <- tibble::tibble(sd = sd, mean = mu, n = n)
z <- pmap(pars, rnorm)
str(z)  # struktura výsledku
## List of 5
##  $ : num [1:100] 0.22 -0.976 0.171 -0.999 0.271 ...
##  $ : num [1:200] -2.982 -0.471 2.5 1.522 4.568 ...
##  $ : num [1:300] -1.3142 6.6749 2.9361 -0.0248 -1.5553 ...
##  $ : num [1:400] -2.04 2.88 -2.11 4.6 0.94 ...
##  $ : num [1:500] 2.34 8.17 -3.22 -1.69 -1.52 ...

Pokud místo funkce zadáme .f jako pravostrannou formuli, pak první vektor v seznamu nebo tabulce označíme jako ..1, druhý jako ..2 atd.:

z <- pmap(pars, ~ rnorm(n = ..3, mean = ..2, sd = ..1))
str(z)  # struktura výsledku
## List of 5
##  $ : num [1:100] 0.38 0.901 1.493 0.373 0.474 ...
##  $ : num [1:200] 1.52 3.87 1.55 1.26 -2.27 ...
##  $ : num [1:300] 4.086 3.124 1.257 0.736 6.059 ...
##  $ : num [1:400] 3.513 2.427 0.316 0.674 -1.39 ...
##  $ : num [1:500] 8.666 0.424 1.579 0.664 1.541 ...

Balík purrr implementuje i funkci modify2() a funkce walk2() a pwalk(), které umožňují iterovat vedlejší efekty nad více vektory.