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 ...
## [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.
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):
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:
## [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.:
## 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.