Zastosowanie pakietu R w statystyce medycznej

LABORATORIUM 1


Podstawy

Terminal

  • Sesję R uruchamiamy w terminalu wpisując komendę R.
  • Zamknięcie sesji następuje po wpisąniu komendy quit() lub q().
  • Wykonanie komendy następuje po wciśnięciu klawisza Enter.
  • Kilka komend w jednej linii należy rozdzielić średnikami.
2*pi;cos(1)
## [1] 6.283185
## [1] 0.5403023
  • Istnieją aż trzy sposoby przypisania: =, <-, <<-
    • <- ma najwyższy priorytet i jest standardowym operatorem przypisania w R
    • <<- może byc wykorzystany w ciele funkcji jeśli chcemy wewnatrz funkcji nadac wartość zmiennej globalnej
    • = służy do podawania parametrów funkcji
a <- 5
2 -> b
a;b
## [1] 5
## [1] 2
b = a <- 10
a;b
## [1] 10
## [1] 10
inkrementuj <- function(x){
  x <<- x+1
}
x <- 1
inkrementuj(x)
x
## [1] 2
rnorm(n=3,mean=2,sd=0.5)
## [1] 3.158013 2.044482 2.094134
  • Aby uruchomić skrypt w trybie tekstowym należy wywołać komendę source() ze sciezką do skryptu.

RStudio

  • Komentarz w skrypcie rozpoczyna się znakiem #.
  • Aby uruchomić skrypt otwarty w RStudio należy wcisnąc przycisk Source.
  • Można uruchomić wybrany fragment kodu ze skryptu zaznaczając go i wciskając kombinacje klawiszy Ctrl+Enter lub przycisk Run.
  • Funkcje print() i cat() służą do wypisywania wartości zmiennych na ekran.
x <- 2
y <- x+1
z <- x^2

cat("y =",y,"\n")
## y = 3
print(z)
## [1] 4


Typy danych


Atomowe

Typ liczbowy numeric
  • Liczby rzeczywiste są domyślnym typem liczbowym w R.
a <- 10
a; typeof(a)
## [1] 10
## [1] "double"
  • Aby zapisać liczbę calkowitą należy na końcu liczby dodać literę L.
b <- 10L
typeof(b)
## [1] "integer"
typeof(b+1)
## [1] "double"
typeof(b+1L)
## [1] "integer"
  • Liczbę zespoloną zapisuje się z użyciem formatu a+bi.
d <- 2+3i
d; typeof(d)
## [1] 2+3i
## [1] "complex"
sqrt(-1)
## Warning in sqrt(-1): NaNs produced
## [1] NaN
sqrt(-1+0i)
## [1] 0+1i
  • Dozwolona jest notacja naukowa.
a <- 2.3e3
a
## [1] 2300
  • Specjalnymi wartościami są NaN ("nie liczba") oraz nieskonczoności Inf, -Inf
1/0; exp(-Inf); 0 * Inf
## [1] Inf
## [1] 0
## [1] NaN
Typ znakowy character
  • Napisy (łancuchy znaków) rozpoczynają się i konczą znakiem ' lub ".
napis <- "Ala ma kota"
napis
## [1] "Ala ma kota"
napis <- 'Pakiet R'
napis; typeof(napis)
## [1] "Pakiet R"
## [1] "character"
  • Do łaczenia napisów służy funkcja paste()
slowo1 <- "I"
slowo2 <- "like"
slowo3 <- "trains"
paste(slowo1,slowo2,slowo3,sep=" ")
## [1] "I like trains"
  • Zmienna znakowa character NIE jest wektorem znaków.
Typ logiczny boolean
  • Reprezentuje logiczną prawdę (TRUE lub T) i fałsz (FALSE lub F).
  • W wyrażeniu arytmetycznym jest automatycznie konwertowany na liczby 1 i 0.
1 == 7
## [1] FALSE
z <- 1 == 1
z; typeof(z)
## [1] TRUE
## [1] "logical"
y <- (1 == 1) + 1
y; typeof(y) 
## [1] 2
## [1] "double"


Struktury danych

Wektor
  • Uporządkowany zbiór obiektów tego samego typu (wyjątek NA - brak wartości).
  • Podstawowy typ w języku R: operacje wykonywane na wektorach są najbardziej efektywne.
  • Funkcja c() tworzy wektor z pojedynczych elementów tego samego typu.
v <- c(-1,2,5)
v
## [1] -1  2  5
  • Ciągi arytmetyczne można łatwo generować funkcją seq() lub zwyczajnym dwukropkiem.
u <- 1:10
u
##  [1]  1  2  3  4  5  6  7  8  9 10
w <- seq(-10,10,2)
w
##  [1] -10  -8  -6  -4  -2   0   2   4   6   8  10
  • Funkcja rep() służy do generowania wektorów z powtórzeniami.
x <- rep(TRUE, 5)
x
## [1] TRUE TRUE TRUE TRUE TRUE
y <- rep(c(1,2,3),3)
y
## [1] 1 2 3 1 2 3 1 2 3
z <- rep(c(1,2,3), each=3)
z
## [1] 1 1 1 2 2 2 3 3 3
Typ wyliczeniowy (factor)
  • Przydatny do przechowywania wektorów wartości wystepujących na kilku poziomach.
  • Służy do reprezentacji danych kategorycznych i jakościowych.
  • Konstruktor factor().
  • Funkcja levels() wypisuje poziomy
wyksztalcenie <- factor(c("podstawowe", "wyzsże", "srednie", "srednie", "wyzsże"))
wyksztalcenie
## [1] podstawowe wyzsże     srednie    srednie    wyzsże    
## Levels: podstawowe srednie wyzsże
levels(wyksztalcenie)
## [1] "podstawowe" "srednie"    "wyzsże"
  • Zajmuje mniej pamięci niż odpowiadający mu typ znakowy - przechowywany jest jako kolejne liczby naturalne, ale nie można na nich wykonywać operacji arytmetycznych.
typeof(wyksztalcenie)
## [1] "integer"
wyksztalcenie+1
## Warning in Ops.factor(wyksztalcenie, 1): '+' not meaningful for factors
## [1] NA NA NA NA NA
Lista
  • Uprządkowany zbiór obiektów (np. wektorów), które mogą być róznych typów i dowolnej długości.
  • Konstruktor list().
L <- list(inty = 1:10, x = 2.71, tekst = c("a", "b", "c"), log = rep(T, 5))
L
## $inty
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## $x
## [1] 2.71
## 
## $tekst
## [1] "a" "b" "c"
## 
## $log
## [1] TRUE TRUE TRUE TRUE TRUE
Macierz
  • Konstruktorem macierzy 2D jest matrix().
A <- matrix(0, 2, 3); A
##      [,1] [,2] [,3]
## [1,]    0    0    0
## [2,]    0    0    0
A <- matrix(1:8, 4, 2); A
##      [,1] [,2]
## [1,]    1    5
## [2,]    2    6
## [3,]    3    7
## [4,]    4    8
A <- matrix(c("a", "b", "c", "d"),2 , 2); A
##      [,1] [,2]
## [1,] "a"  "c" 
## [2,] "b"  "d"
  • Macierz jest domyślnie wypełniana kolumnowo, ale można to zmienic ustawiając wartość parametru byrow=TRUE.
A <- matrix(1:8, 4, 2, byrow = TRUE); A
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4
## [3,]    5    6
## [4,]    7    8
  • W przypadku wielowymiarowych macierzy (D > 2) korzystamy z konstruktora array()
A <- array(1:27, dim = c(3,3,3)); A 
## , , 1
## 
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
## 
## , , 2
## 
##      [,1] [,2] [,3]
## [1,]   10   13   16
## [2,]   11   14   17
## [3,]   12   15   18
## 
## , , 3
## 
##      [,1] [,2] [,3]
## [1,]   19   22   25
## [2,]   20   23   26
## [3,]   21   24   27
Ramka danych (data frame)
  • lista wektorów o tej samej długości,
  • elementy w każdej kolumnie są tego samego typu,
  • elementy w różnych kolumnach mogą być różnych typów,
  • bardzo często wykorzystywana jako podstawowy typ w róznych pakietach R (np. ggplot2),
  • konstruktor data.frame()
ramka <- data.frame(liczby = 5:1, logiczne = T); ramka
##   liczby logiczne
## 1      5     TRUE
## 2      4     TRUE
## 3      3     TRUE
## 4      2     TRUE
## 5      1     TRUE


Indeksowanie w R

w <- 11:20
w[1:5]
## [1] 11 12 13 14 15
w[-1]
## [1] 12 13 14 15 16 17 18 19 20
w[c(1:4,8)]
## [1] 11 12 13 14 18
w[c(-2,-5)]
## [1] 11 13 14 16 17 18 19 20
M <- matrix(1:9, 3, 3)
M
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
# Pierwszy wiersz
M[1,]
## [1] 1 4 7
# Pierwsza kolumna
M[,1]
## [1] 1 2 3
# Dwa pierwsze wiersże
M[1:2,] 
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
# Bez trzeciej kolumny
M[,-3]
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    5
## [3,]    3    6
# Bez drugiego wiersza i drugiej kolumny
M[-2,-2] 
##      [,1] [,2]
## [1,]    1    7
## [2,]    3    9
L <- list(inty = 1:10, x = 2.71, tekst = c("a", "b", "c"), log = rep(T, 5))
L$inty
##  [1]  1  2  3  4  5  6  7  8  9 10
L[1]
## $inty
##  [1]  1  2  3  4  5  6  7  8  9 10
L[[1]]
##  [1]  1  2  3  4  5  6  7  8  9 10
ramka <- data.frame(liczby = 5:1, logiczne = T)
ramka
##   liczby logiczne
## 1      5     TRUE
## 2      4     TRUE
## 3      3     TRUE
## 4      2     TRUE
## 5      1     TRUE
# Pierwsże trzy wiersże
ramka[1:3,]
##   liczby logiczne
## 1      5     TRUE
## 2      4     TRUE
## 3      3     TRUE
# Druga kolumna
ramka[,2]
## [1] TRUE TRUE TRUE TRUE TRUE
# Pierwsza kolumna
ramka$liczby
## [1] 5 4 3 2 1
w[6:10][1:2]
## [1] 16 17
L[[3]][2:3]
## [1] "b" "c"
ramka$liczby[1:3]
## [1] 5 4 3


Operacje na wektorach i macierzach

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

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), wiekszość funkcji jest przeciążona że wzgledu na operacje wykonywane na wektorach i macierzach. Innymi słowy, operacje takie są wykonywane po kolejnych elementach wektora (lub macierzy) i zwracane jako podobny obiekt.

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

  • dodawanie wektorów, np. w + v,
  • dodawanie liczby do wektora, np. w + 5,
  • możenie wektora przez liczbę, np. 2 * w,
  • obliczanie funkcji wykładniczej, np. exp(w),
  • iloczyn skalarny wektorów w %*% v.
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:

  • dodawanie macierzy A + B,
  • dodawanie liczby do macierzy 1 + A,
  • możenie macierzy przez liczbę 2 * A,
  • transpozycja macierzy t(A),
  • wyznacznik macierzy det(A),
  • iloczyn macierzy A %*% B,
  • wartości i wektory wlasne macierzy eigen(A).
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


Wybrane podstawowe funkcje

Następujące funkcje są bardzo przydatne podczas przetwarzania danych w formacie wektorowym:

x <- c(2,-1,0,3,-5)
length(x)
## [1] 5
mean(x)
## [1] -0.2
sd(x)
## [1] 3.114482
rev(x)
## [1] -5  3  0 -1  2
sum(x)
## [1] -1
cumsum(x)
## [1]  2  1  1  4 -1
prod(x)
## [1] 0
cumprod(x)
## [1]  2 -2  0  0  0
min(x)
## [1] -5
which.min(x)
## [1] 5
max(x)
## [1] 3
which.max(x)
## [1] 4
sort(x)
## [1] -5 -1  0  2  3
sort(x, decreasing = TRUE)
## [1]  3  2  0 -1 -5
sort(x, index=TRUE)
## $x
## [1] -5 -1  0  2  3
## 
## $ix
## [1] 5 2 3 1 4
y <- c(1, NA, 2, 5, 7)
sum(y)
## [1] NA
mean(y)
## [1] NA
sum(y, na.rm = TRUE)
## [1] 15
mean(y, na.rm = TRUE)
## [1] 3.75
which(y > 2)
## [1] 4 5
which(y == 2)
## [1] 3
z <- c(0/0, NA, 1/0, -1/0, 10, 15); z 
## [1]  NaN   NA  Inf -Inf   10   15
is.na(z)
## [1]  TRUE  TRUE FALSE FALSE FALSE FALSE
is.nan(z)
## [1]  TRUE FALSE FALSE FALSE FALSE FALSE
is.infinite(z)
## [1] FALSE FALSE  TRUE  TRUE FALSE FALSE
which(is.na(z))
## [1] 1 2
which(is.nan(z))
## [1] 1
which(is.infinite(z))
## [1] 3 4


Instrukcje sterujace


Petle FOR i WHILE

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
x <- 1
while(x < 5) {
  print(x)
  x <- x + 1
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4


Instrukcja warunkowa IF... ELSE...

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.

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

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

ZADANIE: W jaki inny sposób, bez użycia instrukcji ifelse() ani innych pętli, mając wyżej zdefiniowany wektor x wypisać liczby podzielne przez 3?


Skrypty

Skrypty w języku R uruchamiane są komendą source("nazwa_pilku") lub, jeśli jest to aktualnie otwarty program w oknie, kombinacją klawiszy Ctrl+Shift+S. Oczywiście, w przypadku używania własnych funkcji, należy je zdefiniowac przed główną częscią 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")

# Wykonanie skryptu: source("test.R")



Wlasne funkcje

Schemat tworzenia funkcji jest następujący

nazwa_funkcji <- function(x, y, ...) {
  ...
  ...
  return(wartość)
}

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

tabliczka_mnożenia <- function(zakres1, zakres2) {
  return(zakres1 %o% zakres2)
}
tabliczka_mnożenia(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.

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 <<-

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

ZADANIE: Napisz funkcję cross.prod(x,y), która będzie wykonywała iloczyn wektorowy dwóch wektorów.