Pakiet R w analizie układów złożonych

LABORATORIUM 2

Pętle FOR i WHILE

Mówiąc bardzo oględnie, uzytkownicy pakietu R są bardzo zniechęcani do tworzenia i implementowania pętli w kodzie. W 90% przypadków jest inny, szybszy sposób wykoniania algorytmu niż za pomocą petli. Pozostaje jednak te kilka procent, gdzie nie da się tego zrobic inaczej lub po prostu konstrukcja byłaby zbyt karkołomna, stąd też poniżej podane są typowe pętle for i while.

# PRZYKŁAD 2.1
x <- 1:10
for(i in x) print(i)
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
# PRZYKŁAD 2.2
x <- 1
while(x < 5) {
  print(x)
  x <- x + 1
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4


Instrukcja warunkowa IF... ELSE...


# PRZYKŁAD 2.3

x <- 5
if(x < 5) print(x) else print(x ^ 2)
## [1] 25

Warunek musi mieć długość równą 1, inaczej instrukcja warunkowa zostanie wykonana tylko dla pierwszego elementu.

# PRZYKŁAD 2.4

x <- 1:10
if(x %% 3) {
  print("Nie dzieli sie przez 3")
} else {
  print("Dzieli sie przez 3")
}
## Warning in if (x%%3) {: the condition has length > 1 and only the first
## element will be used
## [1] "Nie dzieli sie przez 3"


Instrukcja warunkowa IFELSE(...,...,...)


W przypadku, gdy mamy możliwość pracy na wektorze wartości, wygodnie jest korzystać z funkcji ifelse(), która sprawdza warunek dla każdego elementu wektora i zwraca również wektor.

# PRZYKŁAD 2.5

x <- 1:10
ifelse(x %% 3, "Nie dzieli sie przez 3", "Dzieli sie przez 3")
##  [1] "Nie dzieli sie przez 3" "Nie dzieli sie przez 3"
##  [3] "Dzieli sie przez 3"     "Nie dzieli sie przez 3"
##  [5] "Nie dzieli sie przez 3" "Dzieli sie przez 3"    
##  [7] "Nie dzieli sie przez 3" "Nie dzieli sie przez 3"
##  [9] "Dzieli sie przez 3"     "Nie dzieli sie przez 3"

PYTANIE: jak w miarę elegancki sposób, bez użycia instrukcji ifelse(), mając wyżej zdefiniowany wektor x wypisać liczby podzielne przez 3?


Funkcje


Schemat tworzenia funkcji jest następujący

# PRZYKŁAD 2.6

nazwa_funkcji <- function(x, y, ...) {
  ...
  ...
  return(wartosc)
}

Warto zaznaczyć, że funkcja może przyjmować oraz zwracać nie tylko skalary, ale również wektory. Przykładem może być funkcja realizująca "tabliczkę mnożenia" dla dowolnych dwóch wektorów.

# PRZYKŁAD 2.7

tabliczka_mnozenia <- function(zakres1, zakres2) {
  return(zakres1 %o% zakres2)
}
tabliczka_mnozenia(1:10,1:10)
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]    1    2    3    4    5    6    7    8    9    10
##  [2,]    2    4    6    8   10   12   14   16   18    20
##  [3,]    3    6    9   12   15   18   21   24   27    30
##  [4,]    4    8   12   16   20   24   28   32   36    40
##  [5,]    5   10   15   20   25   30   35   40   45    50
##  [6,]    6   12   18   24   30   36   42   48   54    60
##  [7,]    7   14   21   28   35   42   49   56   63    70
##  [8,]    8   16   24   32   40   48   56   64   72    80
##  [9,]    9   18   27   36   45   54   63   72   81    90
## [10,]   10   20   30   40   50   60   70   80   90   100

Instrukcja return() nie jest obligatoryjna - za wartość funkcji przyjmowana jest wartość wyznaczona w ostatniej jej linii.

# PRZYKŁAD 2.8

dodaj <- function(x, y) {
  x*y
  cos(x)
  x+y
}
dodaj(2,5)
## [1] 7

Wszystkie wartości przekazane do funkcji są widoczne i zmieniane lokalnie. W przypadku potrzeby zmiany wartości zmiennej tak, aby była widoczna globalnie należy użyć operatora przypisania <<-

# PRZYKŁAD 2.9

f <- function(x, y) {
  x <- x * 2
  y <<- y * 2
}
x <- 2
y <- 2
f(2,2)
x; y
## [1] 2
## [1] 4

Skrypty

Skrypty w języku R uruchamiane są komendą source("nazwa_pilku") lub, jeśli jest to aktualny program w oknie źródła, kombinacją klawiszy CTRL + SHIFT + S . Oczywiście, w przypadku używania własnych funkcji, należy je zdefiniować przed główną częścią skryptu, czyli po prostu na górze. W odróżnieniu od linii komend, wypisanie na ekran trzeba ubrać w odpowiednią funkcję print() lub cat().

# PLIK test.R

# Funkcja 
f <- function(x, y) {
  x <- 2*x
  y <<- 2*y
}

# Główna część skryptu

x <- 2
y <- 2

print(x)
print(y)
x

f(2,2)

cat("x =",x,"\n")
cat("y =",y,"\n")
# PRZYKŁAD 2.10

source("test.R")
## [1] 2
## [1] 2
## x = 2 
## y = 4

Operacje na wektorach i macierzach

Zdefiniujmy następujące wektory w, u i macierze A, B:

# PRZYKŁAD 2.11

w <- c(1,2)
v <- c(3,4)
A <- matrix(1:4, 2, 2)
B <- matrix(4:1, 2, 2)
w; v; A; B
## [1] 1 2
## [1] 3 4
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
##      [,1] [,2]
## [1,]    4    2
## [2,]    3    1

W pakiecie R, podobnie zresztą jak w innych językach skryptowych (np. Matlab), większość funkcji jest przeciążona ze względu na operacje wykonywane na wektorach i macierzach. Innym słowy, operacje takie są wykonywane po kolejnych elementach wektora (lub macierzy) i zwracane jako podobny obiekt.

Możemy wykorzystać następujące operacje na wektorach

# PRZYKŁAD 2.12

w + v
## [1] 4 6
5 + w
## [1] 6 7
2 * w
## [1] 2 4
sin(w)
## [1] 0.8414710 0.9092974
w %*% v
##      [,1]
## [1,]   11

Podobna sytuacja dotyczy macierzy. Dodatkowo mamy do dyspozycji inne, bardzo przydatne funkcje obsługi macierzy:

# PRZYKŁAD 2.13

A + B
##      [,1] [,2]
## [1,]    5    5
## [2,]    5    5
1 + A
##      [,1] [,2]
## [1,]    2    4
## [2,]    3    5
2 * A
##      [,1] [,2]
## [1,]    2    6
## [2,]    4    8
t(A)
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4
det(A)
## [1] -2
A %*% B
##      [,1] [,2]
## [1,]   13    5
## [2,]   20    8
eigen(A)
## eigen() decomposition
## $values
## [1]  5.3722813 -0.3722813
## 
## $vectors
##            [,1]       [,2]
## [1,] -0.5657675 -0.9093767
## [2,] -0.8245648  0.4159736