moj_pierwszy_wektor = c(1, 2, 3, 4)
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:
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):
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ą:
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