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.429 3.3049 -1.5824 -0.0259 1.429 ...
## $ : num [1:1000] 0.949 4.397 -0.937 2.243 2.489 ...
## $ : num [1:1000] -1.47 4.451 -0.323 2.964 6.209 ...
## $ : num [1:1000] -3.62 1.07 -4.01 8.56 7.5 ...
## $ : num [1:1000] 7.628 11.075 2.747 3.062 0.362 ...
## $ : num [1:1000] -1.88 12.73 7.93 16.4 7.15 ...
%>% map_dbl(mean) # střední hodnoty jednotlivých vektorů v seznamu z
## [1] 0.01795407 1.07686537 1.90785726 2.83404567 4.24788831 4.98405769
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.03152333 0.99982716 2.00315820 3.06865858 3.94469261 4.93881101
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.1737 -0.5462 0.8767 -0.0866 -0.1762 ...
## $ : num [1:200] 0.242 0.393 0.564 0.232 4.44 ...
## $ : num [1:300] 9 3.28 2.94 4.85 4.01 ...
## $ : num [1:400] 0.974 6.671 1.474 -1.641 9.799 ...
## $ : num [1:500] -0.642 12.44 2.544 8.096 0.786 ...
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.275 -0.022 -0.575 0.127 1.634 ...
## $ : num [1:200] 1.2958 0.1946 0.728 0.0546 4.5983 ...
## $ : num [1:300] 0.558 1.489 -1.301 1.575 3.618 ...
## $ : num [1:400] 6.65 3.44 1.58 7.69 -3.09 ...
## $ : num [1:500] 5.705 6.483 11.685 -0.245 14.568 ...
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] 1.52828 -0.43726 0.37728 0.00864 -2.22425 ...
## $ : num [1:200] 0.961 4.259 1.929 3.967 2.336 ...
## $ : num [1:300] 1.32 2.08 8.88 1.06 5.83 ...
## $ : num [1:400] -2.07 1.4 1.53 4.75 5.05 ...
## $ : num [1:500] 6.094 -3.288 12.799 4.66 -0.994 ...
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.836 0.688 1.681 -0.764 0.963 ...
## $ : num [1:200] -0.795 2.255 6.734 2.872 -1.009 ...
## $ : num [1:300] 3.38 8.17 5.61 3.67 1.85 ...
## $ : num [1:400] 1.702 -0.238 4.224 0.177 8.111 ...
## $ : num [1:500] 11.635 -0.291 7.028 7.493 11.365 ...
Balík purrr implementuje i funkci modify2()
a funkce walk2()
a pwalk()
, které umožňují iterovat vedlejší efekty nad více vektory.