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:
<- 0:5 # požadované střední hodnoty
m <- 1:6 # požadované směrodatné odchylky
std <- map(seq_along(m), ~ rnorm(1000, mean = m[.], sd = std[.]))
z str(z) # struktura výsledného seznamu
## List of 6
## $ : num [1:1000] -0.7373 -0.3658 0.9595 -0.0311 0.31 ...
## $ : num [1:1000] 0.918 -1.11 1.279 -1.324 1.883 ...
## $ : num [1:1000] -0.917 -1.588 1.157 3.268 5.831 ...
## $ : num [1:1000] 6.13 4.243 0.892 -2.908 5.044 ...
## $ : num [1:1000] 3.76 11.61 1.97 8 -10.8 ...
## $ : num [1:1000] 12.96 6.05 -10.78 6.3 -8.44 ...
%>% map_dbl(mean) # střední hodnoty jednotlivých vektorů v seznamu z
## [1] 0.009917511 0.886833563 2.157806711 3.273518951 4.093692378 4.782304445
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.
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):
<- map2(0:5, 1:6, rnorm, n = 1000) z
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.05218614 0.88155062 2.03616458 3.17173130 3.87357526 4.98427829
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:
<- (1:5) * 100 # počet pozorování je 100, 200, ..., 500
n <- 0:4 # střední hodnota je 0, 1, ..., 4
mu <- 1:5 # směrodatná odchylka je 1, 2, ..., 5
sd <- list(n, mu, sd) # nepojmenovaný seznam parametrů v pořadí
pars <- pmap(pars, rnorm)
z str(z) # struktura výsledku
## List of 5
## $ : num [1:100] -0.873 1.935 1.708 0.373 0.056 ...
## $ : num [1:200] -0.501 1.362 3.603 -3.286 -1.954 ...
## $ : num [1:300] 0.712 -0.131 0.659 1.333 5.517 ...
## $ : num [1:400] 9.53 3.63 1.29 1.18 3.49 ...
## $ : num [1:500] 10.019 -1.654 18.975 8.673 0.321 ...
Pokud jednotlivé parametry v seznamu pojmenujeme, na jejich pořadí nezáleží, protože se předají jménem:
<- list(sd = sd, mean = mu, n = n) # pojmenovaný seznam parametrů
pars <- pmap(pars, rnorm)
z str(z) # struktura výsledku
## List of 5
## $ : num [1:100] -0.358 0.173 -0.687 -0.396 -1.417 ...
## $ : num [1:200] 3.93 5.66 4.61 5.2 1.07 ...
## $ : num [1:300] -2.954 1.007 8.952 0.626 4.811 ...
## $ : num [1:400] 2.64 -2.86 -3.51 3.97 1.85 ...
## $ : num [1:500] 8.74 5.16 2.63 5.06 12.81 ...
Pohodlnější je však zadat parametry jako tabulku:
<- tibble::tibble(sd = sd, mean = mu, n = n)
pars <- pmap(pars, rnorm)
z str(z) # struktura výsledku
## List of 5
## $ : num [1:100] -0.696 -0.29 -1.118 -0.762 0.185 ...
## $ : num [1:200] 0.121 3.208 3.133 -0.27 -1.102 ...
## $ : num [1:300] 1.416 -0.628 3.918 -0.355 -1.457 ...
## $ : num [1:400] -0.045 -0.956 7.953 6.801 13.108 ...
## $ : num [1:500] -0.96048 7.17341 4.86472 -0.00911 5.44954 ...
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.:
<- pmap(pars, ~ rnorm(n = ..3, mean = ..2, sd = ..1))
z str(z) # struktura výsledku
## List of 5
## $ : num [1:100] 0.211 1.383 0.7 -0.3 -1.87 ...
## $ : num [1:200] -2.29 2.05 -2.71 6.96 5.01 ...
## $ : num [1:300] 1.372 4.457 -0.529 3.322 1.511 ...
## $ : num [1:400] 12.644 4.125 1.168 -0.291 3.733 ...
## $ : num [1:500] 10.231 2.981 0.184 10.634 3.81 ...
Balík purrr implementuje i funkci modify2()
a funkce walk2()
a pwalk()
, které umožňují iterovat vedlejší efekty nad více vektory.