5.7 Poznámka k doplňování hodnot do tabulek

Stejně jako v případě ostatních základních datových struktur v R, je i v případě tabulek možné přiřadit novou hodnotu do výběru. To se hodí např. v situaci, kdy potřebujete nějakou hodnotu opravit nebo aktualizovat. Často se také hodí ukládat vypočtené nebo stahované hodnoty do tabulky. V tomto posledním případě je však potřeba jisté opatrnosti, protože ukládání do výběru tabulky je výpočetně extrémně nákladná operace (kvůli nákladům na vyhledání dané hodnoty). V případě malých tabulek to není problém, v případě tabulek o milionech řádků to už však může být problematické. Ukažme si to na příkladu (nenechte se vyvést z míry tím, že nebudete rozumět všem detailům; většině porozumíte později v tomto textu).

Nejdříve vytvoříme dva vektory o délce 10 milionů hodnot, první celočíselný a druhý reálný. Vektory naplníme chybějícími hodnotami NA. Vytvoříme i dvě tabulky, které naplníme těmito vektory. První tabulka bude třídy data.frame, druhá třídy tibble. Náš test spočívá v tom, že do každého vektoru budeme chtít vložit na stejnou pozici jedničku. Funkce microbenchmark() ze stejnojmenného balíku nám změří, jak dlouho bude tato operace trvat.

library(microbenchmark)
library(dplyr)
library(tibble)

x <- rep(NA_integer_, 1e7)
y <- rep(NA_real_, 1e7)
df <- data.frame(x = x, y = y)
dt <- tibble(x = x, y = y)

performance <- microbenchmark(
    "vektory" = {x[1000] <- 1L; y[1000] <- 1},
    "data.frame 1" = {df[1000, "x"] <- 1L; df[1000, "y"] <- 1},
    "data.frame 2" = {df$x[1000] <- 1L; df$y[1000] <- 1},
    "data.frame 3" = df[1000, ] <- data.frame(x = 1L, y = 1),
    "tibble 1" = {dt[1000, "x"] <- 1L; dt[1000, "y"] <- 1},
    "tibble 2" = {dt$x[1000] <- 1L; dt$y[1000] <- 1},
    "tibble 3" = dt[1000, ] <- tibble(x = 1L, y = 1),
    unit = "ms"
) %>% 
    summary() %>% select(expr, min, mean, median, max)
Tabulka 5.2: Čas potřebný na vložení dvou hodnot do vektorů a tabulek v milisekundách.
expr min mean median max
vektory 0.000858 0.3730667 0.0069685 36.68847
data.frame 1 35.121408 49.3930537 39.3517615 97.75422
data.frame 2 23.603386 42.3836958 37.2730475 112.43231
data.frame 3 35.897626 47.7953549 39.8374445 104.72969
tibble 1 35.678292 46.7628822 39.4573860 101.55800
tibble 2 35.268639 45.0353950 38.3740045 92.29636
tibble 3 36.240846 43.5741577 39.4811190 91.97515

Vysledky jsou dramaticky odlisne, jak ukazuje tabulka 5.2. Vložení dvou hodnot do tabulek trvá (na mém poměrně výkonném počítači) v průměru několik desítek milisekund, zatímco vložení do vektoru trvá na stejném počítači tisíciny milisekund (pokud se díváme na mediány). Praktický výsledek je zřejmý: pokud vkládáte do tabulky jednotlivé hodnoty, nevadí to, pokud je tabulka malá nebo vkládáte jen velmi málo hodnot. V opačném případě se výrazně vyplatí pracovat s vektory. Jednou jsem potřeboval zjistit vzdálenosti vzdušnou čarou mezi asi 5 000 čerpacími stanicemi. Bylo tedy potřeba spočítat a uložit asi 12 milionů vzdáleností (vzdálenost vzdušnou čarou je symetrická). Vlastní výpočet vzdálenosti je extrémně rychlý. Přesto však celý výpočet neskončil ani za čtyři dny – spočítalo se asi jen 1.5 milionu hodnot. Na vině bylo to, že jsem doplňoval hodnoty přímo do tabulky. Když jsem kód přepsal tak, aby pracoval s vektory, které jsem spojil do tabulky až nakonec, výpočet proběhl zhruba za půl hodiny.

Všimněte si také toho, že jsem vektory a tabulky celé předalokoval. Kdybychom chtěli datové struktury zvětšovat postupně přidáváním dalších a dalších hodnot nebo řádků, byl by výpočet ještě pomalejší, protože by R muselo neustále alokovat nové bloky paměti a do nich nejdříve překopírovat stará data, a teprve potom přidat nové hodnoty. To by se opakovalo při každém zvětšení vektoru nebo tabulky.

Pokud se chcete dozvedet vic o efektivnosti kodu v R, doporucuji zejmena Wickham (2014), kap. Performance a Profiling. Uzitecne je take Burns (2011).

References

Burns, Patrick. 2011. "The R Inferno." http://www.burns-stat.com/pages/Tutor/R_inferno.pdf.
Wickham, Hadley. 2014. Advanced R. 1st ed. Boca Raton, Florida, USA- Chapman; Hall/CRC. http://adv-r.had.co.nz/.