Wektory i macierze

Jako zapowiedziałam w poprzednim rozdziale, zanurzamy się teraz głębiej w odmęty R-a, poznając bodaj najczęściej używane struktury danych, jakimi są wektory i macierze.

Wektor to zbiór wartości, ułożonych w jakimś porządku. Tworzy się go tak:

moj_pierwszy_wektor = c(1, 2, 3, 4)

c to skrót od concatenate, czyli połącz - c wziął wszystkie liczby, które podaliśmy w nawiasie, i połączył je w jeden wektor. Zobaczmy, co można robić z wektorami, a co R-owi już się nie spodoba:

> moj_pierwszy_wektor = c(1,2,3,4)
> moj_pierwszy_wektor
[1] 1 2 3 4
> moj_pierwszy_wektor + 1
[1] 2 3 4 5
> moj_pierwszy_wektor * 3
[1]  3  6  9 12
> moj_pierwszy_wektor ** 2
[1]  1  4  9 16
> moj_pierwszy_wektor > 2
[1] FALSE FALSE  TRUE  TRUE
> moj_pierwszy_wektor == 2
[1] FALSE  TRUE FALSE FALSE
> moj_pierwszy_wektor + moj_pierwszy_wektor
[1] 2 4 6 8
> moj_drugi_wektor = c(10, 11, 12)
> moj_pierwszy_wektor + moj_drugi_wektor
[1] 11 13 15 14
Warning message:
In moj_pierwszy_wektor + moj_drugi_wektor :
  longer object length methods/html/is.html">is not a multiple of shorter object length
> inny_wektor = c(100, 200)
> moj_pierwszy_wektor + inny_wektor
[1] 101 202 103 204
> c(moj_pierwszy_wektor, moj_drugi_wektor)
[1]  1  2  3  4 10 11 12

Prześledźmy co się działo w tym kawałku kodu. Widać, że różne operacje, które działają na dwóch liczbach - jak dodawanie, mnożenie, potęgowanie, porównywanie etc. - zadziałają też na parze wektor i liczba - po prostu takie działanie jako wynik daje wejściowy wektor, na którego każdym elemencie wykonano żądaną operację, np. do każdego dodano 1, albo sprawdzono, czy jest większy niż 2, jak w przykładzie. Oczywiście pozostawia to samą zmienną moj_pierwszy_wektor nienaruszoną. Jeżeli po obu stronach działania "+" stały wektory, R dodał odpowiadające sobie elementy: pierwszy do pierwszego, drugi do drugiego etc. Kiedy spróbowaliśmy z dwoma wektorami, ale o różnej długości, R zwrócił nam uwagę, że długość dłuższego obiektu nie jest wielokrotnością długości krótkiego. Cośtam zrobił, konkretnie spętlił za krótki wektor i do ostatniego elementu moj_pierwszy_wektor dodał pierwszy element z moj_drugi_wektor, ale wydrukował ostrzeżenie. To sugeruje, że wektory nie muszą być tej samej długości, i rzeczywiście, z wektorem dwuelementowym zadziałało już bez ostrzeżeń - R dodał pierwszy element do pierwszego, drugi do drugiego, trzeciego już w drugim wektorze nie było, więc wziął znowu pierwszy etc. No i na koniec pokazaliśmy, że funkcja c() może łączyć dwa wektory w jeden.

Co jeszcze ciekawego można robić z wektorami? Można na przykład dostać się do pojedynczego elementu, albo do jakiegoś fragmentu. Posłużą nam do tego nawiasy kwadratowe:

> wektor = c(11,12,13,14,15,16,17,18,19,20)
> wektor[3]     # bierzemy trzeci element wektora
[1] 13
> wektor[4:7]   # elementy od 4 do 7
[1] 14 15 16 17
> wektor[14]    # ciekawe co sie stanie jak nie ma takiego elementu?
[1] NA
> # NA znaczy not available, czyli ze nie ma tego czegos, o co poprosilismy R-a
> wektor[-4]    # caly wektor oprocz elementu 4
[1] 11 12 13 15 16 17 18 19 20
> wektor[5:2]   # elementy od 5 do 2, czyli w odwrotnej kolejnosci
[1] 15 14 13 12
> wektor[ c(3,5,7) ]    # chcemy kilka konkretnych elementow, podajemy ich indeksy za pomoca wektora
[1] 13 15 17

Uwaga. 4:7 to tak naprawdę też wektor, tak jak w ostatnim poleceniu. Polecam wpisać i zobaczyć, co wyjdzie.

Kolejną rzeczą godną poznania są macierze. Macierz to tak jakby tabela wartości. Można ją stworzyć na przykład tak:

> matrix(1, 3, 4)
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    1
[2,]    1    1    1    1
[3,]    1    1    1    1

Funkcja matrix tworzy macierz; w tym wypadku, kazaliśmy jej stworzyć macierz z samych jedynek (to pierwsza liczba w nawiasie, czyli pierwszy argument funkcji), z trzema wierszami (drugi argument) i czterema kolumnami (trzeci argument). Trochę nudna taka macierz, więc stwórzmy jakąś z różnymi wartościami:

> matrix( c(1,2,3,4,5,6,7,8), 2, 4)
     [,1] [,2] [,3] [,4]
[1,]    1    3    5    7
[2,]    2    4    6    8

Tym razem jako pierwszy argument podaliśmy wektor wartości, potem poinformowaliśmy R-a, że macierz ma mieć dwa wiersze i cztery kolumny i R porozkładał wartości z wektora po macierzy o takich wymiarach (zauważ, że najpierw rozkłada w pierwszej kolumnie, potem w drugiej etc.; idzie kolumnami, nie wierszami). W zasadzie nie musimy mu mówić, ile ma być kolumn jak już podaliśmy liczbę wierszy, bo może to sobie sam wyliczyć. Gorzej, jeśli się pomylimy i podamy taką liczbę wierszy, która nie jest dzielnikiem długości wektora i takiej macierzy nie będzie się dało zrobić.

> matrix( c(1,2,3,4,5,6,7,8), 2)        # to bedzie ok
     [,1] [,2] [,3] [,4]
[1,]    1    3    5    7
[2,]    2    4    6    8
> matrix( c(1,2,3,4,5,6,7,8), 3)        # to juz troche nie
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    1
Warning message:
In matrix(c(1, 2, 3, 4, 5, 6, 7, 8), 3) :
  data length [8] methods/html/is.html">is not a sub-multiple or multiple of the number of rows [3]

R ostrzegł nas, że coś jest nie tak, ale macierz stworzył, na tyle, na ile mu się udało. Po prostu zapętlił sobie wektor, który podaliśmy - tak, jak to robił w przypadku dodawania wektorów o różnej długości. Właściwie pierwszy przykład, kiedy zamiast wektora podaliśmy mu jedną liczbę, jest skrajnym przypadkiem takiego zachowania: R zapętlił sobie pojedynczą wartość 12 razy.

Osobiście często zapominam, czy najpierw podaje się liczbę kolumn, czy wierszy; na szczęście nie trzeba tego pamiętać, wystarczy wprost powiedzieć R-owi, czy chodzi nam o liczbę kolumn (ncol, jak number of columns), czy o liczbę wierszy (nrow, jak number of rows):

> matrix( c(1,2,3,4,5,6,7,8), ncol=4)
     [,1] [,2] [,3] [,4]
[1,]    1    3    5    7
[2,]    2    4    6    8

Przy wpisywaniu argumentów funkcji również działa podpowiadanie za pomocą tabulatora - jeśli nie pamiętasz, czy miało być "ncol" czy "ncolumns" czy co tam jeszcze zaczynasz pisać ncol, naciskasz tabulator (prawdopodobnie dwukrotnie) i patrzysz, jakie w funkcji, którą właśnie chcesz wywołać są dostępne argumenty. Możesz też wpisać "?matrix" i poczytać helpa funkcji matrix, gdzie wypisane i opisane będą wszystkie argumenty. Przypominam, że z helpa wychodzimy naciskając q (chyba, że pracujemy w RStudio, wtedy nie musimy wychodzić).

Odwoływanie się do elementów macierzy, podobnie jak przy wektorze, odbywa się za pomocą nawiasów kwadratowych; najpierw podaje się numer wiersza, potem numer kolumny. R sygnalizuje to podczas drukowania macierzy: zauważ, że numery wierszy są napisane z lewej stony przecinka, a kolumn z prawej.

> macierz = matrix( c(1,2,3,4,5,6,7,8,9), 3)
> macierz
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> macierz[1,2]
[1] 4
> macierz[3]
[1] 3
> macierz[2,]
[1] 2 5 8
> macierz[,3]
[1] 7 8 9
> macierz[1:2,3]
[1] 7 8
> macierz[1:2,2:3]
     [,1] [,2]
[1,]    4    7
[2,]    5    8
> macierz[,c(1,3)]
     [,1] [,2]
[1,]    1    7
[2,]    2    8
[3,]    3    9

Kilka rzeczy godnych zwrócenia uwagi: po pierwsze, można odwołać się do elementu macierzy zupełnie jak do wektora (polecenie macierz[3]). Po drugie, jeśli nic nie wpiszemy po prawej stronie przecinka, czyli tam, gdzie powinien być numer kolumny, R zrozumie, że chodzi nam o wszystkie kolumny (a więc polecenie macierz[2,] wydrukowało elementy z drugiego wiersza, z wszystkich kolumn). Analogicznie jeśli nie wpiszemy nic przed przecinkiem (macierz[,3] wydrukowało całą trzecią kolumnę).

Zobaczmy, jakie operacje możemy wykonać na macierzach:

> pierwsza_macierz = matrix( c(10,11,12,13), 2)
> druga_macierz = matrix( c(100,101,102,103), 2)
> pierwsza_macierz + 3
     [,1] [,2]
[1,]   13   15
[2,]   14   16
> pierwsza_macierz + druga_macierz
     [,1] [,2]
[1,]  110  114
[2,]  112  116
> wektor = c(1000, 2000)
> pierwsza_macierz + wektor
     [,1] [,2]
[1,] 1010 1012
[2,] 2011 2013
> pierwsza_macierz * wektor
      [,1]  [,2]
[1,] 10000 12000
[2,] 22000 26000
> wektor + pierwsza_macierz
     [,1] [,2]
[1,] 1010 1012
[2,] 2011 2013
> c(pierwsza_macierz, druga_macierz)
[1]  10  11  12  13 100 101 102 103

Hmm... To ostatnie zadziałało chyba nie tak, jak byśmy mieli nadzieję. Kiedy chcemy połączyć dwie macierze, to raczej nie w wektor. Oczywiście czasem może się to przydać, ale dobrze też umieć połączyć dwie macierze w jedną:

> cbind(pierwsza_macierz, druga_macierz)        # cbind, od column bind - laczymy kolumnami
     [,1] [,2] [,3] [,4]
[1,]   10   12  100  102
[2,]   11   13  101  103
> rbind(pierwsza_macierz, druga_macierz)        # od row bind - laczymy wierszami
     [,1] [,2]
[1,]   10   12
[2,]   11   13
[3,]  100  102
[4,]  101  103

Elementami zarówno wektora, jak i macierzy nie muszą być liczby, ale muszą to być elementy tego samego typu, inaczej R wszystkie uzna za napisy.

> c("a","b","c")        # wektor napisow, spoko
[1] "a" "b" "c"
> c(TRUE, FALSE, FALSE, TRUE)   # wektor wartosci logicznych, moze byc
[1]  TRUE FALSE FALSE  TRUE
> c(1, TRUE, "spinacz biurowy")         # to troche nie wyjdzie
[1] "1"               "TRUE"            "spinacz biurowy"
> wektor_z_roznymi_typami = c(1, TRUE, "spinacz biurowy")
> wektor_z_roznymi_typami[1]    # sprawdzmy czy pierwszy element to liczba 1 czy napis "1"
[1] "1"
> wektor_z_roznymi_typami[1] + 1        # pewnie nie wyjdzie...
Error in wektor_z_roznymi_typami[1] + 1 :
  non-numeric argument to binary operator

I jeszcze na deser dwie pożyteczne funkcje, tworzące pewne charakterystyczne wektory:

> seq(10)       # wektor od 1 do 10 (seq jak sequence)
 [1]  1  2  3  4  5  6  7  8  9 10
> seq(3,10)     # jak wyzej, ale od 3
[1]  3  4  5  6  7  8  9 10
> seq(3,10,2)   # jak wyzej, ale co dwa
[1] 3 5 7 9
> rep(3, 5)     # powtorz 5 razy liczbe 3 (rep jak replicate)
[1] 3 3 3 3 3
> rep( c(1,2), 3)       # powtorz 3 razy wektor (1,2)
[1] 1 2 1 2 1 2