Slovník | Vyhledávání | Mapa webu
 
Základy informatiky pro biologyAnalýza dat v R Rozšířené základy R R jako programovací jazyk Cykly a podmínky

Logo Matematická biologie

Zjednodušení / zrychlení for cyklu

Pokud máme velmi objemná data, někdy se vyplatí namísto for cyklu použít funkci apply() (nebo jinou z této rodiny)

Mějme matici ma:

> ma <- matrix(rep(1:3,each=2),3,2)

> ma
          [,1] [,2]
    [1,]    1    2
    [2,]    1    3
    [3,]    2    3


Funkce apply() aplikuje námi zvolenou funkci na řádky/sloupce matice.
> apply(ma, 1, mean) # spočítá průměr v jednotlivých řádcích
    [1] 1.5 2.0 2.5
> apply(ma, 2, mean) # spočítá průměr v jednotlivých sloupcích
    [1] 1.333333 2.666667

Oproti tomu funkce tapply() aplikuje funkci na vektor na základě faktoru.
> vektor = ma[,2]
> skupina = c("A", "A", "B")
> tapply(vektor,skupina,FUN=function(x) mean(x))

     A   B
    2.5 3.0

Demonstrujme si rozdíl mezi rychlostí for cyklu,  funkce apply() a funkce rowsums() na příkladu výpočtu průměru v řádcích matice X. Pro výpočet času použijeme funkci system.time():

Definujeme si matici o 10 000 řádcích a 100 sloupcích s náhodně generovanými hodnotami normálního rozdělení:
> X <- matrix(rnorm(1000000),10000,100)
 

Použijeme for cyklus:

> system.time({mean.for = c();
for (i in c(1:nrow(X)))
 {
   mean.temp <- mean(X[i,])
   mean.for <- c(mean.for,mean.temp)
  }

})

   user  system elapsed
  0.000   0.000   0.651

Použijeme funkci apply():

> system.time(mean.apply <- apply(X,1,mean))
   user  system elapsed
  0.000   0.000   0.094 

A nakonec funkci rowSums():

> system.time(a<-rowMeans(X))
   user  system elapsed
  0.000   0.000   0.003

Další optimalizace for cyklu se dá dosáhnout inicializací nových objektů v plné délce ještě před spuštěním samotného cyklu.

Demonstrujeme na příkladu třech funkcí, ve kterých se v cyklu pro hodnoty i=1,..,n  v každém kroku přiřadí i-tému elementu vektoru a hodnota i^2 (tedy druhá mocnina i).

V první funkci není vektor a inicializován v plné délce n , vektor a je v každém cyklu prodlužován:

> funkce1<-function(n){
 a <- NULL
 for (i in 1:n) {a[i]<-i^2}
 return(a)
 }

> system.time(funkce1(n=100000))
   user  system elapsed
  4.728   1.461   6.301

Ve druhé funkci vektor a inicializujeme v plné délce n:

> funkce2<-function(n){
 a <- numeric(n)
 for (i in 1:n) {a[i]<-i^2}
 return(a)
 }

> system.time(funkce2(n=100000))
   user  system elapsed
  0.105   0.000   0.103

Rozdíl v čase výpočtu je několikanásobně nižší u funkce2. To proto, že ve funkci funkce1 dochází v každé iteraeci k alokování paměti a kopírování objektu a, co podstatně snižuje rychlost výpočtu.

Nakonec třetí funkce pro tento výpočet vůbec for cyklus nepoužívá a je také nejrychlejší:

> funkce3<-function(n){
 a <- c(1:n)^2
 return(a)
 }

> system.time(funkce3(n=100000))
        user       system      elapsed
0.0000000000 0.0000000000 0.0009999999

Zamyslete se proto nad tím, je-li for cyklus nejlepším řešením z hlediska výkonnosti i přehlednosti Vašeho kódu.

 
vytvořil Institut biostatistiky a analýz Lékařské fakulty Masarykovy univerzity