Písanie skriptov v R

V RStudio môžeme písať a ukladať rozsiahlejšie skripty a funkcie, keď otvoríme editačné okno File->New File->New Script.

Písanie funkcií v R

Načítajme si dáta ‘iris’ obsahujúce merania 150 kosatcov.

data(iris)

Ako by sme vypočítali rozdiel medzi najväčším a najmenším rozmerom pre každú zo štyroch premenných?

max(iris$Sepal.Length) - min(iris$Sepal.Length)
## [1] 3.6

alebo

range(iris$Sepal.Length)
## [1] 4.3 7.9
diff(range(iris$Sepal.Length))
## [1] 3.6

Vyskúšame teraz napísať prvú funkciu v R pomocou toho, čo už vieme.

rozdiel <- function(x) max(x) - min(x)

Funkcie je odporúčané písať, editovať a priebežne si ukladať v rovnakom editore ako skripty. Následne môžeme funkciu dostať do nášho workspace stlačením Run v pravom hornom rohu. Všimnime si, že táto akcia vytvorí novú premennú s názvom funkcie v globálnom prostredí, s ktorou môžeme pracovať štandardným spôsobom.

Skúste napríklad spustiť

rozdiel(iris$Sepal.Length)
## [1] 3.6

Testovanie rôznych vstupov

rozdiel(1:10)
## [1] 9
rozdiel(iris$Petal.Width)
## [1] 2.4

Skúsime teraz menej ‘prirodzené’ vstupy:

rozdiel(iris$Species)
rozdiel("Janka a Ferko")
A <- c(3,2,8) %*% t(c(3,2,8))
rozdiel(A)
rozdiel(c(TRUE, TRUE, FALSE, TRUE))
## [1] 1
rozdiel(c(iris$Sepal.Length, iris$Sepal.Width))
## [1] 5.9

Potrebujeme teda upraviť funkciu tak, aby skončila chybou vždy, keď dostane nevhodný vstup.

rozdiel2 <- function(x) {
  stopifnot(is.numeric(x))
  max(x) - min(x)
}

Všimnime si štruktúru funkcie, ktorá má v sebe viac príkazov.

Vyskúšajte teraz spustiť túto funkciu s rovnakými vstupmi, na ktorej sme testovali funkciu rozdiel.

stopifnot ale dáva len generické chybové hlášky. Ak chceme užívateľa informovať o tom, čo spravil zle, môžeme použiť napríklad tento postup:

rozdiel3 <- function(x) {
  if(!is.numeric(x)) {
    stop('Function only works for numeric input!\n',
         'You have provided an object of class: ', class(x)[1])
  }
  max(x) - min(x)
}

Je dobrým zvykom za názov funkcie za symbol # uviesť krátky komentár toho, čo daná funkcia robí. V prípade, ak je rozsiahlejšia, odporúča sa podrobnejší popis, vrátane popisu vstupov a výstupov.

rozdiel3 <- function(x) {
  # funkcia, ktora vypocita rozdiel maxima a minima vstupneho vektora
  if(!is.numeric(x)) {
    stop('Function only works for numeric input!\n',
         'You have provided an object of class: ', class(x)[1])
  }
  max(x) - min(x)
}

Funkcie s viacerými argumentmi

Ako by sme postupovali, keby sme chceli vypočítať rozdiel tretieho a prvého kvartilu?

quantile(iris$Sepal.Length)
##   0%  25%  50%  75% 100% 
##  4.3  5.1  5.8  6.4  7.9
quantile(iris$Sepal.Length, probs=c(0.25, 0.75))
## 25% 75% 
## 5.1 6.4
qv <- quantile(iris$Sepal.Length, probs=c(0.25, 0.75))
max(qv) - min(qv)
## [1] 1.3
rozdiel.q <- function(x, probs){
  stopifnot(is.numeric(x))
  qv <- quantile(x = x, probs = probs)
  max(qv) - min(qv)
}
IQR(iris$Sepal.Length)
## [1] 1.3
rozdiel.q(iris$Sepal.Length, c(0.25, 0.75))
## [1] 1.3
rozdiel.q(iris$Sepal.Length, c(0, 1))
## [1] 3.6

Ak chceme, aby niektoré hodnoty probs boli defaultné (teda také, ktoré sa použijú, ak užívateľ nič nezadá), upravíme funkciu nasledovne:

rozdiel.q <- function(x, probs=c(0.25, 0.75)){
  stopifnot(is.numeric(x))
  qv <- quantile(x = x, probs = probs)
  max(qv) - min(qv)
}

Úloha: Upravte funkciu rozdiel.q, aby sa kontrolovali nasledovné veci:

  • či je probs numerický vstup

  • či má probs dĺžku dva

  • či sú hodnoty probs v intervale [0,1]

Riadenie toku pomocou For, While a Repeat

1. For

Štruktúra:

for (premenna in vektor){
    prikazy
}
x<-c(5,12,13)
for (n in x) print(n^2)
## [1] 25
## [1] 144
## [1] 169

Príklad: vypočítajte 1+2+…+300

s<-0
for(j in 1:300)
  s<-s+j
s
## [1] 45150

2. While

Štruktúra:

while (podmienka){
    prikazy
}
z <- 0
while(z < 5) {
  z <- z + 2
  print(z) 
}
## [1] 2
## [1] 4
## [1] 6

Príklad: Koľko sčítancov potrebujeme, aby sme postupným sčítavaním prirodzených čísel prekročili 1 milión?

target <- 1000000
s <- 0
n <- 0
while(s <= target){
  n <- n + 1
  s <- s + n
}
n
## [1] 1414

3. Repeat

z <- 0
repeat {
  z <- z + 1
  print(z)
  if(z > 100) break()
}

Úloha: Napíšte funkciu, ktorej vstupom bude vektor x a prirodzené číslo dg a výstupom matica, ktorá bude mať v i-tom stĺpci vektor i-tych mocnín x. Editujte túto funkciu tak, aby boli v i-tom stĺpci namiesto i-tych mocnín i-te odmocniny. Na začiatok funkcie doprogramujte kontrolu toho, či sú všetky čísla na vstupe nezáporné.

Podmienené príkazy

1. If

Štruktúra:

if (podmienka){
    `prikazy1
} else{
    `prikazy2
}
today <- Sys.Date()
day <- weekdays( today )
if ( day == "Monday" ) {
  print( "I don't like Mondays" )
} else {
  print( ":)" )
}
## [1] ":)"

2. ifelse

Štruktúra: ifelse(test, yes, no)

x <- 1:10 
ifelse(x<5 | x>8, x, 0)
##  [1]  1  2  3  4  0  0  0  0  9 10
x <- c(-2:2)
sqrt(x)  
## Warning in sqrt(x): NaNs produced
## [1]      NaN      NaN 0.000000 1.000000 1.414214
sqrt(ifelse(x >= 0, x, NA))  
## [1]       NA       NA 0.000000 1.000000 1.414214
ifelse(x >= 0, sqrt(x), NA)
## Warning in sqrt(x): NaNs produced
## [1]       NA       NA 0.000000 1.000000 1.414214

Príklad: Fibonacciho postupnosť

dlzka <- 10
fibvals <- numeric(dlzka)
fibvals[1] <- 1
fibvals[2] <- 1
for (i in 3:dlzka) { 
  fibvals[i] <- fibvals[i-1]+fibvals[i-2]
} 
fibvals
##  [1]  1  1  2  3  5  8 13 21 34 55

Nájdite súčet párnych členov Fibonacciho postupnosti, ktoré sú menšie ako 4 milióny

x <- c(1,2)
k <- 3
i <- 3
while(k <= 4000000){
  x <- append(x,k)
  k <- k + x[i-1]
  i <- i +1
}
sum(x[x%%2==0])
## [1] 4613732

Meranie času

system.time(for(j in 1:3000000) s <- s+j)
##    user  system elapsed 
##    0.05    0.00    0.04
system.time(sum(1:3000000))
##    user  system elapsed 
##       0       0       0

Príklady na precvičenie

  1. Napíšte program, ktorého vstupom budú koeficienty kvadratickej rovnice \(ax^2+bx+c=0\) a výstupom reálne korene (dva alebo jeden) alebo string "rovnica nema realne korene".

  2. Napíšte funkciu, ktorej vstupom bude vektor \(x=(x_1,...x_n)\) a výstupom vektor \(((x_1+x_2+x_3)/3,(x_2+x_3+x_4)/3,...,(x_{n-2}+x_{n-1}+x_n)/3)\).

  3. Napíšte funkciu, ktorej vstupom je matica s celočíselnými prvkami. Výstupom funkcie bude matica tých istých rozmerov s tým, že každé nepárne číslo bude nahradené svojim dvojnásobkom.