Ta strona wykorzystuje ciasteczka ("cookies") w celu zapewnienia maksymalnej wygody w korzystaniu z naszego serwisu. Czy wyrażasz na to zgodę?

Czytaj więcej
< All Topics
Print

R, podstawowe operacje

 

Miłosława Sokół

R dla Biologów, podstawowe operacje

 

Zasady cytowania podręcznika: Miłosława Sokół (2023) “R dla Biologów, podstawowe operacje”, Wydział Biologii Uniwersytetu Warszawskiego, online: https://skrypty.biol.uw.edu.pl/skrypty/r-podstawy/

 

 

Wstęp

Podręcznik do programu R został podzielony na 3 skrypty. W pierwszym omówiono instalację programu R i RStudio oraz podstawowe operacje wykonywane w konsoli. W drugiej części wyjaśniono zasadę pisania wielolonijkowych skryptów i programowanie z użyciem instrukcji wyboru i pętli. W trzecim omówiono instrukcje i funkcje graficzne.

Podręcznik ten jest nieco inny niż wiele innych napisanych po polsku. Nie poleca ćwiczyć w R różnych poleceń i instrukcji, ale tylko je pokazuje. Można, co najwyżej, sprawdzić, czy własna wersja R pokazuje to samo. Jest przeznaczona głównie dla tych, którzy pragną sobie przypomnieć, jak to, co mają właśnie zrobić, jest robione, jaka jest składnia odpowiednich funkcji. Jest to rodzaj “przypominajki” dla tych, co tylko czasami korzystają z R i nie starają się na pamięć nauczyć nazw funkcji używanych w R. Jest także przeznaczony dla tych, którzy chcą zastosować R w swojej dziedzinie, ale nie koniecznie się go nauczyć. Trzeba tylko znaleźć miejsce, gdzie dany problem jest pokazany na przykładowych danych i zastosować go u siebie poprzez podmianę nazw.

Osobom, które nie używały dotąd R wystarczy wyjaśnić, że specyfiką tego programu jest uogólnienie wszystkich działań matematycznych wykonywanych na liczbach dla skończonych ciągów liczb (o dowolnej długości) zwanych wektorami. Większość funkcji matematycznych działa na wektorach dając w wyniku wektor. Skraca to znakomicie instrukcję wyliczeń, które dla danych trzeba wykonać wielokrotnie. Kod programów pisanych w skryptach R jest bardzo często krótszy niż w każdym innym języku programowania, w tym Pythonie, który jest znany z krótkich kodów źródłowych w swoich skryptach.

Biologów najbardziej interesują wszelkie operacje związane z bazami danych. Ale poszczególne kolumny tej bazy to wektory, czynniki i czasem szeregi czasowe. Operacje wykonywane na kolumnach są takie same, jak na wektorach, czynnikach i szeregach czasowych. Wymaga to do sięgnięcia do odpowiedniego rozdziału i znalezienie odpowiednich wskazówek. Z kolei w rozdziale o wektorach jest pokazane na kilku zaledwie przykładach, jak na wektor działają pewne funkcje matematyczne. Pełny wykaz tych funkcji znajduje się w pierwszym rozdziale i zobaczenie składni tego, co właśnie jest potrzebne, wymaga kilka kliknięć (przejście do pierwszego rozdziału i kliknięcie na funkcje matematyczne). Tak mniej więcej ma działać ta część naszego podręcznika podręcznika.

 

Instalacja i pierwsze uruchomienie programu

 

Instalacja R (2023 rok)

CRAN to sieć serwerów ftp i internetowych na całym świecie, które przechowują identyczne wersje kodu i dokumentacji dla R. Instalator podstawowej wersji pobieramy z serwera znajdującego się najbliżej. Będzie ona identyczna z programami znajdującymi się na innych serwerach, tylko szybciej trafi na nasz komputer. Wersję 3.0.3 programu R najlepiej pobrać ze strony https://cran.r-project.org/bin/windows/base/.

Wymagania programu R są niewielkie – co najmniej 91 MB wolnej przestrzeni na dysku (ewentualnie 76 MB gdy ograniczymy się do standardowej wersji). Instalacja jest bardzo prosta. Na wstępie proponuje przeczytać zasady licencji umożliwiającej z darmowego korzystania z programu. Instalator proponuje wersję językową programu, zainstalowanie wersji standardowej lub rozszerzonej, zmiany nazw katalogów, gdzie będzie instalowany program. Po instalacji standardowej na dysku zostaje umieszczony folder C:\Program Files\R, a na pulpicie pojawi się ikonki będąca skrótem do programu R.exe:

W starszych wersjach R pojawią się dwie ikonki. Zazwyczaj każda z nich uruchamia taki sam program. Różnią się one tylko organizacją pamięci. Czasami, zwłaszcza na starszych komputerach, jeden z tych programów nie dogaduje się z systemem i po prostu nie warto go uruchamiać.

 

Pierwsze uruchomienie programu R

Po uruchomieniu R pojawi się jedno okno nazwane konsolą:

i = “test”;

Jest ono podobne do okien tekstowych większości programów pisanych dla systemu Windows. Jednak jest to system wysyłania poleceń do interpretera. Po napisaniu w linijce zaczynającej się od > i naciśnięciu ENTER pojawia się wygenerowany przez program komunikat: wynik działań lub informacja o błędzie. Osoby nie pracujące wcześniej z takimi oknami niech zwrócą uwagę na sposób poruszania kursorem za pomocą strzałek i myszki. Zaobserwowane ograniczenia tylko pozornie wskazują na słabe możliwości programu. Wbrew pozorom ułatwiają pracę w R, a nie utrudniają.

Jeżeli komuś przeszkadzają informacje startowe pojawiające się w konsoli po uruchomieniu, może je skasować naciskając Ctrl+L na klawiaturze. Przycisk ten kasuje wcześniej wykonywane polecenia czyszcząc konsolę i umożliwia pracę od jej górnego wiersza.

ne jest zwrócenie uwagi na pracę klawisza “strzałka w górę”. Przywołuje ona poprzednio wpisane komendy, nawet te z błędami i skasowane klawiszem Ctrl+L. Można wtedy te błędy poprawić. Przykładowo można wypróbować działanie matematyczne:

> 1+1+1a
BŁĄD: nieoczekiwano: symbol in "1+1+1a"; 
> 1+1+1
[1] 3

 

Żeby ponownie wywołać wpisane wcześniej 1+1+1a wystarczyło nacisnąć klawisz strzałki w górę. Wtedy można wymazać i ponownie nacisnąć enter.

Czasami wygodnie jest wykonywać jedno działania lub jedna funkcje w kilku linijkach. Można to zrobić patrząc na następujący przykład:

> (1+1+1)*
+ (2+2+2)
[1] 18

 

Taki podział jednego polecenia na dwie linijki możliwy jest tylko wtedy, gdy pierwsza linijka zostanie zakończona znakiem wskazującym na niedokończone działanie. Często też zapomnienie o końcowym nawiasie powoduje wyświetlenie się czerwonego +, co oznacza, że R oczekuje na zakończenie instrukcji.

Przy pisaniu wszystkich komend tekstowych, na przykład nazw funkcji, należy pamiętać, że w R rozróżnialne są małe i duże litery.

> SIN(30)
Błąd w poleceniu 'SIN(30)':nie udało się znaleźć funkcji 'SIN'
> sin(30)
[1] -0.9880316

 

Funkcja sin() działa na kątach wyrażonych w radianach, nie stopniach.

Korzystanie z pomocy

 R w wersji podstawowej, bez dodanych pakietów z nowymi funkcjami, ma ponad 3000 różnych funkcji wykonujących bardzo różne czynności. Wykaz tych funkcji można zobaczyć w formacie PDF na stronie http://cran.r-project.org/doc/manuals/r-release/fullrefman.pdf. Już ładowanie się tego pliku pokazuje jak jest on obszerny (3424 strony, mniej więcej około strony na omówienie każdej funkcji), a korzystanie z niego jest możliwe tylko dla osób o żelaznych nerwach. Do pokazanych w tym podręczniku funkcji z ich jednostronicowym opisem można dojść wpisując:

> help(nazwa funkcji) #albo:
> help("nazwa funkcji") #albo:
> ?nazwa funkcji

 

Tyle tylko, że nazwę potrzebnej funkcji trzeba znać. Bardzo często korzysta się z tej formy pomocy gdy chcemy sprawdzić jakie związane są ze znaną nam funkcją opcje, jakie są jej możliwości, jak została ona wykorzystana w podanych na końcu wyświetlanej strony przykładach.

Gdy szukamy funkcji, w których podejrzewamy, że może występować jakiś popularny człon (np. test lub plot) możemy posłużyć się funkcją apropos() albo ??. Zwraca ona wykaz wszystkich funkcji, w których nazwie jest człon “test”. Przykładowo gdy szukamy nazw funkcji, za pomocą których można wykonać testy statystyczne, możemy wpisać do konsoli apropos(“test”).

> apropos("test")
 [1] ".valueClassTest"         "ansari.test"
[3] "bartlett.test"           "binom.test"
[5] "Box.test"                "chisq.test"
[7] "cor.test"                "file_test"
[9] "fisher.test"             "fligner.test"
[11] "friedman.test"           "kruskal.test"
[13] "ks.test"                 "mantelhaen.test"
[15] "mauchly.test"            "mcnemar.test"
[17] "mood.test"               "oneway.test"
[19] "pairwise.prop.test"      "pairwise.t.test"
[21] "pairwise.wilcox.test"    "poisson.test"
[23] "power.anova.test"        "power.prop.test"
[25] "power.t.test"            "PP.test"
[27] "prop.test"               "prop.trend.test"
[29] "quade.test"              "shapiro.test"
[31] "t.test"                  "testInheritedMethods"
[33] "testPlatformEquivalence" "testVirtual"
[35] "var.test"                "wilcox.test" 

 

Przeważnie znając nazwę testu, który chcemy wykonać możemy domyślić się nazwy odpowiedniej do tego celu funkcji. Jeżeli to nie wystarcza, można napisać help.search(“test”) i uzyskać stronę internetową z wykazem wszystkich funkcji, w której opisie występuje słowo test. Nie zawsze jest to test statystyczny.

W okresie nauki, albo gdy chcemy rozwiązać jakiś problem związany z językiem R, bardzo pomocne mogą się okazać funkcje example() i demo(). Z moich doświadczeń wynika, że uruchomienie tych funkcji dla funkcji graficznych trwa długo. Funkcja example() pokazuje to, co znajduje się na końcach stron pomocy jako przykłady. Funkcja demo() jest zaprogramowana tylko dla niektórych funkcji. Dla wielu funkcji pokazują się komunikaty pokazane poniżej.

> example(anova)
Komunikat ostrzegawczy:
In example(anova) : ‘anova’ posiada plik pomocy, ale nie ma przykładów
> demo(anova)
Błąd w demo(anova) : Nie znaleziono dema dla tematu ‘anova’ 

 

Wszyscy użytkownicy R szybko odkrywają, że najlepszym źródłem pomocy jest Internet. Wpisanie odpowiedniego zapytania w wyszukiwarce skutkuje wykazem wielu stron, w których albo pojawia się rozwiązanie, albo można przesłać swój problem na wybrane forum dyskusyjne i poczekać na odpowiedź kogoś, kto dłużej ma do czynienia z R.

Foldery robocze

 R standardowo zapamiętuje historię wszystkich działań w katalogu Dokumenty w pliku bez nazwy o rozszerzeniu Rhistory. Ponadto w tym samym katalogu tworzy dodatkowe wejście (skrót) do R bez nazwy, ale o rozszerzeniu RData. Uruchomianie R poprzez takie skróty powoduje automatyczne włączenie do nich wartości wszystkich nowych obiektów jakie zostały zdefiniowane podczas pracy z R. W pliku tekstowym .Rhistory zapisywane są wszystkie kolejne polecenia, także te niepoprawne. Po ponownym uruchomieniu R można je wywołać klikając strzałką do góry.

Pliki bez nazwy są nadpisane nowymi takimi plikami przy ponownej pracy w R i zgodzeniu się na zapisanie obszaru roboczego i historii. Czasem tego nie chcemy. Możemy wtedy tym plikom nadać nazwy wskazujące na rodzaj wykonywanych tam operacji. Nadanie nazwy tym plikom wygląda następująco:

> savehistory("hist.Rhistory")
> save.image("moje.RData"")

 

Wykonuje się je przed zamknięciem R. Wtedy obok plików bez nazw pojawiają się odpowiednie pliki z wprowadzoną nazwą i rozszerzeniami .Rdata i .Rhistory. Aby dalej z tymi danymi pracować trzeba uruchomić R poprzez plik z właściwą nazwą i rozszerzeniem .RData. Plik z rozszerzeniem .Rhistory najlepiej otworzyć przez notatnik i skopiować do konsoli R. W notatniku można usunąć z niego błędne polecenia.

Przy pracy z danymi istnieje często potrzeba zapisywania plików .RData i Rhistory w folderze, gdzie mamy własne dane wprowadzone w opracowywane w innych programach. Gdy taki folder o nazwie “MojFolder” jest umieszczony na pulpicie użytkownika “uzytkownik”, to można napisać:

> savehistory("C:/Users/uzytkownik/Desktop/MojFolder/hist.Rhistory")
> save.image("C:/Users/uzytkownik/Desktop/MojFolder/moje.Rdata")

 

Najlepiej jednak by MojFolder był folderem roboczym, gdyż wtedy do tego folderu wysyłane będą pliki tekstowe z wynikami, wykonywane wykresy itp. Istnieje zatem potrzeba zmiany folderu roboczego. Wygląda to następująco:

> setwd("C:/Users/uzytkownik/Desktop/MojFolder")

 

Istnieje czasem konieczność sprawdzenia aktualnego, roboczego katalogu. Wykonuje się to poprzez wywołanie ścieżki dostępu do tego katalogu:

> getwd("C:/Users/uzytkownik/Desktop")
[1] "C:/Users/uzytkownik/Documents"

 

Zmiany katalogu roboczego można dokonywać także znaną wszystkim metodą wędrowania po ścieżkach po wyborze w menu Plik opcji Zmień katalog.

Często ważne jest sprawdzenie jakie foldery i pliki znajdują się w roboczym katalogu. W tym celu wystarczy użyć funkcji list.files().

> list.files()
[1] "desktop.ini"
[2] "Moja muzyka"
[3] "Moje obrazy"
[4] "Moje wideo"
[5] "R" 

 

Zamykanie programu R

Czasem trzeba zamknąć program R przed zakończeniem pracy z danymi. Wtedy najlepiej zapamiętać dotychczasowo wykonane obiekty używając funkcji save.image() oraz historie poleceń używając funkcji savehistory() funkcji. Wygląda to następująco:

> savehistory("mojepolecenia.Rhistory")
> save.image("mojeobiekty.Rdata")

 

Można wymusić zamknięcie R przez kliknięcie na × w prawym górnym rogu okna R. Można w R kliknąć na “plik” i “Wyjście”. W obu przypadkach pojawi się pytanie czy zapisać obszar roboczy. Gdy wprowadzone zostały do R obiekty, za którymi kryją się dane potrzebne do dalszej pracy – to zgadzamy się. Gdy pracujemy w R robiąc różne przykładowe rzeczy w ramach nauki – to lepiej nie.

Jak twierdzą informatycy, bardziej eleganckim sposobem zamykania R jest napisanie funkcji quit() lub krótko q(). Są to inne zapisy tej samej funkcji. Użycie jej z pustymi nawiasami powoduje zamknięcie R w trybie domyślnym, więc pojawi się zapytanie o zapisanie obszaru roboczego. Można napisać q(save=”yes”) lub q(save=”no”) lub q(“yes”) lub q(“no”), co spowoduje odpowiednio zapisanie lub brak zapisu obszaru roboczego. Zamykanie komendami q(“ask”), q(“default”) spowoduje powstanie zapytania o zapamiętanie obszaru roboczego.

Zamykanie R za pomocą funkcji q() może być częścią jakiegoś długo trwającego programu. Wtedy potrzebna jest opcja warunkowo zamykająca R (gdy program nie wygenerował błędów) lub pozostająca w R (gdy są błędy które musi poprawić programista). Służy do tego opcja status=n, gdzie n jest liczbą całkowitą oznaczającą typy błędów. n=0 oznacza bez żadnych błędów.

Podczas pracy z R wszystkie wykonywane po kolei komendy można zapisać klikając na Plik (ang. File) i Zapisz historię (ang. save history). Spowoduje to powstanie pliku tekstowego, w którym zanotowane będą wszystkie komendy wykonane podczas pracy z R w czasie wszystkich zapamiętanych sesji. Można je w notatniku oczyścić z błędnych koment a potem załadować w R.

Pakiety

Podstawowa wersja R posiada ogromne możliwości obliczeniowe, ma wbudowane szereg funkcji matematycznych, procedur graficznych i statystycznych. Dla większości zastosowań związanych z opracowaniem danych jest całkowicie wystarczająca. Dodatkowo można w R zaprogramować bardzo złożone sposoby opracowania danych, choć niekiedy wymaga to pomysłowości i pracy. Co więcej, wiele osób mogło rozpracować ten problem już przed nami i serie instrukcji zapamiętane w postaci jednej funkcji mogą być już dostępne w bibliotekach, zwanych pakietami, oferowanych oficjalnie na serwerach sieci CRAN.

Sieć pakietów działających pod R już jest bardzo bogata i bardzo dynamicznie się rozwija. Pakiety pisane są przez różne osoby na całym świecie, stosujące różne nazewnictwo i nie mające obowiązku dodawania jasnych i czytelnych “helpów”. Powodują lawinę obliczeń i wygenerowanie wyników końcowych, z których tylko część może być potrzebna użytkownikowi. Wiele pakietów różni się tylko tym, że generują nieco inne lub inaczej opracowane wyniki końcowe. Pakietów takich jest zatem bardzo dużo, aktualnie ponad 19000  i nie w sposób nauczyć się wszystkich ich na pamięć. Praca w R polega zatem na poszukiwaniu w Internecie opisu różnych pakietów do R, wybraniu najbardziej odpowiedniego i zainstalowaniu go na swoim komputerze. Forów dyskusyjnych związanych z R jest mnóstwo i łatwo o różne wskazówki i komentarze dotyczące różnych dodatków.

Pakiety instaluje się z poziomu programu R. Należy wybrać z menu polecenie “Pakiety” (“Packages”) i wybrać “Zainstaluj Pakiety” (“Instal Packages”). Pojawi się lista krajów, w których znajdują się serwery sieci CRAN. Niezależnie od wyboru kraju trafimy na taki sam zestaw takich samych pakietów. Z listy ponad 5500 pakietów należy wybrać te, które chcemy zainstalować. Trzymając przycisk Ctrl można podświetlić interesujące pakiety nie koniecznie znajdujące się jeden pod drugim. Po kliknieciu “OK” poszczególne pakiety będą dołączane do R i umieszczane w stosownych bibliotekach. Procedura wygląda następująco:

  • Z głównego menu programu R wybieramy “Pakiety” a następnie “Zainstaluj pakiet(y)”.

  • Pojawi się wykaz serwerów “Cran”, przy czym mozna zgodzić się na automatyczne znajdywanie najblizszego (pod wzgledem szybkości połączenia, nie geograficznym) servera z pakietami. W kolejnym oknie pojawi się wykaz pakietów. Jak widać są one nazwane kilkuliterowym skrótem i z ich nazwy nie można niczego wywnioskować.

  • Z listy nazw pakietów należy wybrać ten poszukiwany i poczekać na jego instalację.

Posługiwanie się nimi wymaga załadowania biblioteki po uruchomieniu programu, co wykonuje się komendą:

> library(nazwa_biblioteki) 

 

Biblioteki, o ile chce się z nich korzystać, należy załadować po każdym uruchomieniu programu R. Wtedy funkcje w nich zawarte dostępne są do końca sesji.

Często różne załadowane pakiety operują tymi samymi nazwami obiektów lub funkcji. Podczas sesji aktywna jest nazwa z ostatnio załadowanego pakietu.

R-Studio

 

Co to jest RStudio?

W Wikipedii możemy przeczytać, że RStudio to “otwarto-źródłowe zintegrowane środowisko programistyczne dla języka R”. Jest to nakładka na program R, która ułatwia posługiwanie się tym programem, proponuje konieczne poprawki kodu, pokazuje jakie obiekty zostały zdefiniowane, jakie pakiety są aktywne itp. Jest obecnie najpopularniejszym sposobem pracy w programie R, głównie dlatego, że można go pobrać i użytkować bezpłatnie. Nie będzie ona jednak działać, jeżeli najpierw nie zainstalujemy na naszym komputerze R. Najlepiej zrobić to przed instalacją RStudio, gdyż wtedy RStudio połączy się z R podczas instalacji i spadnie nam ten problem z głowy.

RStudio tworzony jest przez RStudio Team, którzy rozpowszechnili go zgodnie z licencją Open Source na całym świecie. Więcej o tej organizacji można przeczytać na stronie https://www.rstudio.com/.

 

Instalacja RStudio (2023)

Program instalacyjny RStudio należy pobrać ze strony https://posit.co/download/rstudio-desktop/.

Strona ta przypomina, że najpierw musi zostać zainstalowany program R. Gdy ma to miejsce wystarczy kliknąć na Downoad RStudio Desktop for Windows, ewentualnie zejść niżej i wybrać stosowny system operacyjny komputera. Po kliknięciu na stosowną nazwę instalatora rozpocznie się pobieranie pliku instalacyjnego. Jesienią 2023 nosił on nazwę “Rstudio-2023.09.0-463.exe”. Plik zostaje umieszczony w folderze “Pobrane”. Ikonka programu instalacyjnego wygląda następująco:

Po uruchamieniu pliku instalacyjnego pojawi się okienko startowe.

Klikamy “Dalej”. Pojawia się możliwość zmiany ścieżki dostępu do programu uruchamiającego RStudio. W domyśle jest to C:\Program Files\RStudio. Można się na to zgodzić, gdy nie występuje kolizja nazw.

Klikamy “Dalej”. W kolejnym okienku jest możliwość wstawienia skrótu do RStudio w menu start w katalogach różnych programów zainstalowanych już na dysku. Zazwyczaj nie jest to potrzebne. Lepiej wybrać zaproponowane nowe miejsce o nazwie RStudio.

Po kliknięciu “Zainstaluj” rozpocznie się instalacja programu.

Po zakończeniu instalacji możemy już tylko kliknąć na “Zakończ”.

RStudio pojawi się wśród zainstalowanych programów. Gdy zechcemy umieścić go np. na pulpicie to ikonka tego programu wygląda następująco:

Zainstalowany program będzie w wersji angielskiej, choc pewne komunikaty moga zostać przetłumaczone na jezyk polski przez wirtualnego tłumacza. Zazwyczaj nikomu to nie przeszkadza.

Pierwsze uruchomienie programu RStudio

Po uruchomieniu R pojawi się zestaw trzech okien i zapytanie, czy użytkownik zgadza się na automatyczne przesyłanie informacji o błędach krytycznych programu. Można się na nie zgodzić, gdyż nie powinno to grozić udostępnianiem informacji osobistych zespołowi RStudio Team, a wskaże najważniejsze usterki samego programu. Okno to później już się nie będzie pokazywało.

Następnie warto uruchomić wszystkie dostępne okna. W tym celu trzeba kliknąć na View->Panels->Show All Panels. Pojawi się Najczęściej używany zestaw okien programu RStudio.

W konsoli wykonuje się te same operacje co w konsoli R. Tak samo Ctrl+L czyści konsolę z niepotrzebnych instrukcji. Tak samo strzałka w górę przywołuje poprzednie instrukcje. Inna jest jednak kolorystyka. W R wprowadzane liczby lub tekst jest czerwony, a wyniki obliczeń i informacje o błędach wyświetlają się na niebiesko. W RStudio wprowadzane liczby i tekst jest niebieski, wyniki wyświetlają się na czarno, a komunikaty o błędach na czerwono.

Nad konsolą jest skrypt, na początku nie nazwany (Untitled). Jest to w zasadzie obszar, gdzie pisze się tak, jak w pliku tekstowym. Aby tekst się w nim załamywał należy kliknąć na Code->Soft Wrap Long Lines. W tym miejscu piszemy polecenia dla R i kopiujemy do konsoli, gdzie są wykonywane. Jeżeli są błędy, można poprawić w skrypcie.

Kopiowanie do konsoli jednej linijki ze skryptu może polegać na:

  • Umiejscowieniu kursora w tej linijce w dowolnym miejscu i kliknięciu na ikonkę ze strzałką “Run” ponad skryptem.
  • Umieszczeniu kursora w tej linijce w dowolnym miejscu i kliknięciu Ctrl+Enter.
  • Zaznaczeniu całej linijki i na ikonkę Run lub kliknięciu Ctrl+Enter.

Zaznaczenie fragmentu kodu powoduje przeniesienie do konsoli tego fragmentu. Można w ten sposób przenosić wiele linijek zaznaczając je wszystkie. Oczywiście klasyczne wprowadzanie kodu ze skryptu do pamięci chwilowej (Ctrl+C) i wklejanie do konsoli też działa.

Skrypty zazwyczaj zapamiętuje się poprzez File -> Save as nadając im przy tym odpowiednią nazwę. Później można je otwierać i dalej nad nimi pracować.

Z prawej strony na górze jest są okna Environment, History, Connections i Tutorial. Najbardziej przydaje się podczas pracy w RStudio okno Environment gdzie pojawiają się nazwy wszystkich tworzonych podczas pracy obiektów i ich krótka charakterystyka. Obiekty te można usunąć ze środowiska za pomocą “żółtej miotełki” – ikonki znajdującej się w górze tego okna. Klikniecie na niej powoduje wygenerowanie okna z zapytaniem, czy usunąć wszystkie obiekty. Gdy zgodzimy się w programie nie będzie już obiektów.

Okno history zawiera wszystkie wykonywane działania do momentu, gdy plik ten nie zostanie wyczyszczony przez “Żółtą miotełkę”. O ile niepotrzebne obiekty mogą przeszkadzać w pracy, o tyle historia operacji jest zabezpieczeniem przed utratą danych w razie skasowania przez przypadek lub z powodu awarii uzyskanych w wcześniej wyników. Można je za pomocą historii odtworzyć.

Z prawej strony na dole są okna Files, Plots, Packages, Help, Viewer i Presentation. W oknie Files pokazują się pliki i foldery znajdujące się w folderze Dokumenty. Tam często tworzy się nowy katalog na pliki R-owe, najczęściej skrypty, które można otworzyć i dalej nad nimi pracować.

W oknie plots pojawiają się wykonywane wykresy. Aby to zobaczyć wystarczy w konsoli napisać plot(1:10,10:1) i kliknąć Enter.

W oknie Packages pojawiają się biblioteki zainstalowane do R. Te, które są automatycznie ładowane po uruchomieniu, są odznaczone. Instalowanie nowej biblioteki następuje po kliknięciu na Install i wpisaniu nazwy biblioteki.

Okno Help pokazuje rekomendowane materiały do nauki R i inne materiały edukacyjne.

 

Operacje na liczbach

 

Typy liczb

Liczby rzeczywiste zapisywane i wyświetlane są w R w systemie dziesiętnym, a separatorem części ułamkowej jest kropka, nie przecinek.

> 1,3
BŁĄD: nieoczekiwane ',' in "1,"
> 1.3
[1] 1.3

 

Czerwonym kolorem zaznaczone są wszelkie polecenia wpisywane przez użytkownika przy użyciu klawiatury, niebieskim odpowiedzi programu. Polecenia wpisuje się zawsze po czerwonym >. Odpowiedzi są poprzedzone liczbą w nawiasie, która pokazuje pozycję wyświetlanej za nią liczby wśród wszystkich, jakie składają się na odpowiedź. Warto przy tym wiedzieć, że wciskając strzałkę ↑ na klawiaturze przywołujemy wpisane już polecenia i możemy wykonać w nich poprawki.

W komputerze liczby dziesiętne zamieniane są na liczby w systemie dwójkowym i przypisane są im obiekty fizyczne złożone z nienamagnesowanych i namagnesowanych fragmentów pamięci operacyjnej zwanych bitami ułożone w zestawy zwane bajtami. W inny sposób reprezentowane liczby całkowite, inaczej rzeczywiste.

Liczby całkowite zapisywane są w jednym lub kilku bajtach, gdzie pierwszy bit pierwszego bajtu wykorzystywane jest na znak liczby (0 to +, a 1 to -) a pozostałe bity służą do zapisu liczby w systemie dwójkowym.

Liczby rzeczywiste zapisywane są w kilku bajtach, w których pierwszy bit zarezerwowany jest na znak liczby, kolejne bity na tzw. mantysę – liczbę od 1 do 2 (ale mniejszą od 2), a pozostałe na wykładnik (czyli tzw. cechę). Jest to tzw. postać zmiennoprzecinkowa w systemie dwójkowym:
x = ± m*2c. Zapis ten pozwala na operowanie szerokim zakresem liczb i stosowanie wydajnych algorytmów arytmetycznych. Cechuje go jednak pewna niedokładność. Przykładowo nie można zera zapisać w postaci zmiennoprzecinkowej. Zatem pewne obliczenia wykonywane dla liczb całkowitych są dokładniejsze, gdy wykonuje się je dla liczb zapisywanych jako całkowite (typ integer) niż zmiennoprzecinkowe (typ double).

Po wykonaniu różnych operacji na liczbach w systemie dwójkowym program R zamienia je na liczby w systemie dziesiętnym i pokazuje standardowo jako zestaw cyfr. Liczby w zakresie od 0.001 do 99999999 pokazywane są standardowo (z wyjątkami). Mniejsze od 0.0001 i większe 99999999 pokazywane są w postaci zmiennoprzecinkowej o podstawie 10.

> 9999
[1] 9999
> 99999999
[1] 1e+08
> 100000
[1] 1e+05
> 100001
[1] 100001
> 0.0011
[1] 0.0011
> 0.0001
[1] 1e-04 

 

Występująca w tym ujęciu litera e nie ma nic wspólnego ze stałą Eulera e=2.71… używaną w matematyce, a oznacza 10 do potęgi, gdzie wykładnikiem jest liczba podana po e wraz ze znakiem.

Często wiemy, że efektem wykonywanych operacji liczbowych jest 0, a program wyświetla liczby postaci 1.34e-17. Jest to związane z liczbą bitów używaną do zapisu mantysy i cechy. Im więcej bitów zajmuje zapis jednej liczby, tym zapis liczb jest dokładniejszy ale pojemność pamięci operacyjnej jest ograniczona i przydzielanie dużo miejsca liczbom (a jest ich zazwyczaj dużo) jest nieopłacalne. Drogą kompromisu wypracowano, że najlepiej spisują się komputery operujące liczbami tzw. double precision. Liczby o podwójnej precyzji w zapisie dziesiętnym operują 16 znaczącymi miejscami po przecinku/kropce, co oznacza, że liczby mniejsze od 10-16 nie różnią się już od 0. Pozwala to zazwyczaj na dość dokładne obliczenia. Pokazuje to także, że dla zmiennych z natury nieujemnych powinniśmy używać takich jednostek, by wyniki pomiarów mieściły się od 1 do 1000 (wtedy błąd obliczeń się minimalizuje).

Zapis liczby z literą e i wykładnikiem pochodzi z Ameryki i nosi nazwę formatu naukowego (scientific) liczb. Niezbyt podoba się on Europejczykom. Aby zobaczyć, co się kryje za liczbą w formacie naukowym można użyć funkcji format z opcją scientific=FALSE. Wyświetla ona liczby jako zmienne tekstowe, ale jednocześnie jest to już zapis liczby, nie jej wartość. W porzypadku dużych liczb całkowitych wystaczy bezpośrednio za liczbą napisać L

> 0.0000045
[1] 4.5e-06
> format(0.0000045,scientific=FALSE)
[1] "0.0000045"
> 700000000
[1] 7e+08
> format(700000000,scientific=FALSE)
[1] "700000000"
> 700000000L
[1] 700000000

 

Być może teraz, gdy wpisujemy sami liczby funkcja format(), wydaje się zbędna. Stosuje się ją dopiero wtedy, gdy chcemy zobaczyć wyniki obliczeń różnych funkcji w formie bardziej właściwej dla ich umieszczenia np. w pracy dyplomowej.

R wyświetla liczby rzeczywiste z taką dokładnością, by cała liczba składała się od 7 do 9 liczb. Jednak do obliczeń używa wszystkich znaków znaczących jakie są używane. Można to wykryć każąc wyświetlać liczby, jako zmienne tekstowe za pomocą funkcji format() z opcją digit=n lub digits=n, gdzie n jest liczbą cyfr liczby, którą chcemy zobaczyć.

> 0.1101101101101101
[1] 0.1101101
> format(0.1101101101101101,digits=16)
[1] "0.1101101101101101"

 

Opcja digits działa do n=21, ale tylko 16 znaków po kropce jest wyświetlanych przy jej pomocy.

Komputery cechują się ograniczeniami nie tylko dokładności obliczeń (do 16 cyfr po przecinku/kropce), ale też mają ograniczenie na wielkość liczby. Liczby powyżej 1016 nie są już odróżnialne od siebie. Nie ma to charakteru ogólnego. Wszystko zależy od tego, jak dana liczba jest prezentowana w postaci binarnej.

> 10000000000000000
[1] 1e+16
> 10000000000000001
[1] 1e+16
> 10000000000000001-10000000000000000
[1] 0
> 10000000000000002-10000000000000000
[1] 2
> 10000000000000002-10000000000000001
[1] 2

 

Funkcje sprawdzające i określające typ liczby

Każda liczba wpisana do konsoli R jest traktowana jako liczba rzeczywista (co oznacza zapisanie jej w systemie zmiennoprzecinkowym), nawet wtedy gdy ma postać liczby całkowitej. Liczby całkowite także można wpisać do R przy użyciu funkcji as.integer(x). W niektórych sytuacjach zwiększa to dokładność obliczeń. Z dugiej strony liczby całkowite można zmienić na zmiennoprzecinkowe używając funkcji as.double(). Podobne do nich funkcje is.integer() i is.double() dają odpowiedzi TRUE lub FALSE pozwalając na wykrycie typu liczby. Sprawdzanie, czy dany obiekt wogóle jest liczbą, jest możliwe poprzez użycie funkcji is.numeric(), a zamiana czegoś, co wyląda na liczbę, na tę liczbę, wymaga użycia funkcji as.numeric().

> is.integer(7)
[1] FALSE
> is.integer(as.integer(7))
[1] TRUE
> as.integer(10.64)
[1] 10
> is.double(10.64)
[1] TRUE
> is.numeric("7.2")
[1] FALSE
> as.numeric("7.2")
[1] 7.2

 

Typy różnych obiektów wpisanych do R wykrywa się za pomocą funkcji mode(), typeof(), class() i str(). Dla liczba dają one następujące odpowiedzi:

> mode(7)
[1] "numeric"
> typeof(7)
[1] "double"
> class(7)
[1] "numeric"
> str(7)
 num 7
> mode(as.integer(7))
[1] "numeric"
> typeof(as.integer(7))
[1] "integer"
> class(as.integer(7))
[1] "integer"
> str(as.integer(7))
 int 7

 

Funkcje zaokrąglające liczby rzeczywiste

Przekształcenie danej rzeczywistej na liczbę całkowitą za pomocą funkcji as.integer() powoduje obcięcie jej części ułamkowej. Jest to rodzaj zaokrąglania liczby rzeczywistej w dół. Do zaokrąglania liczb rzeczywistych służy ponadto 5 specjalnych funkcji:

  • ceiling() zaokrąglenie w górę – najmniejsza liczba całkowita większa od argumentu,
  • floor() zaokrąglenie w dół – największa liczba całkowita mniejsza od argumentu,
  • trunc() obcina część liczby po kropce (to samo co floor()),
  • round() klasyczne zaokrąglenie do danej liczby miejsc po przecinku, którą określa się opcją digits=n,
  • signif() zaokrąglenie liczby do 5 miejsc po przecinku.
> # Zaokrąglenia
> ceiling(7.3)
[1] 8
> floor(7.3)
[1] 7
> trunc(7.3)
[1] 7
> round(7.3333)
[1] 7
> round(7.3333,digits=2)
[1] 7.33
> round(7.3393,2)
[1] 7.34
> signif(2.334564345)
[1] 2.33456

 

Opcja digits=n w funkcji round() może być skrócona do wymienienia tylko do n – liczby miejsc po przecinku.

 

Działania na liczbach

Praca z bazami danych i arkuszami kalkulacyjnymi w zasadzie wyeliminowała konieczność używania kalkulatorów. Jednak od czasu do czasu pojawia się konieczność wyliczenia wartości konkretnych działań i by to robić nie trzeba wychodzić z programu R. Wręcz przeciwnie. Wykonanie prostych działań jest w R bardzo proste. Składnia najprostszych działań jest prawie w 100% zgodna z matematyką.

Działania (suma, różnica, iloczyn, iloraz oraz potęgowanie dwóch liczb) są dobrze wszystkim znane. Są to funkcje dwuargumentowe, które zapisuje się za pomocą znaków wstawianych między liczby. W R stosuje się następujące symbole: “+”, “-“, “*”, “/”, “^”, zwane operatorami arytmetycznymi.

># dodawanie i odejmowanie
> 1+1
[1] 2
> 1.6+7.3
[1] 8.9
> 1.6-7.3; 4.5-2.32
[1] -5.7
[1] 2.18
> 10-
+ 5
[1] 5

 

Komunikaty wyjaśniające można wpisywać po znaku #. Nie są one interpretowane przez program.

W jednej linijce można napisać wiele działań, pod warunkiem, że rozdzieli się je średnikiem. Średnik wpisany na końcu każdego polecenia nie generuje błędu, ale jest tak traktowany jak przejście do nowej linijki.

Naciśnięcie klawisza enter przy nie zakończonej instrukcji powoduje pojawienie się znaku +, po którym należy dokończyć działanie. Znak plus na początku linijki oznacza łączenie jej z częścią instrukcji znajdującą w linijce wyżej i nie jest dodawaniem. W przypadku działań może to być mylące.

># mnożenie i dzielenie
> 1*1
[1] 1
> 1.6*7.3
[1] 11.68
> 1.6/7.3
[1] 0.2191781
> 3-1*2
[1] 1
> (3-1)*2
[1] 4
> 1.6/0
[1] Inf
> 1/Inf
[1] 0
> 1/INF
BŁĄD: nie znaleziono obiektu 'INF'
> 1/inf
BŁĄD: nie znaleziono obiektu 'inf'
> 0/0
[1] NaN
> Inf/Inf
[1] NaN > 2(10-3)(41+2)
BŁĄD: próba zastosowania nie-funkcji
> 2*(10-3)*(41+2)
[1] 602 

 

Program R zna pojęcie nieskończoności: Inf, które może być używane w działaniach. Największą liczbą odróżnialną przez R od Inf jest 2^1023=8.988466e+307. Natomiast 2^1024=Inf. Działania nieokreślone powodują generowanie wielkości NaN, która może być używana tak jak liczba. Zawsze jednak wszelkie działania z jej użyciem dają w wyniku NaN. Operowanie taką rozbudowaną strukturą liczb powoduje, że rzadko generowane są błędy.

Nie jest natomiast zrozumiałe dla R zwyczajowe pomijanie znaku mnożenia *, gdy mnożymy wyrażenia w nawiasach.

># potęgowanie i pierwiastkowanie
> 9^2
[1] 81
> 9^0.5
[1] 3
> 10^-1
[1] 0.1
> -10^-2
[1] -0.01
> (-10)^-2
[1] 0.01
> (-1)^0.5
[1] NaN
> 0^Inf
[1] 0
> 0.5^Inf
[1] 0
> 1^Inf
[1] 1
> 1.1^Inf
[1] Inf 

 

 

Dzielenie z resztą

Poza wymienionymi operatorami arytmetycznymi są także:

  • %% reszta z dzielenia liczby całkowitej przez liczbę całkowitą,
  • %/% największa liczba całkowita, która pomnożona przez dzielnik jest mniejsza od dzielnej.

Te dwa działania wymagają przypomnienia sobie ze szkoły zasad dzielenia liczb całkowitych z uzyskiwaniem reszty. W R takie działania zostały uogólnione na dowolne liczby rzeczywiste.

> 25%/%3
[1] 8
> 25.5%%3
[1] 1.5
> 11%/%3.5
[1] 3
> 11%%3.5
[1] 1.5
> -1%/%3
[1] -1
> -1%%3
[1] 2
> -25.72%/%3.4
[1] -8
> -25.72%%3.4
[1] 1.48 

 

Dla liczb K i L zawsze spełnione jest równanie K = (K %/% L)*L + K %% L. Liczba K %% L jest nieujemna i mniejsza od L.

Kolejność działań

Kolejność wykonywania działań jest taka sama, jak przyjęto to w matematyce. Potęgowanie przed mnożeniem i dzieleniem, mnożenie i dzielenie przed dodawaniem i odejmowaniem. Jeżeli wykonywane działania są równoważne – wykonywane są w kolejności od lewej do prawej. Wyjątkiem jest potęgowanie. Zmiana kolejności działań wymaga używania nawiasów. Służą do tego zawsze nawiasy półokrągłe.

># kolejność wykonywania działań
> 81-2-4
[1] 75
> (81-2)-4
[1] 75
> 81-(2-4)
[1] 83
> 44*2/13*100
[1] 676.9231
> 44*(2/13)*100
[1] 676.9231
> 44*2/(13*100)
[1] 0.06769231
> 3^2^0.5
[1] 4.728804
> (3^2)^0.5
[1] 3
> 3^(2^0.5)
[1] 4.728804
> 3*(2+7*(3+4))
[1] 153
> 3*[2+7*(3+4)]
Error: unexpected '[' in "3*[" 

 

Choć używanie nawiasów kwadratowych często daje bardziej czytelne wyrażenie matematyczne dla ludzkiego oka, nie jest ono zrozumiałe dla R. Nawiasy kwadratowe maja w R zupełnie inne zastosowanie.

Relacje między liczbami

Podobny charakter do działań mają relacje. Są to operatory “= =”, “!=”, “<“, “<=”, “>”, “>=” umieszczane między liczbami, których wartością jest “prawda” (TRUE) albo “fałsz” (FALSE). Znaczenia tych relacji łatwo się domyślić:

  • == równość liczb,
  • != nierówność liczb
  • < liczba z lewej jest mniejsza od prawej,
  • <= liczba z lewej jest mniejsza lub równa liczbie z prawej,
  • > liczba z lewej jest większa od liczby z prawej,
  • >= liczba z lewej jest większa lub równa liczbie z prawej.
> 3==3
[1] TRUE
> 3==2
[1] FALSE
> 3!=3
[1] FALSE
> 3!=2
[1] TRUE
> 3>2
[1] TRUE
> 3<2
[1] FALSE
> 3>3
[1] FALSE
> 3>=3
[1] TRUE
> 8*3>=8^3
[1] FALSE 

 

Warto zwrócić uwagę na to, że “=” nie jest relacją równości, jest nią “= =”.

> 7=7
Błąd w poleceniu '7 = 7':niepoprawna (do_set) lewa strona do przypisania
> 7==7
[1] TRUE 

Znak “=” stosuje się jako instrukcje przypisania wartości jakiemuś obiektowi i zostanie opisany później.

Funkcje matematyczne

Funkcje matematyczne znane są dobrze z lekcji matematyki. Najczęściej są to funkcje jednoargumentowe przekształcające liczbę x na y wyliczaną według określonego algorytmu. Zawsze jednak wynik jest wyliczany tylko z pewną dokładnością. W dodatku popełniany błąd nie jest taki sam dla różnych wartości argumentów. Niekiedy ta sama funkcja pojawia się w różnych wariantach wynikających z zastosowania innych algorytmów i innych rodzajach popełnianych błędów.

Najczęściej używane funkcje matematyczne możemy z podzielić na podstawowe, wykładnicze, logarytmiczne, trygonometryczne, cyklometryczne (odwrotne do trygonometrycznych), hiperboliczne oraz specjalne.

Funkcje podstawowe to:

  • abs(x) wartość bezwzględna (wartość absolutna, moduł) liczby,
  • sqrt(x) pierwiastek kwadratowy liczby.
> abs(-3.2345)
[1] 3.2345
> sqrt(32768.2)
[1] 181.0199
> 32768.2^0.5
[1] 181.0199
> sqrt(0)
[1] 0
> sqrt(-1)
[1] NaN
Komunikat ostrzegawczy:
W poleceniu 'sqrt(-1)': wyprodukowano wartości NaN

 

Funkcja sqrt(x) jest dokładnie tym samym, co x^0.5. Czasem, w niektórych wyrażeniach, jej używanie daje bardziej przejrzystą formułę. Do wyliczania innych funkcji potęgowych i pierwiastkowych stosuje się zapis x^a.

Przy obliczaniu funkcji matematycznych odstępy wstawiane przed lub po nawiasach są pomijane.

> sqrt(4)
[1] 2
> sqrt   (4)
[1] 2
> sqrt(    4   )
[1] 2

 

Wyliczanie funkcji wykładniczych można sprowadzić do obliczeń a^x. Jednak częstość stosowania funkcji wykładniczych, których podstawą jest stała e (podstawa logarytmu naturalnego, liczba Eulera, liczba Nepera równa w przybliżeniu 2.718281828459) spowodował, że wyróżniono dwie funkcje wyliczające ex:

  • exp(x) funkcja ex,
  • expm1(x) funkcja ex-1 wyliczona z większą dokładnością niż exp(x)-1 dla liczb bliskich 0.
> exp(1)
[1] 2.718282
> exp(0.000000000001)-1
[1] 1.000089e-12
> expm1(0.000000000001)
[1] 1e-12

 

W R nie ma zaimplementowanej stałej e. Aby posługiwać się tą wartością należy używać exp(1).

Częstość stosowania funkcji logarytmicznych spowodowała, że zaimplementowano do R najwięcej ich rodzajów:

  • log(x, b) logarytm z x o podstawie b,
  • log(x) logarytm naturalny z x,
  • logb(x, b) logarytm z x o podstawie b (wyliczany przy użyciu starszego algorytmu),
  • logb(x) logarytm naturalny z x (wyliczany przy użyciu starszego algorytmu),
  • log10(x) logarytm z x o podstawie 10 (efektywniejszy niż log(x,10),
  • log2(x) logarytm z x o podstawie 2 (efektywniejszy niż log(x,2),
  • log1p(x) logarytm naturalny z x+1 wyliczany z większą dokładnością dla liczb bliskich 0.
> log(1.0000000000002)
[1] 2.000622e-13
> log1p(0.0000000000002)
[1] 2e-13

 

W R zaimplementowano następujące funkcje trygonometryczne:

  • cos(x) funkcja cosinus, jej argumentem jest x wyrażone w radianach,
  • sin(x) funkcja sinus, jej argumentem jest x wyrażone w radianach,
  • tan(x) funkcja tangens, jej argumentem jest x wyrażone w radianach,
  • cospi(x) funkcja cosinus od argumentu pomnożonego przez π,
  • sinpi(x) funkcja sinus od argumentu pomnożonego przez π,
  • tanpi(x) funkcja tangens od argumentu pomnożonego przez π.

Oprócz tego możemy operować stałą π=3.1416 zapisywaną jako pi.

> pi
[1] 3.141593
> format(pi,digits=20)
[1] "3.1415926535897931"
> sin(pi)
[1] 1.224606e-16
> sinpi(1)
[1] 0
> tan(0.5*pi)
[1] 1.633124e+16
> tanpi(0.5)
[1] NaN
Komunikat ostrzegawczy:
W poleceniu 'tanpi(0.5)': wyprodukowano wartości NaN 

 

Funkcjami odwrotnymi do funkcji trygonometrycznych są funkcje cyklometryczne. W R jest ich 4:

  • acos(x) funkcja arcus cosinus dla x z przedziału [0,π], zwraca kąty w radianach,
  • asin(x) funkcja arcus sinus dla x z przedziału [-π/2,π/2], zwraca kąty w radianach,
  • atan(x) funkcja arcus tangens dla x z przedziału [-π/2,π/2], zwraca kąty w radianach,
  • atan2(y, x) kąt między prostą łączącą (0,0) z punktem (x,y) a półosią dodatnią 0X, atan2(y,x)=atan(y/x).
> asin(0.5)
[1] 0.5235988
> acos(0.5)
[1] 1.047198
> atan(3/4)
[1] 0.6435011
> atan2(3,4)
[1] 0.6435011

 

W praktycznych zastosowaniach matematyki, trochę bardziej w fizyce niż w biologii, ważną rolę odgrywają funkcje hiperboliczne. Są to kombinacje funkcji exponencjalnych o następujących wzorach:

  • cosh(x) cosinus hiperboliczny równy (ex+e-x)/2,
  • sinh(x) sinus hiperboliczny równy (ex-e-x)/2,
  • tanh(x) tangens hiperboliczny równy (ex-e-x)/(ex+e-x).
> sinh(0.1)-sin(0.1)
[1] 0.0003333334
> cosh(0.01)-cos(0.01)
[1] 1e-04

 

Funkcje odwrotne do funkcji hiperbolicznych noszą nazwę funkcji polowych z racji tego, że ich wartości są równe polom powierzchni pewnych fragmentów hiperboli jednostkowej x2-y2=1.

  • acosh(x) polowy cosinus hiperboliczny równy ln(x+(x2-1)1/2),
  • asinh(x) polowy sinus hiperboliczny równy ln(x+(x2+1)1/2),
  • atanh(x) polowy tangens hiperboliczny ½ln((1+x)/(1-x)).

Wymienione dotąd funkcje klasyfikowane są przez matematyków jako funkcje elementarne. W R ponadto zaimplementowano szereg funkcji definiowanych w matematyce poprzez wzory całkowe, o których wiadomo, że nie da się ich wyrazić w prostszy sposób. Nazywa się je zazwyczaj funkcjami specjalnymi.

  • beta(a, b) funkcja beta,
  • lbeta(a, b) logarytm z wartości bezwzględnej beta,
  • gamma(x) funkcja gamma,
  • lgamma(x) logarytm z wartości bezwzględnej gamma,
  • digamma(x) pierwsza pochodna funkcji lgamma,
  • trigamma(x) druga pochodna funkcji lgamma,
  • psigamma(x, deriv=n) n-ta pochodna funkcji lgamma(), gdzie n może być równe 0, 1, 2,… i dowolnie duże,
  • choose(n, k) uogólnienie symbolu Newtona dla liczb rzeczywistych wyliczane za pomocą funkcji beta,
  • lchoose(n, k) logarytm funkcji choose,
  • factorial(x) uogólnienie silni dla liczb rzeczywistych wyliczane za pomocą funkcji gamma,
  • lfactorial(x) logarytm funkcji factorial.
> 1*2*3*4*5
[1] 120
> factorial(5)
[1] 120
> 1*2*3*4*5*6
[1] 720
> factorial(6)
[1] 720
> gamma(5)
[1] 24
> gamma(6)
[1] 120
> gamma(7)
[1] 720

 

Wymienione funkcje specjalne pojawiają się głównie w statystyce we wzorach gęstości rozkładów ciągłych. Zastępują wyrażenia z silniami i wzorami Newtona czyniąc skomplikowane wzory łatwiejszymi do zapisania i zrozumienia.

 

Operacje na znakach i tekstach

 

Znaki i ich kodowanie

Znaki służące w różnych językach do tworzenia testu pisanego są tak samo, jak liczby, kodowane w systemie binarnym w 1-3 bajtach złożonych z 8, 16, 32 lub 64 bitów. O ile dla liczb przyjęto system dwójkowy ich kodowania, to dla znaków określono z góry ich kody binarne. Z informatycznego punktu widzenia cyfry arabskie są także znakami, ale zapisane bez cudzysłowu są przekształcane na inny kod binarny niż zapisane w cudzysłowie.

Od początku globalizacji, czyli momentu gdy powstała publiczna sieć “Internet” starano się by standard definiowania różnych znaków za pomocą kodów binarnych był taki sam na całym świecie. System ten nosi nazwę unikod (unicode). Dwa standardy definiowania znaków, jakie wytworzyły się do tej pory, Unicode oraz ISO 10646 różnią się w drobnych kwestiach. Unikod jest rozszerzeniem systemu kodowania znaków ASCII stosowanym do zapisu programów sterujących pracą komputera. Znaki ASCII zajmują 7 bitów pojedynczego bajtu, mają zatem kod binarny odpowiadający którejś z liczb od 0 do 27-1 czyli 127. Wśród tych znaków początkowych 31 pozycji oraz pozycję 127 zajmują znaki sterujące (przesunięcia, cofnięcia, wymazywanie, wstawianie znaków, przejścia do nowej linijki itp.). Kolejne pozycje zajmują znaki, które znajdują się na klawiaturze.

Ponieważ każdemu bajtowi z określonym kodem binarnym odpowiada zarówno liczba jak i jakiś znak, program R wymaga by znaki i teksty były inaczej pisane niż liczby. Znaki i teksty należy zapisać w cudzysłowie.

> "a"
[1] "a"
> "aaa"
[1] "aaa"
> "aa aaa"
[1] "aa aaa"
> "32178"
[1] "32178"
> ""a""
Error: unexpected symbol in "a"

 

Program pokazuje pewne ograniczenia R. Znak cudzysłowu nie może być zapisany jako znak bezpośrednio z klawiatury.

Każdemu znakowi zapisanemu w cudzysłowie odpowiada pewien kod binarny, a temu kodowi odpowiada unikod. Aby zobaczyć liczbę (w systemie dziesiętnym) jaka odpowiada znakowi należy użyć funkcji utf8ToInt(). Aby liczbom przypisać określony unikod należy użyć funkcji intToUtf8().

> utf8ToInt("a")
[1] 97
> intToUtf8(123)
[1] "{"

Liczbom (a właściwie ich kodom binarnym) od 32 do 127 odpowiadają następujące znaki ASCII.

Liczba
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
Znak
! # $ % & (
)
* + , . /
Liczba
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
Znak
0 1 2
3
4
5
6
7
8
9
:
;
<
=
>
?
Liczba
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
Znak
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
Liczba
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
Znak
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
Liczba
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
Znak
'
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
Liczba
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
Znak
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~

 

Znajdują się tu wszystkie znaki, które możemy wpisać z klawiatury amerykańskiej (a taką posługujemy się w Polsce).

Funkcja intToUtf8() pokazuje nam znaki unikodu dla wszystkich liczb, którym przypisano jakiś znak. Przykładowo dla liczb powyżej 20224 pojawiają się znaki pisma chińskiego

> intToUtf8(1134)
Ѯ
> intToUtf8(20534)

> intToUtf8(20634)

 

Niekiedy wywołanie tej funkcji powoduje wyświetlenie się kodu znaku według notacji unicode (zestaw liter i cyfr w cudzysłowie zaczynający się od \u). Wtedy, by wyświetlił się znak (bez cudzysłowów) należy użyć funkcji cat().

Dwuznaki

Przy programach tworzących automatycznie jakieś komunikaty potrzebny jest znakowy system wskazujący na uruchamianie określonych znaków sterujących. W wielu językach (np. Latex) stosuje się do tego dwuznaki zaczynające się od “\”. W R zastosowanie znalazły następujące dwuznaki to:

  • “\n” służący do łamania tekstu (przenoszenia fragmentu za znakiem do kolejnej linijki konsoli). Wykorzystywany jest przede wszystkim wraz z funkcją cat(),
  • “\t” tabulator wykorzystywany do tworzenia tabel,
  • “\\” służący do napisania znaku \ w wyświetlanym tekście

Wszystkie dwuznaki związanie są z funkcją cat() używaną do wyświetlania komunikatów programu. Wyświetla ona tekst bez cudzysłowów, ale jednocześnie kursor pozostaje w tej samej linijce. Aby kursor przeszedł do nowej linijki należy wprowadzić w obrębie tekstu (przed końcowym “) po przecinku dwuznak \n, który jest przetwarzany na znak sterujący: “przejdź do następnej linijki na jej początek”.

> cat("Ala ma kota \n a kot ma Alę")
Ala ma kota
a kot ma Alę>
> cat("Ala ma kota \n a kot ma Alę \n")
Ala ma kota
a kot ma Alę
> cat("Ala ma kota\na kot ma Alę\n")
Ala ma kota
a kot ma Alę

 

Funkcja cat() powoduje wyświetlenie tekstu, ale w tej samej linijce zostaje kursor konsoli. Aby uzyskać przejrzyste komunikaty należy stosować na jej końcu dwuznak “\n”.

Jak wspomniano w poprzednim podrozdziale funkcja intToUtf8() niekiedy zamiast znaku pokazuje numer unikodu tego znaku. Użycie funkcji cat() uwidacznia ten znak.

> intToUtf8(11134)
[1] "\ub7e"
> cat(intToUtf8(11134))
> cat(intToUtf8(11134),"\n")

>

 

Dwuznak “\t” jest odpowiednikiem tabulatora. Przeznacza on na kolejne teksty 7 znaków lub wielokrotność 8 minus 1 i rozpoczyna ich pisanie od początku przeznaczonego miejsca. W rezultacie przy stosowaniu podziału takiego tekstu na linijki tworzy się tabela.

> cat("a ab abc abcd \nabcd abc ab a\n")
 a ab abc abcd
abcd abc ab a
> cat("a\t ab\t abc\t abcd \nabcd\t abc\t ab\t a\n")
a       ab      abc     abcd
abcd    abc     ab      a

Konieczność napisania znaku \ w komunikacie programu powoduje, że musimy użyć dwuznaku “\\”. Wygląda to następująco:

> cat("\\/\\/\\/\\\n")
 \/\/\/\
> cat("W tym systemie operacyjnym stosuje się znak \\ do adresowania!\n")
W tym systemie operacyjnym stosuje się znak \ do adresowania!

 

Tekst

Znaki służą przede wszystkim do tworzenia tekstu. Program R jest w minimalnym stopniu używany jako edytor tekstowy i to tylko wtedy gdy w skomplikowany sposób przekształcany jest jakiś tekst. Często jednak R zawiera funkcje (np. funkcje statystyczne), które wyświetlają się wraz z komunikatami. Te komunikaty to wyświetlane zmienne tekstowe.

Tekst jest po prostu ciągiem znaków umieszczonym w cudzysłowie.

> R jesteś super programem!
BŁĄD: nieoczekiwany symbol in " R jesteś"
> "R jednak nie jesteś super programem"
[1] "R jednak nie jesteś super programem"
> # Wyjaśniam, że R nie jest super programem
>

 

Dłuższy tekst można zapisać w kilku linijkach. Dopóki nie pojawi się cudzysłów, program będzie oczekiwał dalszego ciągu tekstu. Przetwarza go jednak na obiekt jednolinijkowy z dwuznakiem \n w miejscu gdzie naciśnięto enter.

> "Ach, to nie było warte
+ by sny tym karmić uparte"
"Ach, to nie było warte\nby sny tym karmić uparte"

 

Do wyświetlania tekstu służą omówiona wcześniej funkcja cat() oraz funkcja print(). Działają one nieco inaczej. Funkcja print() standardowo wyświetla tekst w cudzysłowie. Opcja quote=FALSE powoduje, że tekst pojawia się bez cudzysłowu. Jest niewrażliwa na dwuznak \n. Natomiast przy podziale tekstu na dwie linijki, wstawia go w miejsce znaku enter.

> print("a aa aaa aaaa aaaaa aaaaaa") 
[1] "a aa aaa aaaa aaaaa aaaaaa"
> print("a aa aaa aaaa aaaaa aaaaaa", quote=FALSE) 
[1] a aa aaa aaaa aaaaa aaaaaa
> print("a aa aaa \n aaaa aaaaa aaaaaa", quote=FALSE)
[1] a aa aaa \n aaaa aaaaa aaaaaa 
> print("a aa aaa
+ aaaa aaaaa aaaaaa", quote=FALSE)
[1] a aa aaa\naaaa aaaaa aaaaaa 
> print("a","aa") 
Błąd w poleceniu 'print.default("a", "aa")':niepoprawny argument 'digit
Dodatkowo: Komunikat ostrzegawczy:
W poleceniu 'print.default("a", "aa")':
pojawiły się wartości NA na skutek przekształcenia

 

Największa różnica między funkcją cat() a funkcją print() polega na tym, że pierwsza z nich tylko wyświetla tekst i ten obraz nie może być przyporządkowany żadnemu obiektowi. Natomiast to, co powstanie po użyciu funkcji print(), może być traktowane jako obiekt.

W R nie przewidziano działań na tekstach. Poprawne wyświetlanie tekstu w wielu linijkach, znajdywanie podtekstu, łączenie tekstów i inne operacje wykonuje się używając funkcji tekstowych.

Relacje między znakami i tekstami.

Już w starożytności pojawiła się potrzeba zastosowania porządku w zbiorze liter (stworzono alfabet) oraz słów (porządek leksykalny). Wiele osób wie, że w porządku alfabetycznym znaki cyfr poprzedzają znaki liter, litery małe poprzedzają duże. Obecnie w encyklopediach internetowych swoje hasła mają poszczególne znaki interpunkcyjne, nawiasy, pauzy, znaki działań matematycznych i inne znaki występujące na klawiaturze. Porządek między tymi symbolami nie jest już tak powszechnie znany i program R może wyjaśnić wiele wątpliwości.

Do sprawdzenia, czy dany znak jest w porządku alfabetycznym przed/po innym znakiem używamy relacji < i >.

 

> ","<"."
[1] TRUE
> "@"<"!"
[1] FALSE

 

Porządek wyznaczony przez kolejne miejsca w unikodzie dla liter nie jest zgodny z porządkiem alfabetycznym. W unikodzie duże litery poprzedzają małe (przy czym Z < a), podczas gdy w porządku alfabetycznym zawsze mała litera jest przed dużą literą, ale obie są mniejsze od małej, następnej litery w alfabecie. Najwięcej wątpliwości wzbudza porządek liter z dodatkowymi znaczkami (kreskami, kropkami na literą, przekreśleniami itp.). Ich porządek jest ustalony przez opcje systemu operacyjnego związane z ustawieniami lokalnymi, a dokładnie językiem stosowanym w R zadeklarowanym podczas instalacji. W polskojęzycznym R obowiązuje znany nam porządek ustalony dla wszystkich polskich liter (ą, ć, ę, ł, ń, ó, ś, ż, ź) ale nie dla wszystkich liter nie używanych w języku polskim jest on jednoznaczny. Można to sprawdzić.

> intToUtf8(261)
[1] "ą"
> intToUtf8(261)=="ą"
[1] TRUE
> intToUtf8(261)>"A"
[1] TRUE
> intToUtf8(261)>"b"
[1] FALSE
> intToUtf8(192)
[1] "À"
> intToUtf8(192)=="A"
[1] FALSE
> intToUtf8(192)<"A"
[1] FALSE
> intToUtf8(192)>"A"
[1] FALSE
> intToUtf8(192)<"b"
[1] TRUE

 

Tego typu relacja jest także porządkiem tylko nieliniowym. Po prostu nie jest określona między pewnymi parami znaków. Spełnia jednak warunek antysymetryczności (jeżeli X jest w relacji z Y to nieprawdą jest, że Y jest w relacji z X) i jest przechodnia (jeżeli X jest w relacji z Y i Y jest w relacji z Z to X jest w relacji z Z).

W porządku leksykalnym przy porównaniu dwóch tekstów sprawdzane są znaki na tych samych pozycjach i pozycja danego słowa jest taka sama jak pozycja pierwszej pary różniących się znaków, przy czym brak znaku poprzedza wszystkie znaki. Dlatego też “10”<“2” i “folder11” poprzedza “folder2” ale występuje za “folder1” w katalogach komputerowych. Obecnie wiele witryn internetowych umożliwia stosowanie prawie wszystkich znaków występujących na klawiaturze do tworzenia NIKów (loginów) logujących się użytkowników. NIKi te na serwerach muszą zostać uporządkowane, choćby po to by łatwiej było sprawdzać, czy nowa, rejestrująca się osoba nie chce używać istniejącego już NIKu. Stosuje się tu porządek leksykalny, przy czym poszczególne znaki na klawiaturze mają następujące pozycje:

< < ! < # < $ < % < & < ( < ) < * < , < . < / < : < ; < ? < @ < [ < \ < ] < ^ < _ < ` < { < | < } < ~ < + < < < = < > < 0 < 1 < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < a < A < b < B < c < C < d < D < e < E < f < F < g < G < h < H < i < I < j < J < k < K < l < L < m < M < n < N < o < O < p < P < q < Q < r < R < s < S < t < T < u < U < v < V < w < W < x < X < y < Y < z < Z

Między pokazanymi znakami mają swoje miejsce jeszcze inne znaki używane np. w języku polskim. Przykładowo A < ą < Ą < b. Porzadek miedzy narodowymi symbolami ustalany jest dla każdego jezyka i system operacyjny komputera ma go zapisanego w swoich opcjach. Pracując na nie własnym komputerze za granicą (albo w Polsce ale z systemem dostosowanym do innego języka) polski porządek alfabetyczny może być nie wykrywany prawidłowo.

Funkcje tekstowe

Funkcja is.character() służy do sprawdzenia, czy jakiś obiekt jest symbolem lub napisem (typu character). Funkcja as.character() pozwala na przekształcenie liczby na napis. Funkcja as.numeric() przekształca napisy mające postać liczby na liczby.

> # funkcja is.character()
> is.character(pi)
[1] FALSE
> is.character(TRUE)
[1] FALSE
> is.character("TRUE")
[1] TRUE
> # funkcja as.character()
> as.character(pi)
[1] "3.14159265358979"
> # funkcja as.numeric()
> as.numeric("4.78654")
[1] 4.78654
> as.numeric(" 4.78654 ")
[1] 4.78654
> as.numeric("4,78654")
[1] NA
Warning message:
NAs introduced by coercion 

 

Funkcja nchar() pozwala na wyliczenie ilości znaków składających się na tekst.

> # funkcja nchar()
> nchar("abcde")
[1] 5
> nchar("abcde abc")
[1] 9
> substr("abcde abc", 3, nchar("abcde abc"))
[1] "cde abc"

Funkcja cat() zamienia liczby na teksty oraz skleja teksty występujące po przecinku rozdzielając je w tekście wynikowym spacją. Aby zmienić sposób sklejania tekstów należy użyć opcji sep=”z”, gdzie z jest wybranym znakiem.

> cat("abc", "acb", "bac", "bca", "cab", "cba \n")
[1] abc acb bac bca cab cba 
> cat("abc", "acb", "bac", "bca", "cab", "cba \n", sep=",")
[1] abc,acb,bac,bca,cab,cba 
> cat("abc", "acb", "bac", "bca", "cab", "cba \n", sep=intToUtf8(10311))
[1] abc⡇acb⡇bac⡇bca⡇cab⡇cba 
> cat("abc", "acb", "bac", "bca", "cab", "cba \n", sep="\t")
[1] abc     acb     bac     bca     cab     cba 
> cat("abc", "acb", "bac", "bca", "cab", "cba \n", sep="")
[1] abcacbbacbcacabcba 

 

Funkcja cat() posiada jeszcze opcje fill=FALSE/TRUE oraz labels=… o wartościach tekstowych. Bardzo długi tekst bez dwuznaku \n zostaje jednolinijkowym tekstem i znaczna jego część jest niewidoczna, gdy fill=FALSE, a jest to wprowadzane standardowo. Opcja fill=TRUE powoduje, że ten tekst automatycznie podzieli dzieli się (głównie w miejscu spacji) na kilka linijek. Można te linijki oznaczyć znakami wpisanymi do opcji labels.

> cat("abcd", "abdc", "acbd", "acdb", "adbc", "adcb",
+ "bacd","badc","bcad","bcda", "bdac", "bdca",
+ "cabd", "cadb", "cbad", "cbda", "cdab", "cdba",
+ "dabc", "dacb", "dbac", "dbca", "dcab", "dcba \n")
abcd abdc acbd acdb adbc adcb bacd badc bcad bcda bdac bdca cabd cadb cbad cbda cdab cdba d$
> cat("abcd", "abdc", "acbd", "acdb", "adbc", "adcb",
+ "bacd","badc","bcad","bcda", "bdac", "bdca",
+ "cabd", "cadb", "cbad", "cbda", "cdab", "cdba",
+ "dabc", "dacb", "dbac", "dbca", "dcab", "dcba \n", fill=TRUE)
abcd abdc acbd acdb adbc adcb bacd badc bcad bcda bdac bdca cabd cadb cbad cbda cdab cdba
dabc dacb dbac dbca dcab dcba
> cat("abcd", "abdc", "acbd", "acdb", "adbc", "adcb",
+ "bacd","badc","bcad","bcda", "bdac", "bdca",
+ "cabd", "cadb", "cbad", "cbda", "cdab", "cdba",
+ "dabc", "dacb", "dbac", "dbca", "dcab", "dcba \n", fill=TRUE, labels="#")
# abcd abdc acbd acdb adbc adcb bacd badc bcad bcda bdac bdca cabd cadb cbad cbda cdab cdba
# dabc dacb dbac dbca dcab dcba 

 

Kolejne linijki mogą być numerowane. Wystarczy napisać na przykład labels=c(“1″,”2″,”3″,”4″,”5”). Gdy powstanie mniej niż 5 linijek (np. 3) dostaną one oznaczenia 1, 2 i 3. Gdy powstanie więcej niż 5 linijek – kolejne oznaczenia będą się powtarzać. Ciąg wpisany do labels może być dowolnie długi i składać się z dowolnych znaków lub tekstów w dowolnej kolejności.

Funkcja print() wyświetla tekst albo liczbę, ale nie skleja różnych tekstów i tekstów z liczbami. Poza tym po wyświetleniu zawartości kursor przechodzi zawsze do nowej linijki.

> print("abc", "acb", "bac", "bca", "cab", "cba \n")
Błąd w poleceniu 'print.default("abc", "acb", "bac", "bca", "cab", "cba \n")':   invalid printing digits -2147483648
Dodatkowo: Komunikat ostrzegawczy:
W poleceniu 'print.default("abc", "acb", "bac", "bca", "cab", "cba \n")':
pojawiły się wartości NA na skutek przekształcenia
> print("abc \n")
[1] "abc \n"
> print("abc")
[1] "abc"
> print(123)
[1] 123

 

Jedna z opcji funkcji print() jest opcja logiczna quote=FALSE/TRUE. Zastosowanie wartości FALSE powoduje, że teksty wyswietlą się bez cudzysłowów. Jest to wygodne w tworzeniu różnych komunikatów. Opcja ta nie działa dla liczb.

> print("abc")
[1] "abc"
> print("abc", quote=FALSE)
[1] abc
> print(123, quote=TRUE)
[1] 123

 

Funkcja print() służy przede wszystkim do wyświetlania złożonych “obiektów” kryjących się za jakimiś literałami. Większość jej opcji słuzy do różnego rodzaju pokazywania tych obiektów. W odróżnieniu od funkcji cat(), wynikiem działania funkcji print() jest także wielkość, która można przypisać jakiemuś literałowi, czyli R-owy “obiekt”.

Do łączenia tekstów, a także tekstu z liczbami, służy funkcja paste(). Jej najpopularniejszą opcją jest sep=”z”, gdzie z jest znakiem rozdzielającym łączone słowa.

> # funkcja paste()
> paste("Ala ma", 3, "koty i", 4, "myszoskoczki." )
[1] "Ala ma 3 koty i 4 myszoskoczki."
> paste("Ala", "Ola","Ela", "Ula")
[1] "Ala Ola Ela Ula"
> paste("Ala", "Ola", "Ela", "Ula", sep=",")
[1] "Ala,Ola,Ela,Ula"
> paste("Ala", "Ola", "Ela", "Ula", sep=", ")
[1] "Ala, Ola, Ela, Ula"

 

Efektem działania funkcji paste() jest tekst, który może być przyporządkowany obiektowi.

Funkcją odwrotną do paste(), przy pomocy której możemy podzielić tekst, jest funkcja strsplit(). Jej obowiązkową opcją jest split=””, gdzie miedzy cudzysłowami dajemy znal, który nam rozdzieli tekst.

> # funkcja strsplit()
> strsplit("Ala ma 3 koty i 4 myszoskoczki.", split="i")
[[1]]
[1] "Ala ma 3 koty " " 4 myszoskoczk" "." 
strsplit("Ala ma 3 koty i 4 myszoskoczki.", split=" ")
[[1]]
[1] "Ala"           "ma"            "3"           "koty"            "i"           "4"
[2] "myszoskoczki."
strsplit("Ala ma 3 koty i 4 myszoskoczki.", split="")
[[1]]
[1] "A" "l" "a" " " "m" "a" " " "3" " " "k" "o" "t" "y" " " "i" " " "4" " " "m"
[20] "y" "s" "z" "o" "s" "k" "o" "c" "z" "k" "i" "."

 

Wynikiem działania tej funkcji jest złożona struktura, tzw lista wyjaśniona dalej. Funkcja ta działa bowiem najczęściej na ciągach tekstowych dzieląc odpowiednio każdy element tego ciągu na jakiś krótszy ciąg. By się one nie pomieszały zapamiętane są w bardziej skomplikowany sposób.

Funkcje substr(tekst,n1,n2) oraz substring(tekst,n1,n2) pozwalają na wyciąganie podtekstu z większego tekstu, jako ciągu znaków od znaku n1 do znaku n2.

> # funkcja substr()
> substr("abcdefgh", 1,3)
[1] "abc"
> substring("abcdefgh", 2,4)
[1] "bcd"
> substr("abcdefgh", 2,14)
[1] "bcdefgh"
> substr("abcdefgh", 12,14)
[1] ""

 

Do sprawdzenia, czy jakiś tekst jest podtekstem dłuższego tekstu służy funkcja grepl(). Jej wartością jest prawda (TRUE) lub fałsz (FALSE). Drugi argument tej funkcji może być wektorem. Funkcja zwraca wtedy wektor wartości logicznych pokazujących, w których wyrazach wektora znaleziono dany tekst.

> # funkcja grepl()
> grepl("bc","abcde")
[1] TRUE
> grepl("bd", "abcde")
[1] FALSE

 

Inne przydatne funkcje tekstowe to sub() oraz gsub(). Obie pozwalają na zamianę w tekście jednego znaku lub krótkiego tekstu na inny, tylko sub() robi to tylko dla pierwszego napotkanego znaku/subtekstu, a gsub() dla wszystkich wskazanych znaków/tekstów. Budowa tych funkcji jest taka sama: sub(“z1″,”z2”,tekst), gdzie znak “z1” jest znakiem/subtekstem szukanym w tekście i zamienianym na znak/subtekst “z2”. Ponieważ wynikiem takich zamian są także listy, podajemy sposób na powrót z listy do tekstu – funkcję unlist().

> # funkcja sub() i gsub()
> sub("o","re","Ala ma 3 koty i 4 myszoskoczki.")
[[1]]
[1] "Ala ma 3 krety i 4 myszoskoczki." 
> unlist(sub("o","re","Ala ma 3 koty i 4 myszoskoczki."))
[1] "Ala ma 3 krety i 4 myszoskoczki."
> gsub("o","re","Ala ma 3 koty i 4 myszoskoczki.")
[[1]]
[1] "Ala ma 3 krety i 4 myszreskreczki." 
> unlist(gsub("o","re","Ala ma 3 koty i 4 myszoskoczki."))
[1] "Ala ma 3 krety i 4 myszreskreczki." 

 

Ponieważ przy znajdywaniu pierwszego znaku/subtekstu wykorzystana jest funkcja logiczna, można w tym miejscu użyć wyrażeń logicznych i zamienić jedna instrukcja kilka różnych znaków/subtekstów.

> # funkcja sub() i gsub()
> unlist(sub("o|i","re","Ala ma 3 koty i 4 myszoskoczki."))
[1] "Ala ma 3 krety i 4 myszoskoczki." 
> unlist(gsub("o|i","re","Ala ma 3 koty i 4 myszoskoczki."))
[1] "Ala ma 3 krety i 4 myszreskreczkre." 

 

Ważne by między znakiem a | nie stosować spacji, bo są wtedy wyszukiwane dwuznaki ze spacją na końcu lub na początku wskazanego znaku.

Operacje na wartościach logicznych

 

Wstęp teoretyczny

Choć logika stanowi podstawę przede wszystkim matematyki, odgrywa też dużą rolę w innych dziedzinach. Logika nie jest zależna od płci, ale jest funkcją języka, w jakim przekazujemy zdobyte informacje. Pokazuje jak sformułować zdanie, aby jego treść była poprawnie zrozumiana przez innych i oceniona jego prawdziwość. Logika nie istnieje nadrzędnie. Nie odkrywa się jej, ale formułuje. Jest rzeczą umowną, tak jak kierunek pisma w językach europejskich.

Logika jest łatwa i trzeba się jej nauczyć, po to by nie robić błędów przy formułowaniu zdań w pracach naukowych. Dotyczy to także prac biologicznych, gdzie posługiwanie się zaprzeczeniem (nie, nieprawda, że), spójnikami (i, oraz, lub, albo) oraz implikacją (jeżeli … to …) musi być zgodne z zasadami logiki matematycznej. Jeszcze większą rolę odgrywają te zasady w programowaniu (nie tylko w R). Zestawianie ze sobą różnych relacji i funkcji logicznych musi być całkowicie zgodne z ustalonymi zasadami logiki, często nazywanej logiką matematyczną.

Zasady te polegają na zmianach wartości logicznych zdań, które mogą być prawdziwe (czyli mieć wartość logiczną TRUE) lub fałszywe (o wartości logicznej FALSE) poprzez zastosowanie negacji, koniunkcji (i, oraz), alternatywy (lub) oraz implikacji. Wartość logiczna złożenia zdań zmienia się według reguł przedstawionych w poniższych tabelach.

Zdanie
Negacja zdania
TRUE
FALSE
FALSE
TRUE

 

Zastosowanie podwójnego przeczenia tworzy zdanie o wyjściowej wartości logicznej, potrójnego – takie, jak pojedyncze zaprzeczenie. Nie jest to w pełni zgodne z gramatyką języka polskiego, toteż stosowanie zaprzeczeń w polskojęzycznych pracach naukowych musi być dobrze przemyślane.

Zdanie 1
Zdanie 2
Koniunkcja zdań
TRUE
TRUE
TRUE
FALSE
TRUE
FALSE
TRUE
FALSE
FALSE
FALSE
FALSE
FALSE

 

Jeżeli zatem piszemy “… i …” lub “… oraz…” oba człony zdania muszą być prawdziwe aby całość była prawdziwa.

Zdanie 1
Zdanie 2
Alternatywa zdań
TRUE
TRUE
TRUE
FALSE
TRUE
TRUE
TRUE
FALSE
TRUE
FALSE
FALSE
FALSE

 

Jeżeli piszemy “… lub …” wystarczy by jeden człon zdania był prawdziwy, aby całość była prawdziwa.

Zdanie 1
Zdanie 2
Implikacja zdań
TRUE
TRUE
TRUE
FALSE
TRUE
TRUE
TRUE
FALSE
FALSE
FALSE
FALSE
TRUE

 

Układ treści typu “jeżeli … to …” dotyczy wielu prac naukowych lub ich części konstruowanych wg. zasad a priori. Polega to na wyprowadzaniu wniosków z przyjętych (nie koniecznie prawdziwych) założeń. Rzadko udaje się je sformułować w postaci jednego zdania, ale zasady oceny prawdziwości całej konstrukcji są takie same. Jest ona tylko wtedy fałszywa, gdy założenia były prawdziwe, a wyciągnięty wniosek fałszywy. Metody zastosowane przy wyciąganiu wniosków fałszywych z prawdziwych założeń uważa się za błędne i to właśnie jest wartość logiczna implikacji. Z fałszywych założeń możemy wyciągnąć wnioski prawdziwe i fałszywe bez zrobienia błędu w rozumowaniu. Oczywiście poprawne wyciąganie wniosków dotyczy też przypadku, gdy z prawdziwych założeń wyciągamy prawdziwe wnioski.

 

Działania na wartościach logicznych

Wartości logiczne zdań “prawda” (TRUE) i “fałsz” (FALSE), są wartościami wielu działań oraz funkcji określonych na liczbach, znakach i tekstach. Jeżeli dużym literom T i F nie są przyporządkowane jakieś obiekty, oznaczają one domyślnie wartości logiczne – prawdę (T) i fałsz (F).

> T
[1] TRUE
> F
[1] FALSE
> True
Error: object 'True' not found 

 

Poprawność oceny wartości logicznej najlepiej sprawdzić stosując proste relacje dla liczb, których prawdziwość łatwo sami ocenimy.

> 7>5
[1] TRUE
> 3-2<1
[1] FALSE
> 3^2>2^3
[1] TRUE
> 3-2<=1
[1] TRUE
> 3-2==1
[1] TRUE
> 3-2=1
Błąd w poleceniu '3 - 2 = 1':
docelowe przypisanie rozszerza się na nie-języczny obiekt
> 3-2!=1
[1] FALSE

 

Przy okazji widać, jak określa się podstawowe relacje między liczbami w R. Symbole < i > są tak samo używane jak w matematyce, <=, >= oznaczają słabe nierówności (mniejsze lub równe, większe lub równe). Równość liczb sprawdza relacja == a nierówność !=. Stosowany w matematyce symbol = na relację równości, ma w R inne znaczenie.

Konieczność obliczania negacji, koniunkcji i alternatywy wielu wyrażeń logicznych spowodowała zaimplementowanie do R operatorów “!”, “&”, “|” działających na wyrażeniach logicznych.

> !TRUE
[1] FALSE
> !FALSE
[1] TRUE
> TRUE & TRUE
[1] TRUE
> TRUE & FALSE
[1] FALSE
> FALSE & TRUE
[1] FALSE
> FALSE & FALSE
[1] FALSE
> TRUE | TRUE
[1] TRUE
> TRUE | FALSE
[1] TRUE
> FALSE | TRUE
[1] TRUE
> FALSE | FALSE
[1] FALSE

 

Trzeba przy tym wiedzieć, że “&&” znaczy to samo co “&”, a “||” znaczy to samo co “|”. Nie dotyczy to operatora “!”. W tym wypadku obowiązują zasady podwójnego (i wielokrotnego) przeczenia.

> TRUE & TRUE
[1] TRUE
> TRUE && TRUE
[1] TRUE
> TRUE &&& TRUE
Error: unexpected '&' in "TRUE &&&"
> TRUE | FALSE
[1] TRUE
> TRUE || FALSE
[1] TRUE
> TRUE ||| TRUE
Error: unexpected '|' in "TRUE |||"
> !TRUE
[1] FALSE
> !!TRUE
[1] TRUE
> !!!TRUE
[1] FALSE 

 

Kolejność wykonywania działań logicznych ustalona jest zgodnie z zasadami przyjętymi w logice matematycznej – to znaczy negacje wykonywane są najpierw, a koniunkcja i alternatywa w drugiej kolejności. Działania równoważne wykonywane są w kolejności od lewej do prawej. Zmiana kolejności wymaga użycia nawiasów okrągłych.

> TRUE | TRUE & TRUE & FALSE
[1] TRUE
> (TRUE | TRUE) & (TRUE & FALSE)
[1] FALSE
> !FALSE & !TRUE
[1] FALSE
> !(FALSE & !TRUE)
[1] TRUE 

 

Inne ważne działania na wartościach logicznych (implikacja, równoważność) są na tyle rzadko używane w programowaniu w R, że nie wyróżniono dla nich specjalnych operatorów. Gdy zaistnieje konieczność posługiwania się nimi, można wykorzystać twierdzenia:
implikacja(p, q) = nieprawda(alternatywa(negacja p, q))
równoważność(p, q) = alternatywa(koniunkcja(p,q), koniunkcja(negacja(p), negacja(q)))

> !(!TRUE | TRUE)
[1] TRUE
> !(!TRUE | FALSE)
[1] FALSE
> !(!FALSE | TRUE)
[1] TRUE
> !(!FALSE | FALSE)
[1] TRUE
> (TRUE & TRUE)|(!TRUE & !TRUE)
[1] TRUE
> (TRUE & FALSE)|(!TRUE & !FALSE)
[1] FALSE
> (FALSE & TRUE)|(!FALSE & !TRUE)
[1] FALSE
> (FALSE & FALSE)|(!FALSE & !FALSE)
[1] TRUE

 

Wymuszenie działań arytmetycznych na wartościach logicznych powoduje, że są one traktowane jako liczby. przy czym T=TRUE=1, F=FALSE=0.

> TRUE+3
[1] 4
> FALSE*11
[1] 0

 

W języku R rzadko wykonuje się działania logiczne bezpośrednio na wartościach TRUE i FALSE. Natomiast stosuje się je przy operowaniu pewnymi wyrażeniami, których prawdziwość ocenia program. Przypisuje im wtedy domyślnie wartości TRUE lub FALSE, a następnie wykonuje na nich odpowiednie działanie.

Funkcje logiczne

W wielu językach programowania istnieją funkcje logiczne zastępujące działania, np. NOT() lub AND(). Nie ma takich funkcji w R. Jedyna funkcją której argumentami sa wartości logiczne jest xor() – odpowiednik polskiego ‘albo’.

> # funkcja xor()
> xor(TRUE,TRUE)
[1] FALSE
> xor(TRUE,FALSE)
[1] TRUE
> xor(FALSE,TRUE)
[1] TRUE
> xor(FALSE,FALSE)
[1] FALSE

 

Jest natomiast wiele funkcji, których argumentami są liczby lub teksty, a wartościami TRUE i FALSE. Są to wspomniane w poprzednich rozdziałach funkcje sprawdzające typ liczb i wyrażeń: is.numeric(), is.integer(), is.double(), is.character(). Funkcji zaczynających się od is. jest więcej. Między innymi:

  • is.finite() daje wartość TRUE dla liczb i FALSE dla Inf.
  • is.infinite() daje wartość FALSE dla liczb i TRUE dla Inf.
  • is.nan() daje wartość TRUE dla wartości nieokreślonej NaN i TRUE dla pozostałych.
  • is.na() daje wartość TRUE dla wartości pomijalnej NA i NaN oraz TRUE dla pozostałych.
> is.infinite(7/0); is.nan(7/0); is.na(7/0)
[1] TRUE
[1] FALSE
[1] FALSE
> is.infinite(sqrt(-7)); is.nan(sqrt(-7)); is.na(sqrt(-7))
[1] FALSE
Komunikat ostrzegawczy:
W poleceniu 'sqrt(-7)': wyprodukowano wartości NaN
[1] TRUE
Komunikat ostrzegawczy:
W poleceniu 'sqrt(-7)': wyprodukowano wartości NaN
[1] TRUE
Komunikat ostrzegawczy:
W poleceniu 'sqrt(-7)': wyprodukowano wartości NaN

 

Wartość NA traktowana jest jako brak danych (wartość pomijalna), natomiast NaN sa to wartości błędne, nieokreślone. Wartości błędne są traktowane jak wartości pomijalne, natomiast w drugą stronę nie ma tożsamości. Wartość pomijalna nie jest wartością błędną i czasem trzeba to rozróżnić w zbiorze danych.

Sprawdzenie czy wartością jakiegoś wyrażenia jest wartość logiczna polega na zastosowaniu funkcji is.logical(). Natomiast konwersja liczb 0 i 1 oraz wyrażeń “TRUE”, “True” itp. na wartość logiczną zachodzi poprzez zastosowanie funkcji as.logical().

> # funkcja is.logical()
> is.logical(T)
[1] TRUE
> is.logical(True)
[1] FALSE
> is.logical(1)
[1] FALSE
> # funkcja as.logical()
> as.logical(0)
[1] FALSE
> as.logical(1)
[1] TRUE
> as.logical(5.768)
[1] TRUE
> as.logical(TRUE)
[1] TRUE
> as.logical("TRUE")
[1] TRUE
> as.logical("True")
[1] TRUE
> as.logical(True)
Error: object 'True' not found
> as.logical("a")
[1] NA

 

Instrukcja przypisania i obiekty

 

Literały

W matematyce definiując np. funkcję liniową jako y=ax+b operuje się parametrami (są to litery a i b za które można podstawić dowolne liczby). W informatyce dość podobnie używa się zastępczych nazw, tylko, że liter alfabetu jest za mało dla potrzeb większości programów. Dlatego też stosuje się w miejsce parametrów tak zwane literały (zestawy liter, cyfr i innych znaków). Ten literał jest wtedy wykorzystywany w różnych instrukcjach w zastępstwie wartości, jaka się za nim kryje. Literałami nie mogą być słowa, służące do pisania instrukcji języka programowania – tzw. słowa kluczowe. Ich używanie w zastępstwie jakiś liczb, znaków i bardziej złożonych struktur mogłoby doprowadzić do niemożliwości zinterpretowania pewnych instrukcji przez komputer.

W R literałem może być dowolny zestaw liter i cyfr oraz znaków “.” i “_”, zaczynający się od litery. Słowami kluczowymi, których nie można użyć jako literałów, są:

Słowa kluczowe R:

if
else
repeat
while
function
for
in
next
break
TRUE
FALSE
NULL
Inf
NaN
NA
NA_integer_
NA_real_
NA_complex_
NA_character_

 

W języku R literały różniące się wielkością choćby jednej z liter traktowane są jako różne.

Literałom przypisuje się liczby, znaki, teksty a także funkcje, opcje funkcji i bardziej złożone obiekty. Są nazwami tych obiektów, ale kryje się zawsze za nimi konkretna wartość.

Instrukcja przypisania

Przypisanie literałom jakiejś liczby, znaku, tekstu lub innej wielkości jest instrukcją. Jest to najprostsza z instrukcji informatycznych obecna we wszystkich programach komputerowych, we wszystkich językach programowania.

Instrukcję przypisania w nowszych wersjach R można wykonać na cztery sposoby:

> assign("a",pi)
> a
[1] 3.141593
> x=pi/2
> y<-pi/2
> x+y
[1] 3.141593
> z<--pi/2
> x+z
[1] 0
> pi->b
> b
[1] 3.141593 

 

W starszych wersjach R instrukcja przypisania była realizowana tylko przez dwuznak “<-“, gdy to co z prawej przypisywano literałowi z lewej lub “->” gdy to co z lewej przypisywano literałowi z prawej. W nowszych wersjach takie sposoby przypisania także istnieją, ale występuje też znak “=”, który zastępuje symbol “<-“. Warto przy tym wiedzieć, że symbol <- dominuje nad symbolem relacji logicznej < z liczbą ujemną po prawej stronie.

> x=0
> x<-2
> x
[1] 2
> x=0
> x< -2
[1] FALSE
> x<(-2)
[1] FALSE

 

Generalnie w R liczba spacji między elementami składni nie odgrywa większej roli. Nie wolno jednak wkładać spacji w środek literału. Natomiast tworząc instrukcje bez spacji, typu x=2, język R interpretując je, dokłada automatycznie spacje wydzielając wyrażenia zdefiniowane w jego składni, a następnie sprawdza poprawność wyrażenia. Stąd są przypadki, takie jak powyżej, gdy brak spacji powoduje błędną interpretację wyrażenia.

W jednej linijce można przypisać wartości kilku literałom, pod warunkiem, że instrukcje te przedzieli się średnikiem:

> imie="Weronika"; wiek=3; wzrost=111
> imie
[1] "Weronika"
> wiek
[1] 3
> wzrost
[1] 111 

 

W polskiej wersji R w literałach używać można polskich liter wprowadzanych tak, jak ustawiono to w systemie operacyjnym. Nie oznacza to jednak, że polecam stosowanie polskich liter w programach R. Prawie zawsze sprawia to różne kłopoty przy zmianach wersji programu oraz korzystaniu z tego kodu na innych komputerach.

> ąęśćźż=345
> sqrt(ąęśćźż)
[1] 18.57418

 

Po przypisaniu jakiemyś literałowi określonej wartości trzeba w nowej linijce napisać nazwę (czyli użyty literał), żeby zobaczyć te wartość. Można to zrobić od razu obejmując całą instrukcje przypisania nawiasem okrągłym.

> x=sqrt(55.5)
> x
[1] 7.449832
> (x=sqrt(55.5))
[1] 7.449832

Jest to przydatne wtedy, gdy utworzyć obiekt i jednocześnie zobaczyć jego wartość.

 

Obiekty

Literały, którym przypisano różne wartości stają się obiektami R. Należy przy tym wiedzieć, że to co w R nazwano obiektem, w innych językach programowania obiektem nie jest. Obiekty w programach takich jal C++, Python i Paskal definiuje sie inaczej. Jednak w R zapamiętując obszar roboczy, zapamiętujemy także wszystkie obiekty zdefiniowane w danej sesji, czyli literały i wartości jakie się pod nimi kryją. Po dłuższej sesji w R takich obiektów może być bardzo dużo i nie wszystkie są potrzebne. Listę obiektów wyświetlimy za pomocą funkcji ls(). Funkcję tę można wywołać klikając na “Różne” (“Misc”) i wybierając z menu “Pokaż wszystkie obiekty” (“List object”). Gdy wszystkie obiekty są niepotrzebne można je usunąć klikając na “Różne” (“Misc”) i na “Usuń wszystkie obiekty” (“Remove all objects”). Można też wpisać w konsoli rm(list=ls(all=TRUE)). Usunięcie jednego z istniejących obiektów może być wykonane za pomocą funkcji rm(“nazwa_obiektu”).

> # funkcja ls()
> ls()
[1] "a" "pi" "T" 
> # funkcja rm()
> rm(list=ls(all=TRUE))
> ls()
character(0)
> a=1
> b="1"
> c=as.logical(1)
> ls()
[1] "a" "b" "c"
> rm("a")
> ls()
[1] "b" "c"

 

Po zobaczeniu, jakie obiekty są zdefiniowane w danej sesji, można sprawdzić, czy dobrze je zapamiętano używając funkcji logicznych. Przykładowo:

> x=-5.4; imie="Gosia"; warunek=x<0
> imie=="Anna"
[1] FALSE
> x<(-1)
[1] TRUE
> warunek==TRUE
[1] TRUE

 

Innym sposobem zorientowania się, co kryje się za danymi literałami jest zastosowanie funkcji mode(), typeof(), class() lub str().

> a=3
> b=a>-1
> a; b
[1] 3
[1] TRUE
> mode(a); mode(b)
"numeric"
"logical"
> typeof(a); typeof(b)
"double"
"logical"
> class(a); class(b)
"numeric"
"logical"
> str(a); str(b)
num 3
logi TRUE

 

W podanym przykładzie używanie tych funkcji wydaje się bezsensowne, bo łatwiej jest po prostu wyświetlić, co kryje się za literałem a. Jednak równie krótkie nazwy można nadawać obiektom bardzo złożonym, których nie da się całkowicie wyświetlić w konsoli. Wtedy pokazane funkcje okazują się bardzo przydatne.

Utworzone obiekty są pamiętane, dopóki nie wyłączy się programu R. Podczas wyłączania program pyta się, czy zapamiętać obszar roboczy. Gdy wyrazimy zgodę, w katalogu roboczym pojawi się ikonka z podpisem .RData. Nazwę jej można wprowadzić przed kropką w systemie operacyjnym bez zmiany właściwości tego wejścia do R. Możemy klikając na nią uruchomić R i będą w nim zapisane wszystkie obiekty utworzone podczas ostatniej sesji (i te, które były wcześniej). Jeżeli w kolejnych sesjach użyjemy takiego samego literału dla nowego obiektu, stary obiekt zostanie zmazany. Warto przy tym wiedzieć, że standardowo bieżącym katalogiem R jest folder “Dokumenty” (“Moje dokumenty”), a można go zmienić na dowolny inny wybierając z górnego menu słowo “Plik” i z rozwinięcia hasło “Zmień katalog”. Ponadto przed zamknięciem R można wykonać w konsoli funkcję save.image(“Moja_nazwa.RData”), gdzie za Moja_nazwa można wstawić cokolwiek, co może być nazwą pliku w danym systemie operacyjnym. Pozwala to na tworzenie własnych wejść do R ze swoimi obiektami.

> n.skunksow=50
> save.image("skunks.RData")
> 

 

Wykonanie powyższego kodu spowoduje pojawienie się w katalogu roboczym ikonki o nazwie skunks.R, a jej aktywacja spowoduje uruchomienie się R, w którym jest obiekt o nazwie n.skunksow i ma on wartość 50.

Zasady przypisywania wartości literałom

Wiele nazw skonstruowanych zgodnie z definicją literału jest zrozumiałych dla R od samego początku. Są to nazwy funkcji, stałych liczbowych i obiektów dostępnych w pakietach podstawowych (dostępnych bez wywołania nazwy pakietu) i pakietach standardowo instalowanych lub zainstalowanych dodatkowo przez użytkowników (dostępnych po słowie require nazwa pakietu). Ponadto użytkownik wymyślając na poczekaniu jakąś nazwę i przypisując jej liczbę, tekst, wartość logiczną lub jakiś bardziej złożony obiekt może użyć literału już wykorzystanego przez kogoś dla nazwy czegoś innego. Nie prowadzi to do konfliktu. Operując konfliktowym literałem określono w R następującą hierarchię przypisywania mu określonej wartości:

  1. Jeżeli literał występuje w katalogu obiektów utworzonych przez użytkownika – przypisana jest mu wartość użytkownika.
  2. Jeżeli literał nie występuje w katalogu obiektów użytkownika, ale występujących w pakietach dodatkowych – przypisana jest mu wartość tego pakietu, który był wywołany funkcją require najpóźniej.
  3. Jeżeli literał nie występuje w katalogu obiektów użytkownika i nie występujących w pakietach dodatkowych, ale jest w pakietach standardowych – przypisana jest mu wartość z pakietu standardowego.
  4. Jeżeli literał nie występuje w katalogu obiektów użytkownika i nie występujących w pakietach dodatkowych i standardowych, pojawia się komunikat, że nie ma takiego obiektu.
> pi
[1] 3.141593
> cos(pi)
[1] -1
> pi=7
> cos(pi)
[1] 0.7539023

 

Literały oznaczające nazwy funkcji (matematycznych, tekstowych, logicznych i wszystkich innych zdefiniowanych w R) pamiętane są wraz z informacją, że są to funkcje i występuje po ich nazwie nawias otwierający. Zatem, gdy takiej nazwie przypiszemy jakiś obiekt nie będący funkcją – nie dojdzie do konfliktu.

> sin(7)
[1] 0.6569866
> sin=7
> sin(sin)
[1] 0.6569866

 

Komunikaty o braku obiektu lub funkcji o danej nazwie wyglądają następująco:

> abrakadabra
BŁĄD: nie znaleziono obiektu 'abrakadabra'
> abrakadabra(3)
BŁĄD: nie udało się znaleźć funkcji 'abrakadabra' 

 

Zarządzanie literałami w R umożliwia stosowanie nazw wszystkich, jakie można sobie wymyślić (poza słowami kluczowymi), nie uzyskując komunikatów o błędzie ani błędnych działań. Jeżeli jednak chcemy wrócić do standardowych wartości – należy obiekt o konfliktowej nazwie usunąć z listy zapisanych obiektów.

> pi=7
> cos(pi)
[1] 0.7539023
> rm("pi")
> cos(pi)
[1] -1

 

Jest to także powód, by czyścić katalog własnych obiektów z niepotrzebnych nazw, a te obiekty, które chce się zapamiętać – trzymać w wejściach do R utworzonych funkcją save.image().

Data i czas

 

Tworzenie obiektów klasy Date i POSIXt z poszczególnych jednostek czasu.

Używane w różnych sytuacjach daty, mają nie do końca określona formę. Format mm/dd/y używany jest w Stanach Zjednoczonych a dd/mm/y poza Stanami Zjednoczonych, gdzie dd to dzień, mm to miesiąc, a y jest rokiem zapisanym 4 cyframi lub dwoma – dwie ostatnie cyfry roku, zazwyczaj drugiej połowy XX wieku. Równorzędnie rozpoczyna się pisanie daty od roku – y/mm/dd, (rzadziej y/dd/mm), co ułatwia np. porządkowanie dokumentów względem czasu ich podpisania. To formatowanie z myślnikami jako separatorami stosowany jest przez R. Separatorami elementów daty obok / mogą być kropki, pauzy lub myślniki. Godzina jest prawie zawsze pisana jako gg:mm:ss, gdzie gg to godzina, mm to minuty, a ss to sekundy. Niekiedy stosuje się inny separator niż dwukropek. W krajach anglosaskich godziny są liczone od 1 do 12, a po frazie określające czas dodaje się AM, gdy są to godziny przedpołudniowe lub PM, gdy czas dotyczy godzin po południu. Niekiedy trzeba do daty dodać jeszcze strefę czasową, zwłaszcza gdy porównuje się czas zdarzeń zachodzących w różnych miejscach kuli ziemskiej. Dla wielu stref trzeba także odróżnić czas zimowy od czasu letniego. Uwzględnienie tego wszystkiego powoduje, że określenie czasu cechuje się wieloma zmiennymi (od 6 do 8 dla jednej i tej samej strefy czasowej. Są to rok, miesiąc, dzień, godzina, minuta i sekunda. Siódma pozycja obejmuje rozróżnienie godzin przedpołudniowych i popołudniowych, ósma oznacza strefę czasową, która ma różne oznaczenia dla czasu letniego i zimowego.

Rok, miesiąc, dzień, godzina, minuta i sekunda danego zdarzenia najczęściej zapisuje się osobno nadając tym wielkościom liczby całkowite o umownie określonym zakresie. Funkcje, które pozwalają na połączenie tych danych i utworzenie obiektów, za którymi kryje się czas w postaci liczb to ISOdate() i ISOdatetime().

 > ISOdate(year=2023, month=04, day=15, hour=13, min=23, sec=45)
[1] "2023-04-15 13:23:45 GMT"
> ISOdate(year=2023, month=04, day=15, hour=13, min=23, sec=45, tz="")
[1] "2023-04-15 13:23:45 CEST"
> ISOdatetime(year=2023, month=04, day=15, hour=13, min=23, sec=45)
[1] "2023-04-15 13:23:45 CEST"
 

 

Z jakiś powodów funkcja ISOdate() ma wpisaną domyślnie strefę czasową (tz od time zone) “GMT” (Greenwich Mean Time, strefa południka 0) i aby operować strefą czasową odczytywaną z systemu operacyjnego należy dopisać opcję tz=””. W Polsce zazwyczaj używamy lokalnej strefy czasowej, czyli CET (od Central European Time), a latem CEST (Central European Summer Time). Skróty do poszczególnych stref czasowych, z których większość obowiązuje w R, można odczytać na stronie https://en.wikipedia.org/wiki/List_of_tz_database_time_zones w kolumnie “Time zone abbreviation”.

Gdy do funkcji ISOdate() i ISOdatetime() wpiszemy same liczby w liczbie sześciu zostaną one potraktowane w kolejności jako rok, miesiąc, dzień, godzina, minuta i sekunda.

 > ISOdate(2023, 04, 15, 13, 23, 45, tz="")
[1] "2023-04-15 13:23:45 CEST"
> ISOdate(hour=13, min=23, sec=45, day=15, month=04, year=2023, tz="")
[1] "2023-04-15 13:23:45 CEST"
> ISOdate(13, 23, 45, 15, 04, 2023, tz="")
[1] NA
 

 

Każda nieistniejąca data i czas zostaje przez funkcje generujące daty przetworzona na NA.

Wydaje się, że funkcje ISOdate() i ISOdatetime() tworza obiekty tekstowe. Nie jest to do końca prawdą. Posłużą do tego funkcje badające typ i strukturę obiektu: mode(), typeof(), class() i str().

 > x=ISOdate(year=2023, month=04, day=15, hour=13, min=23, sec=45)
> mode(x)
[1] "numeric"
> typeof(x)
[1] "double"
> class(x)
[1] "POSIXct" "POSIXt"
> str(x)
POSIXct[1:1], format: "2023-04-15 13:23:45"

 

Obiekt ten ma strukturę tekstowo-liczbową. Ukrycie liczby za formułami czasu ma logiczny sens, jako że czas jest zmienna ciągłą, która można wyrazić liczbami rzeczywistymi i jednostką w układzie SI jego pomiaru jest sekunda, którą odwrotność wylicza się jako odwrotność częstotliwości promieniowania przejścia kwantowego między dwoma nadsubtelnymi poziomami atomu cezu 133 w stanie podstawowym: ΔV(Cs) = 9 192 631 770 Hz. Aby móc wyliczać czas tak, jak to pokazuje się na wykresach w okładzie współrzędnych kartezjańskich, potrzebne jest ustalenie punktu zerowego. Ustalono taki punkt dla całego świata skomputeryzowanego o czym będzie mowa w następnym podrozdziale.

Klasa obiektów wyświetlających zrozumiałą datę i czas, ale za którymi kryją się liczby rzeczywiste oznaczające czas liczony w sekundach nazywana jest powszechnie POSIXt, choć program R podaje nazwę dwuczłonową: POSIXct POSIXt.

Data i czas jako liczby

Daty i czas w R są liczbami całkowitymi, a dokładnie data jest to liczba dni począwszy od dnia 01-01-1970 z ujemnymi wartościami dla dat wcześniejszych. Natomiast czas, to liczba sekund, jaka upłynęła od godziny 1:00 wspomnianej wcześniej doby. Wszystkie operacje na datach to operacje na tych liczbach. Towarzyszy im szereg funkcji przekształcających te liczby na zrozumiały format tekstowy daty i czasu.

Aby zobaczyć ile dni minęło od dnia 01.01.1970 do dnia 21.03.2021 należy użyć funkcji as.Date(), która przekształca tekst w postaci “rok-miesiąc-dzień” lub “rok/miesiąc/dzień” na format typu data. Zobaczmy jak to wygląda:

 > x=as.Date("1970-01-01")
> as.numeric(x)
[1] 0
> x=as.Date("2021-03-21")
> as.numeric(x)
[1] 18707
> 51*365+12+31+28+21 #Liczba.lat*365+12 od lat przestępnych+dni.stycznia2021+dni.lutego2021+21
[1] 18707

 

Datę w formacie tekstowym podaje się R-owi w postaci rok/miesiąc/dzień lub rok-miesiąc-dzień. Gdy daty zapisane zostały w innym formacie należy użyć opcji format.

 > as.Date("21-03-2021")
[1] "0021-03-20"
 as.Date("21-03-2021", format="%d-%m-%y")
[1] "2020-03-21"
> as.Date("21.3.99", format="%d.%m.%y")
[1] "1999-03-21"
 

 

Czas podawany jest poprzez datę i zestaw godzina:minuta:sekunda określone w systemie sześćdziesiątkowym, ale zapisane zwykłymi liczbami arabskimi. Przekształca się go na liczbę sekund od godziny 1:00:00 w dniu 01-01-1970 za pomocą funkcji as.POSIXct().

 > x=as.POSIXct("1970/01/01 00:00:00")
> as.numeric(x)
[1] -3600
> x=as.POSIXct("1970/01/01 01:00:00")
> as.numeric(x)
[1] 0
> x=as.POSIXct("2021/03/21 15:35:10")
> as.numeric(x)
[1] 1616337310
> 18707*24*60*60+14*60*60+35*60+10 #L.sekund w 18707 dniach+L.sekund od 01:00:00 do 15:35:10
[1] 1616337310

 

Skrót CET po godzinie zapamiętany w x oznacza strefę czasową, którą R odczytał z systemu komputerowego. W każdej innej strefie czasowej liczba dni odczytana z wprowadzonej formuły daty byłaby taka sama. Umowny moment “narodzin” czasu komputerowego (01.01.1970 01:00:00) jest zatem de facto różny w różnych strefach czasowych – przesuwa się od wschodu na zachód mniej więcej o godzinę co 15o. Takie podejście ułatwia, a nie utrudnia, przeliczenia czasu zajścia jakiegoś zdarzenia w różnych rejonach świata (trzeba po prostu dodać lub odjąć liczbę godzin lub godzin z minutami, jaka dotyczy różnicy stref czasowych, gdzie te zdarzenia miały miejsce).

Określanie formatu zapisu daty i czasu

Niemal identycznie jak as.POSIXct() działają funkcje as.POSIXlt() oraz strptime(), aczkolwiek tworzą ona datę w złożonym formacie, zwanym listą, o którym mowa będzie później. Ponadto funkcja strptime() zawsze wymaga wstawienia opcji format.

> x=as.POSIXlt("2021/03/21 15:35:10")
> mode(x)
[1] "list"
> typeof(x)
[1] "list"
> class(x)
[1] "POSIXlt" "POSIXt"
> as.numeric(x)
[1] 1616337310

> x=strptime("2021/03/21 15:35:10")
Błąd w poleceniu 'strptime("2021/03/21 15:35:10")':
brakuje argumentu 'format', a nie ma określonej wartości domyślnej
> x=strptime("2021/03/21 15:35:10", format="%Y/%m/%d %H:%M:%S")
> mode(x)
[1] "list"
> typeof(x)
[1] "list"
> class(x)
[1] "POSIXlt" "POSIXt"
> as.numeric(x)
[1] 1616337310

 

W opcji format=”” należy wpisać sposób w jakim zapisano datę i czas w formacie tekstowym. Skrót %Y oznacza rok zapisany 4 cyframi, %y – rok zapisany dwoma cyframi, %m – miesiąc, %d – dzień, %H – godzina, %M – minuta, %S sekunda. Między nimi należy wstawić te przerywniki, które są wprowadzone w formacie tekstowym. Czasami mamy zapis czasu w innej postaci niż godzina:minuta:sekunda. Na przykład, zamiast dwukropka są średniki albo pomiar wykonywany był z dokładnością do minut i brak jest ostatniego członu czasu. Wtedy także pomaga opcja format.

 > as.POSIXct("2021/03/21 15;35")
[1] "2021-03-21 CET"
> as.POSIXct("2021/03/21 15;35", format="%Y/%m/%d %H;%M")
[1] "2021-03-21 15:35:00 CET"

 

W krajach angielskojęzycznych operuje się czasem, w którym godziny podaje się liczbami od 1 do 12, a po zapisie czasu dodaje się “AM”, gdy czas dotyczy godzin przedpołudniowych lub “PM”, gdy czas dotyczy godzin popołudniowych. Wtedy w godzina ma symbol %I, a odczytanie “PM” lub “AM” następuje po zastosowaniu symbolu %p. Wygląda to następująco:

 > x= "15/03/2023 10:16:12 PM"
> as.POSIXct(x, format="%d/%m/%Y %I:%M:%S %p")
[1] "2023-03-15 22:16:12 CET"
> as.POSIXlt(x, format="%d/%m/%Y %I:%M:%S %p")
[1] "2023-03-15 22:16:12 CET"
> strptime(x, format="%d/%m/%Y %I:%M:%S %p")
[1] "2023-03-15 22:16:12 CET"

 

Określenia pory dnia “AM i “PM” mogą być pisane małymi literami, albo w sposób mieszany (jedna litera duża, druga mała). Rezultat przekształcania takich danych tekstowych na datę i czas jest taki sam.

W drugiej połowie XX wieku rok w datach podawano powszechnie pisząc dwie jego ostatnie cyfry. Obecnie widząc tak zapisaną datę nie wiemy, czy odnosi się ona do XX czy XXI wieku. Potrzebne do tego są dodatkowe wskazówki. Tymczasem funkcje as.POSIXct() i as.POSIXlt() i strptime() wstawiają w to miejsce 20, gdy rok jest mniejszy od 69 i 19, gdy rok jest równy 69 lub większy. Wszystkie te funkcje potrzebują uzupełnienia opcji format.

 > as.POSIXct("68/12/31 14:31:10")
[1] "0068-12-31 14:31:10 LMT"
> as.POSIXct("68/12/31 14:31:10",format="%y/%m/%d %H:%M:%S")
[1] "2068-12-31 14:31:10 LMT"
> as.POSIXct("69/01/01 00:00:01", format="%y/%m/%d %H:%M:%S")
[1] "1969-01-01 00:00:01 CET"
> strptime("68/01/01 23:59:59", format="%y/%m/%d %H:%M:%S")
[1] "2068-01-01 23:59:59 CET"
> strptime("69/01/01 00:00:00", format="%y/%m/%d %H:%M:%S")
[1] "1969-01-01 00:00:01 CET"

 

Operowanie tylko godziną, minutą i sekundą uniemożliwia utworzenie obiektu klasy POSIXt poprzez funkcje as.POSIXct i as.POSIXlt, chyba, że doda się do nich opcję format=”%H:%M:%S”. Wszystkie te funkcje dodają wtedy aktualną datę odczytaną z systemu operacyjnego.

 > as.POSIXct("14:31:10")
Błąd w poleceniu 'as.POSIXlt.character(x, tz, ...)':
łańcuch tekstowy nie jest w standardowym jednoznacznym formacie
> as.POSIXct("14:31:10", format="%H:%M:%S")
[1] "2023-04-14 14:31:10 CEST"
> as.POSIXlt("14:31:10", format="%H:%M:%S")
[1] "2023-04-14 14:31:10 CEST"
> strptime("14:31:10", format="%H:%M:%S")
[1] "2023-04-14 14:31:10 CEST"

 

W dalszym ciągu ograniczymy się do pokazywania wyników dla funkcji as.POSIXct().

Liczby jako data i czas

Żeby odkryć, jaka data kryje się za jakąś liczbą, należy użyć funkcji as.Date() z opcją origin=, w której trzeba zapisać “1970-01-01”. Wyświetla się wtedy data o podaną liczbę dni późniejsza po wprowadzonym do R dniu. Wygląda to następująco:

 > as.numeric(as.Date("2021-03-21"))
[1] 19707
> as.Date(18707)
Błąd w poleceniu 'as.Date.numeric(18707)':
argument 'origin' musi zostać dostarczony
> as.Date(18707, origin = "1970-01-01")
[1] "2021-03-21"
> as.Date(18707, origin = "1980-01-01")
[1] "2031-03-21"

 

Funkcja as.POSIXct() ma także opcję origin ale występuje pewna nieprawidłowość:

 > as.POSIXct(0, origin="2021-03-21 16:36:10")
[1] "2021-03-21 17:36:10 CET"
> as.POSIXct(-3600, origin="2021-03-21 16:36:10")
[1] "2021-03-21 16:36:10 CET"

 

Być może twórcy funkcji as.POSIXct() pomyśleli, że czasem zerowym dla czasu komputerowego było “1970-01-01 00:00:00”. Daje to w sumie następujące efekty:

 > as.numeric(as.POSIXct("2021-03-21 16:36:10")
[1] 1616340970
> as.POSIXct(1616340970, origin="1970-01-01 01:00:00")
[1] "2021-03-21 17:36:10 CET"
> as.POSIXct(1616340970, origin="1970-01-01 00:00:00")
[1] "2021-03-21 16:36:10 CET"

 

Tymczasem między “1970-01-01 01:00:00” a “2021-03-21 16:36:10” jest dokładnie 1616340970 sekund. Natomiast między “1970-01-01 00:00:00” a “2021-03-21 16:36:10” jest 1616344570 sekund.

Innymi słowy, posługując się funkcją as.POSIXct() (a także as.POSIXlt()) do generowaniu nowej daty i czasu trzeba od wyniku odejmować godzinę. Na szczęście poprawne wyniki można uzyskać prościej.

Odczytywanie aktualnej daty i czasu

W komputerach w latach 80-tych zaczęto umieszczać zegary systemowe, przydatne i powszechnie wykorzystywane narzędzia do określania czasu utworzenia lub modyfikacji różnych plików. Większość programów ma funkcje automatycznego odczytywania czasu systemowego, gdyż można w ten sposób sprawdzać, w których momentach program wykonywał określone etapy jakiegoś programu. Do odczytywania aktualnego czasu w R służą funkcje: date(), Sys.Date(), Sys.time(). Pokazują on datę i czas w różnych formatach.

Aktualną datę (jaka jest wpisana do systemu) odczytamy w R za pomocą funkcji Sys.Date() oraz Sys.time().

 > date()
[[1] "Thu Apr 13 13:19:26 2023"
> Sys.Date()
[1] "2023-04-13"
> Sys.time()
[1] "2023-04-13 13:19:26 CEST"
> as.numeric(Sys.time())
[1] 1681384922 

 

Liczby, które kryją sią za aktualnymi datami i czasem, wyrażanymi w sekundach, używane są jako tzw. seed, czyli inicjator w funkcjach generujących liczby losowe. Powoduje to, że programy używające takich funkcji, za każdym uruchomieniem generują inne wyniki.

Operacje możliwe dla daty i czasu

Funkcje as.Date() i as.POSIXct() zamieniające datę i czas na liczby pozwalają na błyskawiczne wyliczenia odstępu czasowego w dniach między różnymi datami. Jednocześnie twórcy R zadbali by nie można było dodawać dat i wykonywać nie zawsze sensownych działań na datach.

 > x1=as.Date("1999-01-12")
> x2=as.Date("1981-05-22")
> x3=as.Date("2013-03-14")
> min(x1,x2,x3)
[1] "1981-05-22"
> max(x1,x2,x3)
[1] "2013-03-14"
> sort(x1,x2,x3)
Błąd w poleceniu 'sort(x1, x2, x3)':
'decreasing' musi być wektorem logicznym o długości 1.
Czy chciałeś ustawić 'partial'?

 

Normalnie funkcja sort() porządkuje dane od najmniejszej do największej, ale powinny one tworzyć strukturę zwaną wektorem. Będzie jej poświęcony kolejny rozdział.

Najczęściej format danych typu Date wykorzystywany jest, aby wyliczyć czas między dwoma datami. Podawany jest on w dniach.

 > x1=as.Date("1999-01-12")
> x2=as.Date("1981-05-22")
> x1-x2
Time difference of 6444 days
> x2+6444
[1] "1999-01-12"
> x1+x2
Błąd w poleceniu '`+.Date`(x1, x2)':
dwuargumentowy operator '+' nie jest określony dla obiektów klasy "Date"

 

Sumy dni, które upłynęły po 1.01.1970, można od biedy zsumować, ale po co? Sumowanie dat jest bezsensowne. Mówi o tym wygenerowany komunikat.

Podobne operacje można wykonać dla wielkości określającej datę i czas.

 > x1=as.POSIXct("1999-01-12 01:00:00")
> x2=as.POSIXct("1999-05-22 08:30:00")
> x3=as.POSIXct("1999-05-21 15:45:00")
> x4=as.POSIXct("1999-05-21 16:10:50")
> x5=as.POSIXct("1999-05-21 16:11:11")
> min(x1,x2,x3,x4)
[1] "1999-01-12 01:00:00 CET"
> x2-x1
Time difference of 130.2708 days
> x2-x3
Time difference of 16.75 hours
> x4-x3
Time difference of 25.83333 mins
> x5-x4
Time difference of 21 secs
> x1+x2
Błąd w poleceniu '`+.POSIXt`(x1, x2)':
dwuargumentowy operator '+' nie jest określony dla obiektów klasy "POSIXt"

 

Czas między dwoma datami i obiektami klasy POSIXt można wyliczyć za pomocą funkcji difftime().

 > x1=as.Date("1999-01-12")
> x2=as.Date("1981-05-22")
> difftime(x1,x2)
Time difference of 6444 days
> x1=as.POSIXct("1999-01-12 01:00:00")
> x2=as.POSIXct("1999-05-22 08:30:00")
> x3=as.POSIXct("1999-05-21 15:45:00")
> x4=as.POSIXct("1999-05-21 16:10:50")
> x5=as.POSIXct("1999-05-21 16:11:11")
> difftime(x2,x1)
Time difference of 130.2708 days
> difftime(x2,x1,units="hours")
Time difference of 3126.5 hours
> difftime(x2,x3)
Time difference of 16.75 hours
> difftime(x2,x3,units="secs")
Time difference of 60300 secs
> difftime(x4,x3)
Time difference of 25.83333 mins
> difftime(x5,x4)
Time difference of 21 secs

 

Zastosowanie funkcji difftime() umożliwia kontrolę nad jednostkami czasu, w jakim ma zostać podany wynik. Należy zastosować opcję units, której wartościami mogą być: “days”, “hours”, “mins” i “secs”.

Zobaczmy, co się stanie, gdy do wielkości o typie POSIXct dodamy jakąś liczbę.

 > x1=as.POSIXct("1999-01-12 01:00:00")
> x2=as.POSIXct("1999-05-22 08:30:00")
> x2-x1
Time difference of 130.2708 days
> x1+130
[1] "1999-01-12 01:02:10 CET"
> x1+130.2708
[1] "1999-01-12 01:02:10 CET"
> x1+130.99
[1] "1999-01-12 01:02:10 CET"
> x1+131
[1] "1999-01-12 01:02:11 CET"
> x1+130.2708*24*60*60
[1] "1999-05-22 08:29:57 CEST"
> x1+130.2709*24*60*60
[1] "1999-05-22 08:30:05 CEST"
> x1+130.27084*24*60*60
[1] "1999-05-22 08:30:00 CEST"

 

Dodawanie liczby do obiektu typu POSIXct powoduje, ze odcięta zostaje cała część dziesiętna, a to co zostaje potraktowane jest jak liczba sekund. Oznacza to czasami konieczność przeliczenia dni, godzin i minut na sekundy (co na szczęście w przypadku dni, godzin i minut jest jednoznaczne w przeciwieństwie do liczby dni w miesiącu lub roku).

Wyciąganie informacji z obiektów o formacie daty i czasu

Za każdą datą kryje się jakiś dzień tygodnia, pora roku, numer dnia od początku roku itd. Najtrudniej jest zgadnąć, jaki dzień tygodnia odpowiada określonej dacie. W R służy do tego funkcja weekdays(), która dla każdej daty pokazuje dzień tygodnia.

 > x=as.Date("1999-01-17")
> weekdays(x)
[1] "niedziela"
> x=as.POSIXct("1999-01-17 10:28:33")
> weekdays(x)
[1] "niedziela"

 

Czasami chcemy mieć datę zapisaną w inny sposób niż to jest standardem w R. Można to uzyskać za pomocą funkcji format() i odpowiednio zapisanych opcji. Wygląda to następująco:

 > x=as.Date("1999-01-17")
> format(x, "%A %d %B %Y")
[1] "niedziela 17 styczeń 1999"
> format(x, "%j") #Numer dnia roku
[1] "017"
> format(x, "%U, %W") #Numer tygodnia roku
[1] "03, 02"

 

Ostatnie działanie dało różne odpowiedzi dla opcji %U i %W, gdyż pierwsza z nich dotyczy tygodni liczonych od niedzieli, a druga tygodni liczonych od poniedziałku. Wszystkie opcje jakie można wpisać w funkcji format podaje poniższa tabela.

Opcja Opis
%a
Skrócona nazwa tygodnia
%A
Pełna nazwa tygodnia
%b lub %h
Skrócona nazwa miesiąca
%B
Pełna nazwa miesiąca
%d
dzień miesiąca
%j
dzień
%m
numer miesiąca
%u
numer dnia tygodnia (0:6) z niedzielą mającą numer 0
%w
numer dnia tygodnia (1:7) z niedzielą mająca numer 7
%U
numer tygodnia roku z niedzielą jako pierwszym dniem
%W
numer tygodnia roku z poniedziałkiem jako pierwszym dniem
%y
rok bez wieku (2 ostatnie cyfry roku)
%Y
rok z wiekiem (4 cyfry roku)
%C
wiek (2 pierwsze cyfry roku)
%x
data w formacie określonym przez system
%D
data według formatu amerykańskiego (mm/dd/yyyy)

 

Możemy też wyciągać informacje o godzinie, minucie i sekundzie danego czasu. służą do tego opcje %H (godzina), %M (minuta) i %S (sekunda).

Przy badaniach dobowej lub sezonowej zmienności różnych zjawisk przyrodniczych chciałoby się policzyć, ile z nich było bliskie kolejnym godzinom, dniom, miesiącom itp., albo zdarzyło się w kolejnych godzinach, dniach, miesiącach itp. Można w tym celu wykorzystać możliwość “zaokrąglania” daty i czasu do godziny, doby, miesiąca itp. Służy do tego funkcja round() z opcją units. Częściej jednak (choć nie zawsze prawidłowo) używa się obcinanie mniejszych jednostek czasu. Służy do tego funkcja trunc().

 > x=as.POSIXct("1999-08-17 16:38:33")
> round(x,units="mins")
[1] "1999-08-17 16:39:00 CEST"
> trunc(x,units="mins")
[1] "1999-08-17 16:38:00 CEST"
> round(x,units="hours")
[1] "1999-08-17 17:00:00 CEST"
> trunc(x,units="hours")
[1] "1999-08-17 16:00:00 CEST"
> round(x,units="days")
[1] "1999-08-18 CEST"
> trunc(x,units="days")
[1] "1999-08-17 CEST"
> round(x,units="months")
[1] "1999-09-01 CEST"
> trunc(x,units="months")
[1] "1999-08-01 CEST"
> round(x,units="years")
[1] "2000-01-01 CET"
> trunc(x,units="years")
[1] "1999-01-01 CET"

 

Uzyskane wyniki są zapisane w formacie POSIXt lub Date i dopiero funkcja format() pozwoli nam na wybranie konkretnych liczb oznaczających godzinę, miesiąc, rok itp.

Wektory

 

Tworzenie wektorów

Wektory są to skończone ciągi elementów tego samego typu (liczbowe, tekstowe, logiczne lub bardziej złożonych typów, np. wektor wektorów). Wektor można utworzyć i zapamiętać pod dowolną nazwą poprzez zastosowanie składni:

nazwa.wektora=c(x1,x2,…)Tworzenie wektorów poprzez stosowanie funkcji c(), o dowolnej liczbie argumentów rozdzielonych przecinkami, stosowane było i jest w niektórych starych językach programowania. W języku S, a po nim R, po prostu zastosowano tę nazwę.

> # utworzenie wektora liczb i zapamiętanie go pod nazwą "nazwa"
> nazwa=c(1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1, 11.11,
+ 12.12, 13.13, 14.14, 15.15)
> # zobaczenie co kryje się pod wektorem "nazwa"
> nazwa
 [1]  1.10  2.20  3.30  4.40  5.50  6.60  7.70  8.80  9.90 10.10 11.11
[12] 12.12 13.13 14.14 15.15
> nazwa=c("ab", "ac", "ad", "bb", "bc", "bd", "ca", "cb", "cd", "ce",
+ "db", "dc", "de", "ea", "ef")
 [1] "ab" "ac" "ad" "bb" "bc" "bd" "ca" "cb" "cd" "ce" "db" "dc" "de"
[14] "ea" "ef"
> nazwa=c("a",1)
> nazwa
[1] "a" "1"

 

Zastosowanie w ciągu wyrazów wektora choć jednego elementu o typie tekstowym powoduje, że wszystkie elementy tego ciągu będą typu tekstowego.

Wektory to uporządkowane ciągi. Elementy wektora nazywane są wyrazami. Każdy wyraz ma swoją pozycję w ciągu zwaną indeksem będącym liczba naturalną. Pierwszy wyraz ma indeks 1. Nie istnieje wyraz zerowy w żadnym wektorze.

Automatyczne tworzenie standardowych wektorów umożliwiają następujące wyrażenia i funkcje.

  • integer(n) ciąg złożony z n zer o typie integer,
  • numeric(n) ciąg złożony z n zer o typie double,
  • double(n) ciąg złożony z n zer o typie double,
  • n1:n2 (gdzie n1 i n2 są liczbami całkowitymi). Gdy n1<n2 powstaje ciąg rosnący n1, n1+1, n1+2,…,n2, gdy n1>n2 powstaje ciąg malejący n1, n1-1,… ,n2,
  • seq(a,b,by=r) ciąg arytmetyczny o pierwszym wyrazie a, różnicy ciągu równej r i ostatnim wyrazie będącym liczbą nie większą niż b,
  • rep(n,m) ciąg złożony z m liczb n,
  • rep(x, times=n) ciąg złożony z n ciągów x jeden za drugim,
  • rep(x, each=n) ciąg złożony z n powtórzeń każdego wyrazu ciągu x po kolei.
> -3:10
[1] -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10
> 8:-4
[1] 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4
> seq(-5.4,3.2,by=0.5) 
 [1] -5.4 -4.9 -4.4 -3.9 -3.4 -2.9 -2.4 -1.9 -1.4 -0.9 -0.4 0.1 0.6
[14] 1.1 1.6 2.1 2.6 3.1
> seq(-5.4,3.2,length=18)
 [1] -5.4000000 -4.8941176 -4.3882353 -3.8823529 -3.3764706 -2.8705882
[7] -2.3647059 -1.8588235 -1.3529412 -0.8470588 -0.3411765  0.1647059
[13]  0.6705882  1.1764706  1.6823529  2.1882353  2.6941176  3.2000000
> rep(1,30)
[1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
> c=c(1,3,2,0)
> rep(c, times=5)
[1] 1 3 2 0 1 3 2 0 1 3 2 0 1 3 2 0 1 3 2 0
> rep(c, each=5)
[1] 1 1 1 1 1 3 3 3 3 3 2 2 2 2 2 0 0 0 0 0

 

Funkcja seq() pozwala także na automatyczne tworzenie wektorów zawierających daty i czas. Jest to przydatne zwłaszcza przy tworzeniu kalendarza dla zbieranych danych, gdyż liczba dni w poszczególnych miesiącach i latach nie jest taka sama.

> seq(as.Date("2020-01-01"), as.Date("2020-03-01"), by="days")
[1] "2020-01-01" "2020-01-02" "2020-01-03" "2020-01-04" "2020-01-05" "2020-01-06"
[7] "2020-01-07" "2020-01-08" "2020-01-09" "2020-01-10" "2020-01-11" "2020-01-12"
[13] "2020-01-13" "2020-01-14" "2020-01-15" "2020-01-16" "2020-01-17" "2020-01-18"
[19] "2020-01-19" "2020-01-20" "2020-01-21" "2020-01-22" "2020-01-23" "2020-01-24"
[25] "2020-01-25" "2020-01-26" "2020-01-27" "2020-01-28" "2020-01-29" "2020-01-30"
[31] "2020-01-31" "2020-02-01" "2020-02-02" "2020-02-03" "2020-02-04" "2020-02-05"
[37] "2020-02-06" "2020-02-07" "2020-02-08" "2020-02-09" "2020-02-10" "2020-02-11"
[43] "2020-02-12" "2020-02-13" "2020-02-14" "2020-02-15" "2020-02-16" "2020-02-17"
[49] "2020-02-18" "2020-02-19" "2020-02-20" "2020-02-21" "2020-02-22" "2020-02-23"
[55] "2020-02-24" "2020-02-25" "2020-02-26" "2020-02-27" "2020-02-28" "2020-02-29"
[61] "2020-03-01"
> seq(as.Date("2021-01-01"), as.Date("2021-03-01"), by="days")
[1] "2021-01-01" "2021-01-02" "2021-01-03" "2021-01-04" "2021-01-05" "2021-01-06"
[7] "2021-01-07" "2021-01-08" "2021-01-09" "2021-01-10" "2021-01-11" "2021-01-12"
[13] "2021-01-13" "2021-01-14" "2021-01-15" "2021-01-16" "2021-01-17" "2021-01-18"
[19] "2021-01-19" "2021-01-20" "2021-01-21" "2021-01-22" "2021-01-23" "2021-01-24"
[25] "2021-01-25" "2021-01-26" "2021-01-27" "2021-01-28" "2021-01-29" "2021-01-30"
[31] "2021-01-31" "2021-02-01" "2021-02-02" "2021-02-03" "2021-02-04" "2021-02-05"
[37] "2021-02-06" "2021-02-07" "2021-02-08" "2021-02-09" "2021-02-10" "2021-02-11"
[43] "2021-02-12" "2021-02-13" "2021-02-14" "2021-02-15" "2021-02-16" "2021-02-17"
[49] "2021-02-18" "2021-02-19" "2021-02-20" "2021-02-21" "2021-02-22" "2021-02-23"
[55] "2021-02-24" "2021-02-25" "2021-02-26" "2021-02-27" "2021-02-28" "2021-03-01"

 

Inne opcje, jakie wtedy można dodać do funkcji seq(), to length.out=n – liczba dat lub okresów czasowych jaka ma być w wektorze i along.with=x, gdzie x jest jakimś wektorem do którego dopasowujemy kolejne daty.

Każda wielkość liczbowa, znakowa, tekstowa i logiczna traktowana jest jako wektor jednoelementowy. Wektor może być dowolnej długości ograniczony tylko rozmiarami pamięci operacyjnej. Ponadto można zdefiniować wektor pusty (0 elementowy) instrukcją przypisania:

x=NULLx=numeric(0), gdy wiemy, że x to wektor liczbowyx=character(0), gdy wiemy, że x to wektor tekstowyx=logical(0), gdy wiemy, że x to wektor logicznyJest to bardzo przydatne, gdy opracowanie danych wiąże się z sekwencyjnym uzyskiwaniem wyników, które dopisujemy do wektora “wynik”. Wtedy na początku przypisujemy mu wartość wynik=NULL, a potem stosujemy instrukcję wynik=c(wynik, kolejna_wartość).

Nazywanie elementów wektora

Niekiedy poszczególne elementy wektora mają swoje nazwy. Takie wektory tworzy się przypisując nazwom elementy wektora. Wygląda to następująco:

> wek=c(Ala=1.1, Ola=2.2, Ela=3.3, Ula=4.4, Iga=5.5)
> wek
Ala Ola Ela Ula Iga
1.1 2.2 3.3 4.4 5.5
> wek=c("Ala"=1.1, "Ola"=2.2, "Ela"=3.3, "Ula"=4.4, "Iga"=5.5)
> wek
Ala Ola Ela Ula Iga
1.1 2.2 3.3 4.4 5.5

 

Nazwy można wpisać w cudzysłowie lub bez. R potraktuje je jako teksty.

Nazywanie elementów ciągu może być zautomatyzowane. Nazwy mogą się powtarzać lub być puste.

> nazwa=c(liczba=c(3.1, 2.2, 1.3, 7.4, 6.5, 5.6, 4.7))
> nazwa
liczba1 liczba2 liczba3 liczba4 liczba5 liczba6 liczba7
    3.1     2.2     1.3     7.4     6.5     5.6     4.7
> a=c(L=c(1, 3, 2.2, 5))
> b=c(2, 4, 5.4, 6)
> c(a,b)
 L1  L2  L3  L4
1.0 3.0 2.2 5.0 2.0 4.0 5.4 6.0
> b=c(L=c(2, 4, 5.4, 6))
> c(a,b)
 L1  L2  L3  L4  L1  L2  L3  L4
1.0 3.0 2.2 5.0 2.0 4.0 5.4 6.0

 

Innym sposobem nazywania elementów wektora utworzonego wcześniej jest użycie funkcji names().

> populacja=c(1732707, 707504, 760701, 633802, 546503)
> names(populacja)=c("Warszawa", "Łódź", "Kraków", "Wrocław", "Poznań")
> populacja
Warszawa     Łódź   Kraków  Wrocław   Poznań 
 1732707   707504   760701   633802   546503 

 

Funkcja names() pozwala nie tylko nazywać wyrazy ciągu, ale także umożliwia wyświetlanie nazw elementów wektora, o ile istnieją.

> a=c(L=c(1, 3, 2.2, 5))
> b=c(2, 4, 5.4, 6)
> c(a,b)
 L1  L2  L3  L4
1.0 3.0 2.2 5.0 2.0 4.0 5.4 6.0
> names(a)
[1] "L1" "L2" "L3" "L4"
> names(b)
NULL
> names(c)
[1] "L1" "L2" "L3" "L4" ""  ""  ""  ""

 

Zmianę nazw wektora w można uzyskać poprzez zwykła przypisanie nowych nazw wektorowi names(w). Ma to postać names(w)=ciąg_nowych_nazw.

Pozbycie się nazw elementów z jakiegoś ciągu można uzyskać stosując również funkcję names() w postaci names(ciag)=NULL.

> a=c(L=c(1, 3, 2.2, 5))
> a
 L1  L2  L3  L4
1.0 3.0 2.2 5.0
> names(a) = NULL
> a
[1] 1.0 3.0 2.2 5.0

 

 

Wektory zaimplementowane do R

Do R zaimplementowano kilka przydatnych stałych będących wektorami tekstowymi.

  • LETTERS duże litery alfabetu łacińskiego w porządku alfabetycznym,
  • letters małe litery alfabetu łacińskiego w porządku alfabetycznym,
  • month.abb trzyliterowe skróty angielskich nazw miesięcy,
  • month.name angielskie nazwy miesięcy.
> LETTERS
 [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q"
[18] "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
> letters
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q"
[18] "r" "s" "t" "u" "v" "w" "x" "y" "z"
> month.abb
 [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov"
[12] "Dec"
> month.name
 [1] "January"   "February"  "March"     "April"     "May"
[6]  "June"      "July"      "August"    "September" "October"
[11] "November"  "December" 

 

Wektor islands jest z kolei wektorem liczbowym z nazwanymi wyrazami pokazującym powierzchnie największych obszarów lądowych kuli ziemskiej. Powierzchnie te wyrażone są w tysiącach mil kwadratowych (1 mi2= 2,59 km2).

> islands
Africa            Antarctica        Asia             Australia        Axel Heiberg
11506             5500            1 6988             2968             16
Baffin            Banks             Borneo           Britain          Celebes
184               23                280              84               73
Celon             Cuba              Devon            Ellesmere        Europe
25                43                21               82               3745
Greenland         Hainan            Hispaniola       Hokkaido         Honshu
840               13                30               30               89
Iceland           Ireland           Java             Kyushu           Luzon
40                33                49               14               42
Madagascar        Melville          Mindanao         Moluccas         New Britain
227               16                36               29               15
New Guinea        New Zealand (N)   New Zealand (S)  Newfoundland     North America
306               44                58               43               9390
Novaya Zemlya     Prince of Wales   Sakhalin         South America    Southampton
32                13                29               6795             16
Spitsbergen       Sumatra           Taiwan           Tasmania         Tierra del Fuego
15                183               14               26               19
Timor             Vancouver         Victoria
13                12                82

 

Nie-amerykanom czasem mogą się przydać zaimplementowane do R trzy wektory charakteryzujące 50 stanów USA:

  • state.abb – dwuliterowe skróty nazw poszczególnych stanów,
  • state.area – powierzchnie poszczególnych stanów w milach kwadratowych,
  • state.name – pełne nazwy poszczególnych stanów.
> state.name
 [1] “Alabama”        “Alaska”         “Arizona”        “Arkansas”       “California”
[6] “Colorado”       “Connecticut”    “Delaware”       “Florida”        “Georgia”
[11] “Hawaii”         “Idaho”          “Illinois”       “Indiana”        “Iowa”
[16] “Kansas”         “Kentucky”       “Louisiana”      “Maine”          “Maryland”
[21] “Massachusetts”  “Michigan”       “Minnesota”      “Mississippi”    “Missouri”
[26] “Montana”        “Nebraska”       “Nevada”         “New Hampshire”  “New Jersey”
[31] “New Mexico”     “New York”       “North Carolina” “North Dakota”   “Ohio”
[36] “Oklahoma”       “Oregon”         “Pennsylvania”   “Rhode Island”   “South Carolina”
[41] “South Dakota”   “Tennessee”      “Texas”          “Utah”           “Vermont”
[46] “Virginia”       “Washington”     “West Virginia”  “Wisconsin”      “Wyoming”

> state.abb
 [1] “AL” “AK” “AZ” “AR” “CA” “CO” “CT” “DE” “FL” “GA” “HI” “ID” “IL” “IN” “IA” “KS” “KY”
[28] “LA” “ME” “MD” “MA” “MI” “MN” “MS” “MO” “MT” “NE” “NV” “NH” “NJ” “NM” “NY” “NC” “ND”
[35] “OH” “OK” “OR” “PA” “RI” “SC” “SD” “TN” “TX” “UT” “VT” “VA” “WA” “WV” “WI” “WY”

> state.area
 [1]  51609 589757 113909  53104 158693 104247   5009   2057  58560  58876   6450  83557
[13]  56400  36291  56290  82264  40395  48523  33215  10577   8257  58216  84068  47716
[25]  69686 147138  77227 110540   9304   7836 121666  49576  52586  70665  41222  69919
[37]  96981  45333   1214  31055  77047  42244 267339  84916   9609  40815  68192  24181
[49]  56154  97914

 

Takich wektorów zaimplementowanych do R jest wiele. Wykaz wszystkich obiektów z danymi wprowadzonych do R, których oglądanie i używanie nie wymaga instalowania nowych bibliotek, można zobaczyć na stronie “http://127.0.0.1:28315/library/datasets/html/00Index.html#M”, na którą można przejść z R wpisując w konsoli ?datasets, a na wyświetlonej stronie klikając na index. W większości są to jednak bardziej złożone obiekty niż wektor (najczęściej bazy danych), o których będzie mowa w dalszych rozdziałach.

Odwołanie się do wybranych wyrazów wektora

Odwołanie się do i-tego wyrazu wektora zapamiętanego pod nazwą v polega na napisaniu v[i]. Indeksy są liczbami naturalnymi od 1 do d gdzie d jest długością wektora, ale R dopuszcza wpisanie za indeks liczby rzeczywistej, którą zamienia sam na liczbę całkowitą odcinając jej część ułamkową. Można odwołać się do kilku na raz wyrazów danego wektora, pod warunkiem, że odpowiednia lista indeksów jest ciągiem. Wynikiem takiego odwołania jest wektor złożony z jednego lub kilku wyrazów wektora wyjściowego bądź też wartości pomijalnej NA, gdy dla danego indeksu ciąg nie ma określonego wyrazu.

> v=0:10
> v[1]
[1] 0
> v[0]
integer(0)
> v[5.5]
[1] 4
> v[7:10]
[1] 6 7 8 9
> v[12]
[1] NA
> v[7:12]
[1] 6 7 8 9 10 NA
> v[c(1,4,7)]
[1] 0 3 6

 

Odwołać się można do wybranych elementów wektora poprzez eliminację wyrazów na wybranych miejscach. Ciąg eliminowanych wyrazów należy poprzedzić znakiem -.

> v=0:10
> v[-1]
[1] 1 2 3 4 5 6 7 8 9 10
> v[-7:10]
[1] 0 1 2 3 4 5
> v[-12]
[1] NA
> v[-c(1,4,7)]
[1] 1 2 4 5 7 8 9 10

 

Gdy wektor ma nazwane elementy można do nich odwołać się poprzez nazwę.

> x=c(Ala=1, Ola=3, Ela=7, Ula=11)
> x
Ala Ola Ela Ula
1   3   7  11
> x["Ula"]
Ula
11

 

Odwołanie się do szczególnych wyrazów wektora może zachodzić także poprzez warunek logiczny. Wynikiem jest wektor złożony z wyrazów ciągu wyjściowego spełniających ten warunek.

> v=0:10
> v[c(TRUE,TRUE,FALSE,FALSE,TRUE,TRUE,FALSE,FALSE,TRUE,TRUE,FALSE)]
[1] 0 1 4 5 8 9
> v[v>5]
[1] 6 7 8 9 10
> v[v%%2==0]
[1] 0 2 4 6 8 10

 

 

Edycja wektorów

Zamianę i-tej wartości wektora na inną wykonuje się poprzez odwołanie się do i-tej wartości i zastosowanie instrukcji przypisania. Można w ten sposób zmienić wartości całych grup wyrazów, a stosując warunki logiczne można wyliczyć np. wartości bezwzględne (moduły) kolejnych wyrazów ciągu.

> v=c(1.10, -2.20, 13.30, -4.40, 5.50, -6.60, 7.70, -8.80, 9.90, -10.10)
> v[5]=-7
> v
[1] 1.1 -2.2 13.3 -4.4 -7.0 -6.6 7.7 -8.8 9.9 -10.1
> v[v<0]=-v[v<0]
> v
[1] 1.1 2.2 13.3 4.4 7.0 6.6 7.7 8.8 9.9 10.1
> v[c(1,3,5,7,9)]=c(-1,-2,-3,-4,-5)
> v
[1] -1.0 2.2 -2.0 4.4 -3.0 6.6 -4.0 8.8 -5.0 10.1 

 

Utworzony wektor czasem trzeba poprawić skreślając w nim pewne elementy. Skreślanie elementów polega na napisaniu indeksów elementów, które chcemy wyrzucić ze znakiem -.

> v=c(1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9)
> v[-1]
[1] 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9
> v=v[-1:3]
Błąd w poleceniu 'v[-1:3]':tylko zera mogą być mieszane z ujemnymi indeksami
> v=v[-(1:3)]
> v
[1] 4.4 5.5 6.6 7.7 8.8 9.9
> v=v[-3:-20]
> v
[1] 1.1 2.2

 

Dodawanie nowych elementów do wektora na jego końcu i automatycznie wydłużenie go, łączenie dwóch wektorów, realizowane jest poprzez zastosowanie funkcji c(). Dodanie elementu x do wektora ma postać: c(wektor,x) a łączenie dwóch wektorów wygląda następująco c(wektor1,wektor2).

> v=c("a1", "b2", "c3", "d4", "e5", "f6", "g7")
> w=c(v,"h8")
> w
[1] "a1" "b2" "c3" "d4" "e5" "f6" "g7" "h8"
> c(w,c("k11","l12"))
 [1] "a1" "b2" "c3" "d4" "e5" "f6" "g7" "h8" "k11" "l12"
> c(v[1:5],w[6:8])
 [1] "a1" "b2" "c3" "d4" "e5" "f6" "g7" "h8" 

 

To samo można uzyskać za pomocą funkcji append(). Funkcja ta ma opcje pozwalające na wpisanie wektora wymienionego z prawej wewnątrz wektora wymienionego z lewej za pomocą opcji after=n, gdzie n jest liczbą naturalną pokazującą po ilu wyrazach wektora z lewej ma być dołączony wektor z prawej. Spełnia zatem taką rolę jak funkcje wstaw (insert) w innych językach programowania.

> v=c(1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1)
> v
[1] 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.1
> append(v, 0:5, after=3)
 [1] 1.1 2.2 3.3 0.0 1.0 2.0 3.0 4.0 5.0 4.4 5.5 6.6 7.7 8.8 9.9 10.1

 

Możliwe jest także ręczne poprawianie utworzonego wcześniej wektora przez jego wyświetlenie go za pomocą funkcji fix(), zmianę, wstawienie lub usunięcie wybranego wyrazu i zapamiętanie zmian. Funkcja fix() zastosowana na wektorze tworzy okienko podobne do skryptu, gdzie działa mysz i edycja jest prosta.

Zmiany należy później zapamiętać. Po zapamiętaniu możemy wrócić do konsoli i operować zmienionym wektorem do końca sesji. Jeżeli chcemy by obiekt w postaci tego wektora istniał i był na stale zmieniony należy przy wychodzeniu z R zapamiętać obszar roboczy.

Działania na wektorach liczbowych

 W matematyce działania +, -, *, /, ^ wstawiane są między dwie liczby. Tak samo dzieje się w R, co pokazano w rozdziale 1. Jednak w R działania te zostały uogólnione na dowolne wektory i należy zobaczyć jak to działa.

Dodawanie i odejmowanie wektorów jest takie same jak w matematyce, gdy wektory są tej samej długości. Jest definiowane następująco:

(a1, a2, …,an) + (b1, b2, …, bn) = (a1 + b1, a2 + b2, …, an + bn)(a1, a2, …,an) – (b1, b2, …, bn) = (a1 – b1, a2 – b2, …, an – bn)W R uogólniono tę definicje na sumę wektorów o różnej długości. Przyjęto zasadę, że wektor krótszy jest wydłużany do długości wektora dłuższego w taki sposób, że dopisywane są do jego końca kolejne jego wyrazy od początku począwszy. Wygląda to następująco:

> c(1,2,3) + 4
[1] 5 6 7
> c(1, 2, 3)+c(4,5)
[1] 5 7 7
Komunikat ostrzegawczy:
W poleceniu 'c(1, 2, 3) + c(4, 5)':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
> c(1,2,3)+c(4,5,6)
[1] 5 7 9
> c(1,2,3)+c(4,5,6,7)
[1] 5 7 9 8
Komunikat ostrzegawczy:
W poleceniu 'c(1, 2, 3) + c(4, 5, 6, 7)':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu

 

W przypadku gdy różnice długości wektorów są takie, że nie można wektora dłuższego wyrazić jako wielokrotności wektora krótszego – pojawia się komunikat ostrzegawczy, choć działanie zostaje wykonane.

Zupełnie tak samo zdefiniowano mnożenie, dzielenie i potęgowanie wektorów. Nie są to działania stosowane w matematyce w rachunku wektorowym i nie powinny być mylone z różnymi odmianami mnożenia wektorów, np. z iloczynem tensorowym.

> c(1,2,3)*4
[1] 4 8 12
> c(1, 2, 3)*c(4,5)
[1] 4 10 12
Komunikat ostrzegawczy:
W poleceniu 'c(1, 2, 3) * c(4, 5)':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
> c(1,2,3)*c(4,5,6)
[1] 4 10 18
> c(1,2,3)*c(4,5,6,7)
[1] 4 10 18 8
Komunikat ostrzegawczy:
W poleceniu 'c(1, 2, 3) * c(4, 5, 6, 7)':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu

 

Jest możliwe także podniesienie do potęgi wyrażającej sie wektorem dowolnego wektora liczbowego.

> c(1,2,3)^4
[1] 1 16 81
> c(1, 2, 3)^c(4,5)
[1] 1 32 81
Komunikat ostrzegawczy:
W poleceniu 'c(1, 2, 3)^c(4, 5)':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
> c(1,2,3)^c(4,5,6)
[1] 1 32 729
> c(1,2,3)^c(4,5,6,7)
[1] 1 32 729 1
Komunikat ostrzegawczy:
W poleceniu 'c(1, 2, 3) ^ c(4, 5, 6, 7)':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu

 

Analogiczne zasady dotyczą także działań na liczbach całkowitych: wyliczania reszty z dzielenia %% oraz wyliczania ile razy liczba z prawej mieści się w lewej %/%.

Relacje dla wektorów liczbowych.

Zasada wydłużania krótszego wektora do długości dłuższego, poprzez powtarzanie wyrazów i porównywania ze sobą liczb na tych samych pozycjach, obowiązuje także dla relacji. Wygląda to następująco:

> x=c(-1,1,-1,1,-1,1)
> x
[1] -1  1 -1  1 -1  1
> y=-1:1
> y
[1] -1  0  1
> x == y
[1] TRUE FALSE FALSE FALSE FALSE  TRUE
> x < y
[1] FALSE FALSE  TRUE FALSE  TRUE FALSE
> y>=x
[1] TRUE FALSE TRUE FALSE TRUE TRUE

 

Specyficzną relacją jest sprawdzanie czy jakiś element lub elementy wektora występują w jakimś innym wektorze. Służy do tego relacja %in%

> x=c(-1,1,-1,1,-1,1)
> x
[1] -1  1 -1  1 -1  1
> y=-1:1
> y
[1] -1  0  1
> -1 %in% x
[1] TRUE
> 0 %in% x
[1] FALSE
> y %in% x
[1] TRUE FALSE TRUE

 

Wynikiem zastosowanie relacji %in%, jest wektor o długości takiej samej, jak wektor z lewej strony, o wartości logicznych pokazujących, czy odpowiedni wyraz pierwszego wektora znajduje się w obrębie drugiego wektora.

Funkcje matematyczne na wektorach liczbowych.

Znane z lekcji matematyki funkcje liczbowe zostały szczegółowo opisane w rozdziale “Operacje na liczbach” i podrozdziale “Funkcje matematyczne”. Wszystkie je można zastosować na wektorach liczbowych zgodnie z zasadą:

Wyrazy wektora, które nie mieszczą się w dziedzinie funkcji f() zostają po przekształceniu zamienione na NaN.

> x=-3:3/2
> x
[1] -1.5 -1.0 -0.5 0.0  0.5  1.0  1.5
> exp(x)
 [1] 0.2231302 0.3678794 0.6065307 1.0000000 1.6487213 2.7182818 4.4816891
> log(x,2)
 [1]       NaN       NaN       NaN      -Inf -1.0000000 0.0000000 0.5849625
Komunikat ostrzegawczy:
wyprodukowano wartości NaN
> gamma(x)
[1] 2.3632718       NaN -3.5449077       NaN 1.7724539 1.0000000 0.8862269
Komunikat ostrzegawczy:
W poleceniu 'gamma(x)': wyprodukowano wartości NaN

 

Obok opisanego uogólnienia funkcji matematycznych, są w R funkcje przekształcające wektor na wektor. Dotyczy to sortowania, inwersji elementów ciągu i innych operacji. Najważniejsze z nich to:

  • rev() ciąg zapisany w odwrotnej kolejności,
  • sort() dane od najmniejszej do największej,
  • order() pozycje elementów wektora począwszy od wartości najmniejszej do największej,
  • rank() rangi kolejnych elementów wektora – numery elementów wektora, gdy posortuje się je w kolejności od najmniejszej do największej, przy czym elementom o tej samej wartości przypisuje się średnią arytmetyczną z odpowiadających im numerów,
  • diff() różnice między kolejnymi wyrazami wektora,
  • cumsum() ciąg sum wyrazów od pierwszego do i-tego.
  • cumprod() ciąg iloczynów wyrazów od pierwszego do i-tego.
  • cummin() ciąg najmniejszych wyrazów wektora od pierwszego do i-tego.
  • cummax() ciąg największych wyrazów wektora od pierwszego do i-tego.
> wek=c(5.5, 2.2, 8.8, 4.4, 5.5, 8.8)
> rev(wek)
[1] 8.8 5.5 4.4 8.8 2.2 5.5
> sort(wek)
[1] 2.2 4.4 5.5 5.5 8.8 8.8
> order(wek)
[1] 2 4 1 5 3 6
> rank(wek)
[1] 3.5 1.0 5.5 2.0 3.5 5.5
> diff(wek)
[1] -3.3 6.6 -4.4 1.1 3.3
> cumsum(wek)
[1] 5.5 7.7 16.5 20.9 26.4 35.2
> cumprod(wek)
[1] 5.500 12.100 106.480 468.512 2576.816 22675.981
> cummin(wek)
[1] 5.5 2.2 2.2 2.2 2.2 2.2
> cummax(wek)
[1] 5.5 5.5 8.8 8.8 8.8 8.8

 

Szczególną rolę odgrywa funkcja which(). Jej argumentem musi być ciąg o wartościach logicznych skonstruowany zgodnie z zasadą: nazwa_wektora==inny_wektor, a wartościami są indeksy wyrazów wektora, dla których nazwa_wektora==inny_wektor ma wartość TRUE.

> ciag = c(10.1, 8.2, 3.3, 6.4, 5.5, 6.0, 3.7, 8.8, 9.9, 1.1, 2.0, 6.12, 5.1, 4.14, 3.0)
> ciag==round(ciag,0)
[1] [1] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE
[13] FALSE FALSE  TRUE
> which(ciag==round(ciag,0))
[1] 6 11 15

 

Zastosowana funkcja which() pokazała miejsca, gdzie w wektorze ciag znajdywały się liczby całkowite.

Funkcje statystyczne na wektorach liczbowych.

Funkcje statystyczne są funkcjami matematycznymi, ale dość specyficznymi. Działają bowiem na całych wektorach o dowolnej długości dając często w wyniku jedną liczbę. Pojawiły się w matematyce dopiero gdzieś w XXVII wieku i służyły powstałym wtedy urzędom państwowym do charakteryzowania populacji ludzkiej zamieszkującej dany kraj. Zaczęły odgrywać one dużą rolę w organizacji i funkcjonowaniu państwa. Opracowanie takich danych (w tym tworzenie i interpretacja sensownych funkcji przekształcających te dane) została nazwana statystyką (od łac. status = państwo). Dość szybko znalazły one zastosowanie w innych dziedzinach nauki, gdzie dokonuje się pomiarów obiektów w jakiś zbiorowościach i wykonuje szereg eksperymentów dających wyniki liczbowe.

Dziedziną funkcji matematycznych jest liczba, para liczb lub wektory o konkretnej długości. Zbór wszystkich wektorów o długości n oznaczyć można Rn, gdzie R jest zbiorem liczb rzeczywistych. Dziedziną funkcji statystycznych są wektory o dowolnej długości, czyli zbiór ∪nRn. Nie jest to jedyna różnica. Funkcje statystyczne mają bowiem to do siebie, że ich wynik charakteryzuje wektor w sensowny, interpretowalny sposób.

Podstawowe funkcje charakteryzujące wektor to:

  • length() liczba elementów wektora,
  • min() najmniejszy element wektora,
  • max() największy element wektora,
  • which.min() miejsce pierwszego najmniejszego elementu wektora,
  • which.max() miejsce pierwszego największego elementu wektora,
  • range(nazwa) wartość minimalna i maksymalna wektora.
> w = 0:10
> length(w)
[1] 11
> min(w)
[1] 0
> which.min(w)
[1] 1
> range(w)
[1] 0 10

 

Gdy opracowanie danych wymaga nie tylko wyliczenia np. wartości minimalnej wektora, ale policzenia ile wyrazów ma wartość minimalną i w których miejscach wektora się one znajdują, należy postępować w następujący sposób:

> w=c(7,3,8,3,9,3,11,3,11)
> min(w)
[1] 3
> w==min(w)
[1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE
> w[w==min(w)]
[1] 3 3 3 3
> #liczba wartości minimalnych
> length(w[w==min(w)])
[1] 4
> #miejsca wartości minimalnych
> which(w==min(w))
[1] 2 4 6 8

 

Wektor wyników pomiarów osobników jakiejś badanej populacji, komórek jakiejś tkanki i innych obiektów biologicznych nazywany jest w biologii (i statystyce) próbą. Funkcje charakteryzujące taki wektor nazywane są charakterystykami z próby. Najważniejsze z tych funkcji to:

  • sum() suma wszystkich elementów,
  • prod() iloczyn wszystkich elementów,
  • mean(nazwa) średnia arytmetyczna,
  • mean(nazwa,trim=0.05) średnia arytmetyczna po odrzuceniu 5% skrajnych wyników,
  • median(nazwa) mediana wektora,
  • quantile(nazwa,p) kwantyl rzędu p,
  • sd(nazwa)odchylenie standardowe z próby,
  • var(nazwa) wariancja z próby.
> w = c(1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1, 11.11, 12.12, 13.13, 14.14, 15.15)
> mean(w)
[1] 8.35
> var(w)
[1] 19.51486
> sd(w)
[1] 4.417562
> min(w)
[1] 1.1
> max(w)
[1] 15.15
> median(w)
[1] 8.8

 

Warto zaznaczyć, że wariancja, var(), wyliczana jest ze wzoru:

gdzie

Odchylenie standardowe wyliczne za pomocą funkcji sd() jest pierwiastkiem z tak wyliczanej wariancji.

Niektóre (stare) podręczniki do opracowania danych podają następujący wzór na wyliczenie wariancji:

Wzór ten nie obowiązuje dla wariancji liczonej obecnie według podanego pierwszego wzoru. Jest on prawdziwy dla wariancji wyliczanej jako suma kwadratów odchyleń danych od średniej podzielona przez n, nie n-1.

Jedną z najczęściej używanych funkcji statystycznych wyliczającą od razu kilka podstawowych charakterystyk wektora jest summary().

> w = c(1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1, 11.11, 12.12, 13.13, 14.14, 15.15)
> summary(w)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
1.10    4.95    8.80    8.35   11.61   15.15 

 

Wynikiem działania funkcji summary() jest 6-elementowy wektor z podstawowymi charakterystykami statystycznymi wektora: minimum, pierwszy kwartyl (kwantyl rzedu 0.25), mediana, średnia, 3 kwartyl (kwantyl rzedu 0.75) i maksimum.

Funkcje na wektorach tekstowych

Typ wektora zależy od typu jego wyrazów. Badamy go używając funkcji mode(), typeof(), class() lub str().

> w1=as.integer(c(1,2,3,4))
> w2=c(1.0,2.0,3.1,4.1)
> w3=c("1", "2", "3", "a")
> mode(w1); mode(w2); mode(w3)
[1] "numeric"
[1] "numeric"
[1] "character"
> typeof(w1); typeof(w2); typeof(w3)
[1] "integer"
[1] "double"
[1] "character"
> class(w1); class(w2); class(w3)
[1] "integer"
[1] "numeric"
[1] "character"
> str(w1); str(w2); str(w3)
int [1:4] 1 2 3 4
num [1:4] 1 2 3.1 4.1
chr [1:4] "1" "2" "3" "a"

 

Wektory tekstowe można w różny sposób przetwarzać za pomocą funkcji działających na indeksach i funkcji logicznych, podobnie jak wektory liczbowe. Pokazano to na przykładzie ciągu tekstowego (oznaczeń płci “f” (samica), “m” samiec, “o” obojnak).

> ciag=c("f","f","m","o","o","f","m","m","f","o")
> ciag
[1] "f" "f" "m" "o" "o" "f" "m" "m" "f" "o"
> dobre = ciag=="f"
> dobre
[1] TRUE TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE
> ciag[dobre]
[1] "f" "f" "f" "f"
> ciag[ciag=="f"]
[1] "f" "f" "f" "f"
> length(ciag[ciag=="f"])
[1] 4
> ciag=="f" | ciag=="o"
[1] TRUE TRUE FALSE TRUE TRUE TRUE FALSE FALSE TRUE TRUE
> ciag[ciag=="f" | ciag=="o"]
[1] "f" "f" "o" "o" "f" "f" "o"
> length(ciag[ciag=="f" | ciag=="o"])
[1] 7

 

Ponadto na wektorach tekstowych działają następujące funkcje:

  • length() liczba elementów wektora,
  • min() pierwszy w kolejności alfabetycznej wyraz tekstowy,
  • min() ostatni w kolejności alfabetycznej wyraz tekstowy,
  • rev() ciąg zapisany w odwrotnej kolejności,
  • sort() ciąg w kolejności alfabetycznej,
  • order() numery wyrazów elementów w wektorze po uporządkowaniu ich w kolejności alfabetycznej,
  • rank() numery wyrazów po uporządkowaniu ich w kolejności alfabetycznej, przy czym takie same wyrazy mają rangę równą średniej arytmetycznej numerów kolejności, które na nie przypadają.
> wek=c("Ala", "Ola", "Ela", "Ula", "Iza", "Ewa", "Ala" )
> length(wek)
[1] 7
> min(wek)
[1] "Ala"
> rev(wek)
[1] "Ala" "Ewa" "Iza" "Ula" "Ela" "Ola" "Ala"
> sort(wek)
[1] "Ala" "Ala" "Ela" "Ewa" "Iza" "Ola" "Ula"
> order(wek)
[1] 1 7 3 6 5 2 4
> rank(wek)
[1] 1.5 6.0 3.0 7.0 5.0 4.0 1.5

 

Za pomocą operatora == wektory tekstowe można porównywać pod względem identyczności wyrazów (czyli na którym miejscu w obu wektorach są takie same teksty), przy czym zgodnie z ogólnymi zasadami R, krótszy wektor jest wydłużany poprzez wielokrotne powielanie go. Natomiast operator %in% powoduje sprawdzenie czy kolejne wyrazy wektora z lewej strony są równe któremukolwiek wyrazowi wektora z prawej strony.

> weka=c("a", "b", "c")
> wekb=c("a", "a", "b", "b", "c", "c", "d")
> weka==wekb
[1] TRUE FALSE FALSE FALSE FALSE TRUE FALSE
Komunikat ostrzegawczy:
W poleceniu 'a == wekb':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
> weka %in% wekb
[1] TRUE TRUE TRUE
> wekb %in% weka
[1] TRUE TRUE TRUE TRUE TRUE TRUE FALSE

 

W rozdziale pokazującym działania na znakach i tekstach omówiono funkcję paste() sklejającą kilka tekstów w jeden. Nie działa ona na wektorze tekstów gdy jest wywołana bez opcji collapse=”. Operacja odwrotna do tej funkcji: strsplit() omówiona jest w rozdziale Listy.

> # funkcja paste()
> paste("Ala", "Ola", "Ela", "Ula", sep=", ")
[1] "Ala, Ola, Ela, Ula"
> ciag=c("Ala", "Ola", "Ela", "Ula")
> paste(ciag)
[1] "Ala" "Ola" "Ela" "Ula"
> paste(ciag, sep='')
[1] "Ala" "Ola" "Ela" "Ula"
> paste(ciag, collapse='')
[1] "AlaOlaElaUla"
> paste(ciag, collapse='; ')
[1] "Ala; Ola; Ela; Ula"

 

Najczęściej jednak funkcja paste() służy do automatycznego numerowanych wektorów testowych. Jest to przy opisie wyników, projektowaniu nagłówków itp. bardzo przydatna możliwość.

> paste("miesiąc",1:12)
[1] "miesiąc 1" "miesiąc 2" "miesiąc 3" "miesiąc 4" "miesiąc 5" "miesiąc 6" "miesiąc 7"
[8] "miesiąc 8" "miesiąc 9" "miesiąc 10" "miesiąc 11" "miesiąc 12"
> paste(letters,1:26, sep="")
 [1] "a1" "b2" "c3" "d4" "e5" "f6" "g7" "h8" "i9" "j10" "k11" "l12" "m13" "n14" "o15"
[16] "p16" "q17" "r18" "s19" "t20" "u21" "v22" "w23" "x24" "y25" "z26"

 

Funkcja substring() z opcjami n1= (pierwszy znak podtekstu) i n2= (ostatni znak podtekstu) gdy zastosujemy ciągi zamiast pojedynczych liczb, utworzy wektor fragmentów tekstu lub ciąg znaków.

> substring("statystyka",1:10,10)
[1] "statystyka" "tatystyka"  "atystyka"   "tystyka"    "ystyka"
[6] "styka"      "tyka"       "yka"        "ka"         "a"
> substring("statystyka",1,1:10)
[1] "s"         "st"         "sta"       "stat"      "staty"
[6] "statys"    "statyst"    "statysty"  "statystyk" "statystyka"
> substring("statystyka",1:10,1:10)
  [1] "s" "t" "a" "t" "y" "s" "t" "y" "k" "a"

 

Funkcja tekstowa grepl() pozwalająca na sprawdzenie czy dany tekst zawiera się w innym dłuższym tekście również może działać na wektorze tekstowym i sprawdzać, w których tekstach zawiera się pierwszy tekst.

> c=c('abdjy','skahtd','abdtsfe','asba','cxwqab')
> grepl('ab',c)
[1] TRUE FALSE TRUE FALSE TRUE

 

Funkcje zmieniające wektor liczbowy na tekstowy i odwrotnie

Zamiana liczb na tekst następuje po zastosowaniu funkcji as.character(), którą można zastosować dla dowolnego wektora liczbowego. Funkcja do niej odwrotna as.numeric() zamienia teksty mające postać liczb nma liczby.

> x=c(10.28, 8.64, 5.67, 10.41, 6.20, 8.07, 7.90, 5.33, 8.12, 6.39, 6.71, 6.13)
> as.character(x)
[1] "10.28" "8.64" "5.67" "10.41" "6.2" "8.07" "7.9" "5.33" "8.12" "6.39" "6.71" "6.13"
> c=c("10.28", "Ala", "5.67", "10.41", "ma", "8.07", "7.9", "5.33", "8.12", "kota", "6.71", "6.13")
> as.numeric(c)
[1] 10.28   NA 5.67 10.41   NA 8.07 7.90 5.33 8.12   NA 6.71 6.13
Komunikat ostrzegawczy:
pojawiły się wartości NA na skutek przekształcenia

 

Do zamiany wektora liczbowego na tekstowy można też użyć funkcji paste().

> x=c(10.28, 8.64, 5.67, 10.41, 6.20, 8.07, 7.90, 5.33, 8.12, 6.39, 6.71, 6.13)
> paste(x)
[1] "10.28" "8.64" "5.67" "10.41" "6.2" "8.07" "7.9" "5.33" "8.12" "6.39" "6.71" "6.13"
> paste(1:10)
[1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10"

 

 

Wartość nieokreślona i pomijalna w wektorze

W rozdziale o procedurach stosowanych dla liczb pokazano, że pewne działania (dzielenie zera przez zero; różnica Inf – Inf) generują wynik NaN. Jest to wartość nieokreślona. Gdy wyniki pewnej funkcji zapisujemy w ciągu, to mogą się tam pojawić elementy NaN. Dla takiego ciągu nie da się wyliczyć funkcji statystycznych.

> x = 1:9 - 5
> x
[1] -4 -3 -2 -1 0 1 2 3 4
> y = 9:1-5
> y
[1] 4 3 2 1 0 -1 -2 -3 -4
> x/y
[1] -1 -1 -1 -1 NaN -1 -1 -1 -1
> mean(x/y)
[1] NaN 

 

Czasem w ciągu wyników jakiegoś doświadczenia trzeba umieścić wartość, która w obliczeniach statystycznych będzie pomijana. Robi się to w przypadku gdy wyniku tego z różnych powodów nie ma, co wcale nie oznacza, że taki doświadczenie ma wynik np. równy 0 lub nieokreślony. Może w tym czasie aparatura odczytująca ten wynik zepsuła się, może trzeba go było odrzucić jako niewiarygodny, może próbka zaginęła lub zniszczyła po wykonaniu innych eksperymentów, z których nie należy rezygnować. W bazach danych wstawia się w to miejsce wartość pomijalną. Może to być puste miejsce (w Excelu, Accessie), kropka (w SASie), liczba -32768 (w Statgrafie), zestaw liter “MISS” (w programie sx). W R jest to zestaw dwóch liter NA. Mogą zastępować zarówno liczby jak i symbole.

Wartość pomijalna NA, podobnie jak NaN, uniemożliwia wykonanie niektórych funkcji statystycznych.

> x=c(-1,0,2,-1,NaN,0,NA)
> length(x)
[1] 7
> mean(x)
[1] NaN
> x=c(-1,0,2,-1,0,NA)
> mean(x)
[1] NA

 

Wartość pomijalna NA w R nie jest traktowana zatem tak samo jak wartość pomijalna w programach Excel, Access, SAS itd. W tamtych programach po prostu jest pomijana i wszelkie funkcje statystyczne są wykonywane na odpowiednio krótszym ciągu. Aby w R wykonywać obliczenia statystyczne na ciągach zawierających NA lub NaN należy użyć funkcji is.na() generującej ciąg wartości logicznych TRUE lub FALSE w zależności od tego czy odpowiada mu wartość NA lub NaN w ciągu użytym jako argument.

> x=c(-1,0,2,3,NA,0,NA,-1,NaN)
> is.na(x)
[1] FALSE FALSE FALSE FALSE TRUE FALSE TRUE FALSE TRUE
> !is.na(x)
[1] TRUE TRUE TRUE TRUE FALSE TRUE FALSE TRUE FALSE
> x[!is.na(x)]
[1] -1 0 2 3 0 -1
> sum(x[!is.na(x)])
[1] 3

 

Takie funkcje jak sum(), prod(), mean(), var(), sd(), min(), max() i range() wykonywane na wektorach, w których są wartości nieokreślone lub pomijalne wymagają użycia formuł:

sum(nazwa_wektora[!is.na(nazwa_wektora)])

prod(nazwa_wektora[!is.na(nazwa_wektora)])

mean(nazwa_wektora[!is.na(nazwa_wektora)])

var(nazwa_wektora[!is.na(nazwa_wektora)])

sd(nazwa_wektora[!is.na(nazwa_wektora)])

min(nazwa_wektora[!is.na(nazwa_wektora)])

max(nazwa_wektora[!is.na(nazwa_wektora)])

range(nazwa_wektora[!is.na(nazwa_wektora)])

Co ciekawe, nie dotyczy to funkcji which.min() i which.max().

 

Szeregi czasowe

 

Tworzenie szeregów czasowych

Szeregi czasowe, a właściwie serie danych, którym przypisywany jest czas (ang time series) to dość częsty rodzaj danych związanych z rozliczeniami finansowymi, analizą wskaźników stanu gospodarki, które zazwyczaj wykonuje się raz na miesiąc lub raz na kwartał. Aby ułatwić zapisywanie takich danych w R, wymyślono rodzaj zmiennych zwanych po polsku szeregami czasowymi. W biologii trafiają się doświadczenia, których wyniki zapisuje się regularnie, co ustalony przedział czasowy i wyniki takiego eksperymentu można zapisać jako wektor danych z informacją od jakiej chwili rozpoczęto pomiary i co jaki czas je powtarzano.

Szeregi czasowe podlegają innej analizie statystycznej, niż wektory danych pomiarowych wykonywanych w tym samym czasie lub niezależnych od czasu. Standardowa analiza statystyczna dotyczy właśnie takich wektorów i opiera się o niezależność tych danych od siebie. Wyniki analiz nie powinny wtedy zależeć od kolejności danych występujących w takim ciągu. Natomiast kolejne wyniki pomiarów w szeregu czasowym w dużym stopniu zależą od wyniku pomiaru poprzedniego (np. średnica grzybni rosnącej na pożywce), zwłaszcza gdy pomiary wykonywane są często. Kolejność zapisu danych ma w nich istotne znaczenie.

Szeregi czasowe tworzy się funkcją ts(), której argumentem są wektory danych.

> cc=ts(c(3:1,1:3,c(2,3,1)))
> cc
Time Series:
Start = 1
End = 9
Frequency = 1
[1] 3 2 1 1 2 3 2 3 1
> cc=ts(c("c", "a", "b", "a", "b", "c", "c", "b", "c"))
> cc
Time Series:
Start = 1
End = 9
Frequency = 1
[1] c a b a b c c b c

 

Szeregi czasowe można utworzyć z wektorów o nazwanych wyrazach.

> d=c(L=c(0,1,4,0,0,1,4,0,1))
> ts(d)
Time Series:
Start = 1
End = 9
Frequency = 1
L1 L2 L3 L4 L5 L6 L7 L8 L9
0  1  4  0  0  1  4  0  1

 

Funkcja ts() standardowo ustala czas rozpoczęcia obserwacji (początek obserwacji) jako 1 i częstotliwość obserwacji jako co 1. Rzadko odpowiada to potrzebom. Zmianę tych wielkości uzyskuje się poprzez zastosowanie opcji start i frequency lub opcji deltat. Opcja frequency to liczba określająca ile razy w czasie 1 (który dodaje się do liczby podanej jako start) dokonano pomiaru. Opcja deltat jest odwrotnością frequencji i określa co jaki ułamek 1 dokonywano pomiaru. Tylko jedna z opcji frequency lub deltat może być użyta. W przypadku gdy frequency=4, deltat=0.25 lub frequency=12, deltat=1/12 przypisuje się automatycznie znaczenie co miesiąc lub co kwartał i szereg czasowy jest inaczej wyświetlany.

> aa=ts(1:36, start=1987, frequency=3)
> aa
Time Series:
Start = c(1987, 1)
End = c(1998, 3)
Frequency = 3
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
[33] 33 34 35 36
> aa=ts(1:36, start=1987, frequency=4)
> aa
Qtr1 Qtr2 Qtr3 Qtr4
1987    1    2    3    4
1988    5    6    7    8
1989    9   10   11   12
1990   13   14   15   16
1991   17   18   19   20
1992   21   22   23   24
1993   25   26   27   28
1994   29   30   31   32
1995   33   34   35   36
> aa=ts(1:36, start=1987, frequency=12)
> aa
    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1987   1   2   3   4   5   6   7   8   9  10  11  12
1988  13  14  15  16  17  18  19  20  21  22  23  24
1989  25  26  27  28  29  30  31  32  33  34  35  36
> aa=ts(1:36, start=1987, deltat=1/12)
> aa
    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1987   1   2   3   4   5   6   7   8   9  10  11  12
1988  13  14  15  16  17  18  19  20  21  22  23  24
1989  25  26  27  28  29  30  31  32  33  34  35  36

 

Pomimo, że dane wyświetlają się w tablicy, nadal jest to wektor z dodatkowymi informacjami, które pozwalają na określenie czasu pomiaru.

Opcja start nie musi być liczbą całkowitą i może mieć charakter wektora dwuelementowego. Pierwsza liczba w tym wektorze określa czas w większej jednostce, a druga – ile jednostek typu deltat trzeba do niej dodać.

> aa=ts(1:36, start=1987.75, deltat=1/12)
> aa
    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1987                                       1   2   3
1988   4   5   6   7   8   9  10  11  12  13  14  15
1989  16  17  18  19  20  21  22  23  24  25  26  27
1990  28  29  30  31  32  33  34  35  36
> aa=ts(1:36, start=c(1987,10), deltat=1/12)
> aa
    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1987                                       1   2   3
1988   4   5   6   7   8   9  10  11  12  13  14  15
1989  16  17  18  19  20  21  22  23  24  25  26  27
1990  28  29  30  31  32  33  34  35  36

 

Funkcja ts() ma jeszcze opcję: end. Nie zastępuje ona (niestety) opcji frequency lub deltat, co mogłoby zachodzić, gdyby podany w niej czas przyporządkować ostatniej z danych. Pokazuje ona po prostu, do której wartości ciągu (której odpowiada czas podany w end) dane powinny być wpisane do szeregu czasowego. Gdy czas podany w end przewyższa czas odpowiadający ostatniej zmiennej dane z ciągu zostają powielone, czasem wielokrotnie.

> kk=ts(letters, start=0, end=5, frequency=2)
> kk
Time Series:
Start = c(0, 1)
End = c(5, 1)
Frequency = 2
[1] a b c d e f g h i j k
> kk=ts(letters, start=0, end=30, frequency=2)
> aa
Time Series:
Start = c(0, 1)
End = c(30, 1)
Frequency = 2
[1] a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w
[50] x y z a b c d e f g h i

 

Szeregi czasowe zaimplementowane do R

Do R zaimplementowano kilkadziesiąt ciekawych szeregów czasowych, na których można ćwiczyć różne operacje. Przykładem takiego szeregu jest obiekt Nile pokazujący roczny przepływ wód Nilu w milionach metrów sześciennych wody na wysokości Assuanu w czasie 100 lat (do 1871 do 1970).

> Nile
Time Series:
Time Series:
Start = 1871
End = 1970
Frequency = 1
[1] 1120 1160  963 1210 1160 1160  813 1230 1370 1140  995  935 1110  994 1020  960 1180
[18]  799  958 1140 1100 1210 1150 1250 1260 1220 1030 1100  774  840  874  694  940  833
[35]  701  916  692 1020 1050  969  831  726  456  824  702 1120 1100  832  764  821  768
[52]  845  864  862  698  845  744  796 1040  759  781  865  845  944  984  897  822 1010
[69]  771  676  649  846  812  742  801 1040  860  874  848  890  744  749  838 1050  918
[86]  986  797  923  975  815 1020  906  901 1170  912  746  919  718  714  740

 

Można tez ćwiczyć na szeregu czasowym lynx pokazującym liczby złowień rysi w Kanadzie w latach 1821 – 1934. Dane pochodzą z pracy Campbell, M. J.and A. M. Walker (1977). A Survey of statistical work on the Mackenzie River series of annual Canadian lynx trappings for the years 1821–1934 and a new analysis. Journal of the Royal Statistical Society series A, 140, 411–431.

> lynx
Time Series: Start = 1821
End = 1934
Frequency = 1
[1]  269  321  585  871 1475 2821 3928 5943 4950 2577  523   98  184  279  409 2285 2685
[18] 3409 1824  409  151   45   68  213  546 1033 2129 2536  957  361  377  225  360  731
[35] 1638 2725 2871 2119  684  299  236  245  552 1623 3311 6721 4254  687  255  473  358
[52]  784 1594 1676 2251 1426  756  299  201  229  469  736 2042 2811 4431 2511  389   73
[69]   39   49   59  188  377 1292 4031 3495  587  105  153  387  758 1307 3465 6991 6313
[86] 3794 1836  345  382  808 1388 2713 3800 3091 2985 3790  674   81   80  108  229  399
[103] 1132 2432 3574 2935 1537  529  485  662 1000 1590 2657 3396

 

Zainteresowanym polecam też bardzo długi szereg czasowy pokazujący średnią liczbę plam słonecznych w kolejnych miesiącach o nazwie sunspots.month. Dane te pochodzą z Królewskiego Obserwatorium Astronomicznego w Belgii na podstawie obserwacji prowadzonych od 1700 roku. Można za jego pomocą weryfikować hipotezy, czy plamy słoneczne mają jakiś wpływ na życie na Ziemi.

Odwołanie się do elementów szeregu czasowego

Odwołanie się do wybranych wartości szeregu czasowego jest takie same jak odwołanie się do tych wartości w wektorze.

> a=ts(c(5,6,4,3,6,5,7,5),start=c(14,3), frequency=7)
> a[7]
[1] 7
> aa[17]
[1] NA

 

Jeżeli chcemy zobaczyć wektor czasu, trzeba użyć funkcji time(szereg.czasowy).

> a=ts(c(3,2,5,6,4,3,6,5,7,5),start=c(10,5), frequency=7)
> a
Time Series:
Start = c(10, 5)
End = c(11, 7)
Frequency = 7
[1] 3 2 5 6 4 3 6 5 7 5> time(a)
Time Series:
Start = c(10, 5)
End = c(11, 7)
Frequency = 7
[1] 10.57143 10.71429 10.85714 11.00000 11.14286 11.28571 11.42857 11.57143 11.71429 11.85714
> c(time(a)[7],a[7])
[1] 11.42857 6.00000

 

Jeżeli chcemy wyświetlić początek pomiarów należy użyć funkcji start(). Funkcja end() pokazuje koniec obserwacji. Funkcja frequency() pokaże częstotliwość pomiarów w jednostce czasu, a funkcja deltat() co jaki czas wykonywane były pomiary.

> a=ts(c("a","a","b","a","c","b","c"),start=c(28.6,0.5), frequency=15)
> a
Time Series:
Start = 28.5666666666667
End = 28.9666666666667
Frequency = 15
[1] a a b a c b c
> start(a)
[1] 28.56667
> end(a)
[1] 28.96667
> frequency(a)
[1] 15
> deltat(a)
[1] 0.06666667

 

Edycja szeregów czasowych

Poprzez odwołanie szereg[n] można zamienić wartość n-tego wyrazu czynnika

> b=ts(1:10,start=3,frequency=4)
> b
 Qtr1 Qtr2 Qtr3 Qtr4
3    1    2    3    4
4    5    6    7    8
5    9   10
> b[6]=15
> b
 Qtr1 Qtr2 Qtr3 Qtr4
3    1    2    3    4
4    5   15    7    8
5    9   10

 

Metoda tą nie da się zamienić wielkości związanych z czasem (start(szereg.czasowy), frequency(szereg.czasowy) i innych)

> b=ts(1:10,start=c(32),frequency=5)
> b
Time Series:
Start = c(32, 1)
End = c(33, 5)
Frequency = 5
[1] 1 2 3 4 5 6 7 8 9 10
> start(b)
[1] 32 1
> start(b)=33
Błąd w poleceniu 'start(b) = 33':nie udało się znaleźć funkcji 'start<-'
> frequency(b)
[1] 5
> frequency(b)=6
Błąd w poleceniu 'frequency(b) = 6':nie udało się znaleźć funkcji 'frequency<-'

 

Gdy istnieje konieczność poprawienia danych czasowych (start, frequency) należy utworzyć nowy szereg czasowy na wektorze as.vectos(szereg.czasowy).

> b=ts(1:10,start=c(32),frequency=5)
> b
> b
Time Series:
Start = c(32, 1)
End = c(33, 5)
Frequency = 5
[1] 1 2 3 4 5 6 7 8 9 10
> b=ts(as.vector(b), start=33, frequency=6)
> b
Time Series:
Start = c(33, 1)
End = c(34, 4)
Frequency = 6
[1] 1 2 3 4 5 6 7 8 9 10

 

Wykrawanie fragmentu szeregu czasowego wykonuje się za pomocą funkcji window(), w której dwa parametry liczbowe oznaczają początek i koniec czasu jakich wykrojone dane dotyczą.

> b=ts(1:10,start=c(3,2),frequency=4)
> b
  Qtr1 Qtr2 Qtr3 Qtr4
3         1    2    3
4    4    5    6    7
5    8    9   10
> c=window(b, start=3.75, end=5)
> c
 Qtr1 Qtr2 Qtr3 Qtr4 3                   3
4    4    5    6    7
5    8
> d=window(b, 3.2, 5)
> d
  Qtr1 Qtr2 Qtr3 Qtr4
3         1    2    3
4    4    5    6    7
5    8
Komunikat ostrzegawczy:
W poleceniu 'window.default(x, ...)': argument 'start' nie został zmieniony

 

Może zaistnieć potrzeba utworzenia szeregu czasowego z co drugiego lub co trzeciego wyrazu istniejącego już szeregu. Wykonuje się to także funkcją window() z opcją frequency.

> b=ts(1:100,start=c(1900,1),frequency=12)
> b
    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1900   1   2   3   4   5   6   7   8   9  10  11  12
1901  13  14  15  16  17  18  19  20  21  22  23  24
1902  25  26  27  28  29  30  31  32  33  34  35  36
1903  37  38  39  40  41  42  43  44  45  46  47  48
1904  49  50  51  52  53  54  55  56  57  58  59  60
1905  61  62  63  64  65  66  67  68  69  70  71  72
1906  73  74  75  76  77  78  79  80  81  82  83  84
1907  85  86  87  88  89  90  91  92  93  94  95  96
1908  97  98  99 100
> window(b, frequency=2)
Time Series:
Start = c(1900, 1)
End = c(1908, 1)
Frequency = 2
[1]  1  7 13 19 25 31 37 43 49 55 61 67 73 79 85 91 97

 

Wszelkie łączenia szeregów czasowych polegają na dokładnym dopasowaniu momentów czasowych. Takie dopasowanie momentów czasowych dwóch szeregów umożliwiają funkcje ts.union() i ts.intersect(). Tworzą one obiekty typu: wielokrotny szereg czasowy (mts) tożsamy z macierzą lub baza danych (po użyciu opcji dframe=TRUE). Posiadają kolumnę danych czasowych i dwie kolumnami danych z jednego lub drugiego szeregu i ewentualnie wartości NA. Oba szeregi muszą mieć tę samą częstotliwość i, a ich zakresy czasowe pokrywać się częściowo.

> aa=ts(1:10,-5,frequency=0.5)
> aa
Time Series:
Start = -5
End = 13
Frequency = 0.5
[1] 1 2 3 4 5 6 7 8 9 10
> bb=ts(11:20,1,frequency=0.5)
> bb
Time Series:
Start = 0
End = 18
Frequency = 0.5
[1] 11 12 13 14 15 16 17 18 19 20
> ts.union(aa,bb)
Time Series:
Start = -5
End = 17
Frequency = 0.5
aa bb
-5  1 NA
-3  2 NA
-1  3 NA
1  4 11
3  5 12
5  6 13
7  7 14
9  8 15
11  9 16
13 10 17
15 NA 18
17 NA 19
19 NA 20
> ts.intersect(aa,bb)
Time Series:
Start = 1
End = 13
Frequency = 0.5
aa bb
1  4 11
3  5 12
5  6 13
7  7 14
9  8 15
11  9 16
13 10 17
> bb=ts(11:20,0,frequency=0.5)
> ts.intersect(aa,bb)
Błąd w poleceniu '.cbind.ts(list(...), .makeNamesTs(...), dframe = dframe, union = FALSE)':
liczba pozycji do zastąpienia nie jest wielokrotnością długości zamiany

 

Ostatni komunikat związany był z tym, że po poprawce szereg czasowy bb dotyczył liczb parzystych, a czas w szeregu czasowym aa był liczony liczbami nieparzystymi. Niezrozumiały język tego komunikatu wynika z tego, że jest to dosłowne tłumaczenie informacji o wyniku funkcji sprawdzającej zachodzenie na siebie wektorów czasowych.

Po zrobieniu takiej macierzy (bazy danych) w zależności od potrzeb można dodać osobną kolumnę i wpisać do niej średnie, sumy lub wyniki innych działań, ale będzie to wyjaśnione w rozdziałach omawiających macierze i bazy danych.

Działania na szeregach czasowych

Na szeregach czasowych można wykonać takie same działania jak na wektorach. Dotyczą one jednak tylko przekształceń danych, a nie wektora czasu.

Przy działaniu miedzy szeregiem czasowym a liczba lub wektorem liczb obowiązują podobne zasady przy działaniu między wektorami. Gdy wektor jest krótszy od szeregu czasowego, jest wielokrotnie powielany aż osiągnie długość szeregu lub ja przewyższy. Następnie działania wykonywane są między odpowiadającymi sobie wyrazami. W przypadku gdy wektor jest dłuższy od szeregu czasowego działanie nie zostaje wykonane.

> aa=ts(1:5, c(2000,3) ,frequency=4)
> aa
    Qtr1 Qtr2 Qtr3 Qtr4
2000              1    2
2001    3    4    5
> aa*10
    Qtr1 Qtr2 Qtr3 Qtr4
2000             10   20
2001   30   40   50
> aa*c(10,20,30)
    Qtr1 Qtr2 Qtr3 Qtr4
2000             10   40
2001   90   40  100
Komunikat ostrzegawczy:
W poleceniu '`*.default`(aa, c(10, 20, 30))':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
> aa*c(10,20,30,40,50)
    Qtr1 Qtr2 Qtr3 Qtr4
2000             10   40
2001   90  160  250
> aa*c(10,20,30,40,50,60)
Błąd w poleceniu '`*.default`(aa, c(10, 20, 30, 40, 50, 60))':
niezgodność długości szeregu czasowego/wektora

 

Na liczbowych szeregach czasowych i wektorach można w podobny sposób wykonywać działania +, -, *, /, ^, %%, %/%.

Można wykonywać działania między dwoma szeregami czasowymi, o ile ich wektory czasu całkowicie się pokrywają.

> bb=ts(1:7, 2000, frequency=5)
> bb
Time Series:
Start = c(2000, 1)
End = c(2001, 2)
Frequency = 5
[1] 1 2 3 4 5 6 7
> cc=ts(8:2, 2000, frequency=5)
Time Series:
Start = c(2000, 1)
End = c(2001, 2)
Frequency = 5
[1] 8 7 6 5 4 3 2
> bb+cc
Time Series:
Start = c(2000, 1)
End = c(2001, 2)
Frequency = 5
[1] 9 9 9 9 9 9 9
> bb*cc
Time Series:
Start = c(2000, 1)
End = c(2001, 2)
Frequency = 5
[1] 8 14 18 20 20 18 14
> cc=ts(8:2, 2000, frequency=6)
> cc
Time Series:
Start = c(2000, 1)
End = c(2001, 1)
Frequency = 6
[1] 8 7 6 5 4 3 2
> bb+cc
Błąd w poleceniu '.cbind.ts(list(e1, e2), c(deparse(substitute(e1))[1L],
deparse(substitute(e2))[1L]), ':
nie wszystkie szeregi mają tę samą częstotliwość

 

Funkcje na szeregach czasowych

Podstawowe funkcje analizujące typ obiektu i typ jego elementów: mode(), typeof(), class() i str() zastosowane dla szeregów czasowych dają następujące odpowiedzi:

> cc=ts(c(7,5,4,3,6,5,7,8,9,10,7,6,5), 1999,frequency=5)
> cc
Time Series:
Start = c(1999, 1)
End = c(2001, 3)
Frequency = 5
[1]  7  5  4  3  6  5  7  8  9 10  7  6  5
> mode(cc)
[1] "numeric"
> typeof(cc)
[1] "double"
> class(cc)
[1] "ts"
> str(cc)
Time-Series [1:13] from 1999 to 2001: 7 5 4 3 6 5 7 8 9 10 ...

 

Podstawowe funkcje pokazujące cechy szeregu czasowego to:

  • length() – liczba danych
  • start() – czas (data) pierwszego pomiaru
  • end() – czas (data) ostatniego moniaru
  • frequency() – liczba pomiarów w jednostce czasu
  • deltat() – czas między kolejnymi pomiarami
> aa=ts(1:24, c(12,3), frequency=6)
> aa
Time Series:
Start = c(12, 3)
End = c(16, 2)
Frequency = 6
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
> length(aa)
[1] 24
> start(aa)
[1] 12 3
> end(aa)
[1] 16 2
> frequency(aa)
6
> deltat(aa)
0.166667

 

Funkcja as.vector pokazuje wektor danych bez przyporządkowanych mu wartości czasu. Funkcja time() pokazuje czasu, jako szereg czasowy liczb rzeczywistych z ułamkiem dziesiętnym równym wielokrotnościom liczby deltat. Aby uzyskać wektor czasu należy użyć formuły as.vector(time())

> ee=ts(letters[1:12], c(0,3), frequency=7)
> ee
Time Series:
Start = c(0, 3)
End = c(1, 7)
Frequency = 7
[1] a b c d e f g h i j k l
> as.vector(ee)
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l"
> time(ee)
Time Series:
Start = c(0, 3)
End = c(1, 7)
Frequency = 7
[1] 0.2857143 0.4285714 0.5714286 0.7142857 0.8571429 1.0000000 1.1428571 1.2857143
[9] 1.4285714 1.5714286 1.7142857 1.8571429
> as.vector(time(ee))
[1] 0.2857143 0.4285714 0.5714286 0.7142857 0.8571429 1.0000000 1.1428571 1.2857143
[9] 1.4285714 1.5714286 1.7142857 1.8571429

 

Szeregi czasowe o liczbowych wartościach można przekształcić za pomocą funkcji matematycznych. Obowiązują tu zasady takie same, jak w przypadku wektorów. Przekształcenia te nie dotyczą wektora czasu związanego z szeregiem czasowym.

> aa=ts(c(1.4, 1.4, 5.6, 5.6, 9.9, 9.9), 2000, frequency=3)
> aa
Time Series:
Start = c(2000, 1)
End = c(2001, 3)
Frequency = 3
[1] 1.4 1.4 5.6 5.6 9.9 9.9
> log(aa)
Time Series:
Start = c(2000, 1)
End = c(2001, 3)
Frequency = 3
[1] 0.3364722 0.3364722 1.7227666 1.7227666 2.2925348 2.2925348
> sin(aa)
Time Series:
Start = c(2000, 1)
End = c(2001, 3)
Frequency = 3
[1] 0.9854497 0.9854497 -0.6312666 -0.6312666 -0.4575359 -0.4575359
> gamma(aa)
Time Series:
Start = c(2000, 1)
End = c(2001, 3)
Frequency = 3
[1] 8.872638e-01 8.872638e-01 6.155392e+01 6.155392e+01 2.898677e+05 2.898677e+05

 

Typowe funkcje statystyczne działają na szeregach czasowych tak samo, jak na wektorach.

> ss=ts(c(5.2, 4.3, 2.9, 7.2, -2.1, 5.1, 4.7, -3.7, 0.8, -7.2), 2018, frequency=5)
> ss
Time Series:
Start = c(2018, 1)
End = c(2019, 5)
Frequency = 5
[1] 5.2 4.3 2.9 7.2 -2.1 5.1 4.7 -3.7 0.8 -7.2
> mean(ss)
[1] 1.72
> var(ss)
[1] 21.65289
> sd(ss)
[1] 4.653266
> min(ss)
[1] -7.2
> which.min(ss)
[1] 10

 

Szeregi czasowe, ze względu na zależność danych od siebie, podlegają odrębnej analizie statystycznej. Traktuje się je, jako realizacje jakiegoś procesu stochastycznego (czyli poszczególne elementy nie są losowane z jakiejś populacji możliwych wyników pomiarów, ale cały ciąg zostaje wylosowany z jakiegoś zbioru możliwych ciągów). Wypracowano dla nich mnóstwo odrębnych metod analiz statystycznych, głównie w takich dziedzinach nauki jak: socjologia, bankowość, nauka o gospodarce i finansach. Chodzi w nich przede wszystkim o możliwość przewidywania dalszych wartości szeregu czasowego na podstawie dotychczasowym jego przebiegu. W podstawowym kursie statystyki dla biologów nie ma niestety miejsca na omówienie tych zagadnień i studenci mający tego typu dane do opracowania, sami muszą się zmierzyć z tym problemem.

R jest programem, do którego można ściągnąć pakiety związane z analizą szeregów czasowych. Wraz z nimi R stanowi największy pakiet statystyczny analizujący takie obiekty. Omówienie możliwości R, w zakresie analizy szeregów czasowych, znajduje się na stronie https://cran.r-project.org/web/views/TimeSeries.html. Teoretyczne zagadnienia omówiono na stronie https://cran.r-project.org/web/packages/timeSeries/index.html, gdzie też znajduje się link do podręcznika statystycznego poświęconego szeregom czasowym.

Czynniki

 

Tworzenie czynników

Czynnikiem nazywamy zmienną która ma kilka, za to wielokrotnie powtarzających się wartości. Zawsze są to zmienne dyskretne, a bardzo często są to zmienne o wartościach tekstowych. Ich przechowanie w pamięci komputera polega na przechowaniu krótkiego ciągu różnych wartości, które może mieć czynnik, przypisaniu tym wartościom liczb od 1 do n oraz przechowaniu ciągu liczbowego, w którym wartości wyjściowego ciągu zostają zamienione na przypisane im liczby.

Czynniki tworzy się funkcją factor(), której argumentem są wektory.

> cc=factor(c(3:1,1:3,c(2,3,1)))
> cc
[1] 3 2 1 1 2 3 2 3 1
Levels: 1 2 3
> cc=factor(c("c", "a", "b", "a", "b", "c", "c", "b", "c"))
> cc
[1] c a b a b c c b c
Levels: a b c

 

Czynnik można utworzyć z wektorów o nazwanych wyrazach. W poziomie levels: zostają wyszczególnione tylko różne wartości tego wektora.

> c=c(0,1,4,0,0,1,4,0,1)
> names(c)=rep(c("a","b","c"),3)
> c
a b c a b c a b c
0 1 4 0 0 1 4 0 1 
> cc=factor(c)
> cc
a b c a b c a b c
0 1 4 0 0 1 4 0 1
Levels: 0 1 4

 

Zazwyczaj czynnik wyświetla nazwy kryjące się za liczbami występującymi w pierwszym wierszu. Sprawia to wrażenie, że czynnik to ten sam ciąg, z którego tworzy sią czynnik z dołączonym wykazem możliwych wartości. Pewne operacje ujawniają jednak liczby, które się kryją za tymi nazwami.

> aa=factor(c(0,5,4,0,0,5,4,0,5))
> aa
[1] 0 5 4 0 0 5 4 0 5
Levels: 0 5 4
> bb=factor(c(6,3,6,5,5,5,3))
> bb
[1] 6 3 6 5 5 5 3
Levels: 3 5 6
> factor(c(aa,bb))
[1] 1 3 2 1 1 3 2 1 3 3 1 3 2 2 2 1 Levels: 1 2 3
> # Jeżeli celem jest zrobienie czynnika z wektora c(0,5,4,0,0,5,4,0,5,6,3,6,5,5,5,3) to:
> cc=factor(c(as.vector(aa),as.vector(bb)))
> cc
[1] 0 5 4 0 0 5 4 0 5 6 3 6 5 5 5 3
Levels: 0 3 4 5 6

 

Dla czynników niekiedy znamy maksymalną liczbę różnych wartości, które tego typu zmienne mogą posiadać. Tworząc czynnik powinno się te zmienne wpisać za pomocą opcji levels, nawet gdy wprowadzony na początku czynnik nie ma (jeszcze) takich wartości.

> c=c("samiec", "samiec", "samiec", "samiec", "samiec")
> cc=factor(c, levels=c("samiec", "samica"))
> cc
[1] samiec samiec samiec samiec samiec
Levels: samiec samica

 

Inną opcją funkcji factor() jest label, która umożliwia automatyczną zmianę wartości ciągu, z którego tworzony jest czynnik na inne, zazwyczaj dłuższe, ale bardziej czytelne. Nowe nazwy należy ustawić w takim porządku, w jakim porządkują się alfabetycznie nazwy w ciągu wyjściowym, by właściwie je przyporządkować. Jeżeli nie jest to możliwe, należy użyć opcji level z odpowiednim porządkiem istniejących nazw.

> c=c("Af", "Af", "Aa", "Aa", "Af", "Aa", "Aa", "Af")
> cc=factor(c, label=c("Apodemus_flavicollis", "Apodemus_arvalis"))
> cc
[1] Apodemus_arvalis Apodemus_arvalis Apodemus_flavicollis
[4] Apodemus_flavicollis Apodemus_arvalis Apodemus flavicollis
[7] Apodemus_flavicollis Apodemus_arvalis
Levels: Apodemus_flavicollis Apodemus_arvalis
> cc=factor(c, level=c("Af", "Aa"), label=c("Apodemus_flavicollis", "Apodemus_arvalis"))
> cc
[1] Apodemus_flavicollis Apodemus_flavicollis Apodemus_arvalis
[4] Apodemus_arvalis Apodemus_flavicollis Apodemus_arvalis
[7] Apodemus_arvalis Apodemus_flavicollis
Levels: Apodemus_flavicollis Apodemus_arvalis

 

Zdarza się, że wartości zmiennych, z których tworzymy czynnik, posiadają naturalny porządek niezgodny z porządkiem alfabetycznym i chcemy by te wartości pojawiały się w naturalnym porządku na poziomie levels:. Przy tworzeniu czynnika należy użyć wtedy opcji levels i określić porządek wartości oraz opcji logicznej ordered i dać jej wartość TRUE.

> c=c("poniedziałek", "wtorek", "wtorek", "środa", "czwartek", "czwartek", "czwartek",
+ "piątek", "sobota", "niedziela", "poniedziałek", "środa")
> cc=factor(c)
> cc
[1] poniedziałek wtorek       wtorek       środa        czwartek     czwartek     czwartek
[8] piatek       sobota       niedziela    poniedziałek środa
Levels: czwartek niedziela piatek poniedziałek sobota środa wtorek
> cc=factor(c,levels=c("poniedziałek", "wtorek", "środa", "czwartek", "piątek", "sobota",
+ "niedziela"), ordered=TRUE)
[1] poniedziałek wtorek       wtorek       środa        czwartek     czwartek      czwartek
[8] piątek       sobota       niedziela    poniedziałek środa
Levels: poniedziałek < wtorek < środa < czwartek < piątek < sobota < niedziela 

 

Ciekawym sposobem na tworzenie czynników jest funkcja gl(). Ma ona składnie gl(n,k) i tworzy ciąg liczbowy, w których liczby od 1 do n powtarzają się k razy (to samo co rep(1:n,each=k)), a następnie przekształca go na czynnik. Liczby od 1 do n można zamienić na teksty za pomocą opcji labels=c(). Ciąg nazw przy opcji labels musi mieć n elementów.

> gl(3,5)
[1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
Levels: 1 2 3
> gl(3,5, labels=c("10_stopni", "15_stopni", "kontrola"))
[1] 10_stopni 10_stopni 10_stopni 10_stopni 10_stopni 15_stopni 15_stopni 15_stopni
[9] 15_stopni 15_stopni kontrola kontrola kontrola kontrola kontrola
Levels: 10_stopni 15_stopni kontrola

 

 

Czynniki zaimplementowane do R

Do R zaimplementowano kilka czynników stałych, na których można ćwiczyć różne operacje. Przykładowo state.region jest czynnikiem pokazującym przyjęte określenie rozmieszczenia geograficznego każdego z 50 stanów USA.

> state.region
 [1] South         West          West          South         West          West
[7] Northeast     South         South         South         West          West
[13] North Central North Central North Central North Central South         South
[19] Northeast     South         Northeast     North Central North Central South
[25] North Central West          North Central West          Northeast     Northeast
[31] West          Northeast     South         North Central North Central South
[37] West          Northeast     Northeast     South         North Central South
[43] South         West          Northeast     South         West          South
[49] North         Central       West
Levels: Northeast South North Central West

 

Odwołanie się do elementów czynnika

Odwołanie się do wybranych wartości czynnika jest takie same jak odwołanie się do tych wartości w wektorze. Jednak odpowiedź programu nieco inaczej wygląda.

> a=c("kawa", "kakao", "kawa", "herbata", "kawa", "kakao")
> a[1]
[1] "kawa"
> aa=factor(a)
> aa[1]
[1] "kawa"
Levels: herbata kakao kawa

 

Do wybranych wyrazów czynnika można się odwoływać zarówno przez wskazanie indeksów jak i ciąg wartości logicznych.

> b=c("Lis", "Foka", "Bóbr", "Lis", "Lis", "Bóbr")
> bb=factor(b)
> bb
[1] Lis Foka Bóbr Lis Lis Bóbr
Levels: Bóbr Foka Lis
> bb[c(1,2,4,5)]
[1] Lis Foka Lis Lis
Levels: Bóbr Foka Lis
> bb[c(TRUE,TRUE,FALSE,TRUE,TRUE,FALSE)]
[1] Lis Foka Lis Lis
Levels: Bóbr Foka Lis

 

Odwołania przez indeksy lub ciąg wartości logicznych nie zmieniaja poziomu levels:, nawet wtedy, gdy czynnik po takim odwołaniu nie posiada pwnych wartości wyróżnionych w poziomie levels:.

Wektor wartości czynników łatwo jest uzyskać stosując funkcje as.vector(). Powstały ciąg jest tożsamy z ciągiem wyjściowym użytym do utworzenia czynnika, tylko wartości liczbowe zostają zamienione na tekstowe. Odwołanie się do poziomu levels:, czyli wyświetlenie, jakie różne wartości występują w czynniku, umożliwia funkcja levels()

> bb=factor(rep(letters[1:4],each=3))
> bb
[1] a a a b b b c c c d d d
Levels: a b c d
> as.vector(bb)
[1] "a" "a" "a" "b" "b" "b" "c" "c" "c" "d" "d" "d"
> levels(bb)
[1] "a" "b" "c" "d"> bb=factor(rep(1:4,each=3))
> bb
[1] 1 1 1 2 2 2 3 3 3 4 4 4
Levels: 1 2 3 4
> as.vector(bb)
[1] "1" "1" "1" "2" "2" "2" "3" "3" "3" "4" "4" "4"
> level(bb)
[1] "1" "2" "3" "4"

 

Edycja czynników

Poprzez odwołanie czynnik[n] można zamienić wartość n-tego wyrazu czynnika pod warunkiem, że nowa nazwa będzie występować w poziomie levels: czynnika.

> a=c("kawa", "kakao", "kawa", "herbata", "kawa", "kakao")
> aa=factor(a)
> aa[6]="kawa"
> aa
[1] kawa kakao kawa herbata kawa kawa
Levels: herbata kakao kawa
> aa[6]="mleko"
Komunikat ostrzegawczy:
W poleceniu '`[<-.factor`(`*tmp*`, 6, value = "mleko")':
invalid factor level, NA generated
> aa
[1] kawa kakao kawa herbata kawa <NA>
Levels: herbata kakao kawa

 

Wprowadzenie nowej wartości w dowolne miejsce czynnika musi poprzedzić wprowadzenie tej wartości do poziomu levels:. Wektor ten wywołuje się funkcją levels() i wtedy można go zmodyfikować.

> aa=factor(c("kawa", "kakao", "kawa", "herbata", "kawa", "kakao"))
> levels(aa)
[1] "herbata" "kakao" "kawa"
> levels(aa)=c(levels(aa),"mleko")
> aa[6]="mleko"
> aa
[1] kawa kakao kawa herbata kawa mleko
Levels: herbata kakao kawa mleko

 

Zdarza się, że wszystkie elementy czynnika o danej wartości należy zamienić. Najlepiej taką zamianę zrobić w wektorze, który wywołuje się funkcją levels(czynnik).

> aa=factor(c(1987, 1995, 1987, 1987, 1995))
> aa
[1] 1987 1995 1987 1987 1995
Levels: 1987 1995
> levels(aa)[1]=1997
> aa
[1] 1997 1995 1997 1997 1995
Levels: 1997 1995

 

Czynniki można tworzyć z uporządkowanymi wartościami na poziomie levels: według zadanego klucza. Czasem dopiero po utworzeniu czynnika pojawia się potrzeba innego uporządkowania wartości na tym poziomie. Aby zmienić uporządkowanie nazw należy użyć funkcji ordered().

> aa=factor(c("wiosna","lato","wiosna","jesień","zima","lato","wiosna"))
> aa
[1] wiosna lato   wiosna jesień zima   lato   wiosna
Levels: jesien lato wiosna zima
> bb=ordered(aa,levels=c("wiosna","lato","jesień","zima"))
> bb [1] wiosna lato   wiosna jesień zima   lato   wiosna
Levels: wiosna < lato < jesień < zima

 

Czasami chcemy jakąś wartość występującą w levels wykluczyć. Służy do tego opcja exclude.

> c=c("Af", "Af", "Aa", "Aa", "Af", "Aa", "Aa", "Af")
> cc=factor(c, levels=c("Aa", "Af", "Mg"))
> cc
[1] Af Af Aa Aa Af Aa Aa Af
Levels: Aa Af Mg
> dd=factor(cc, exclude="Mg")
Komunikat ostrzegawczy:
W poleceniu 'as.vector(exclude, typeof(x))':
pojawiły się wartości NA na skutek przekształcenia
> dd
[1] Af Af Aa Aa Af Aa Aa Af
Levels: Aa Af

 

Komunikat dotyczy ewentualnego błędu, który nie pojawia się, gdy w czynniku nie ma wartości wymienionych w ciągu exclude. Gdyby w wyjściowym ciągu była wartość “Mg” pojawiłaby się w tym miejscu wartość pomijalna NA.

Wykrawanie fragmentu czynnika może być wykonane poprzez wskazanie indeksów lub wskazanie poprzez funkcje logiczne.

> bb=factor(c("Ala","Ola","Ala","Ela","Ola","Ala"))
> cc=bb[1:3]
> cc
[1] Ala Ola Ala
Levels: Ala Ela Ola
> dd=bb[c(TRUE,TRUE,FALSE,FALSE,TRUE,TRUE)]
> dd
[1] Ala Ola Ola Ala
Levels: Ala Ela Ola
> ee=bb[bb!="Ela"]
> ee
[1] Ala Ola Ala Ola Ala
Levels: Ala Ela Ola

 

Łączenie czynników można wykonać za pomocą formuły:

factor(c(as.vector(czynnik_1),as.vector(czynnik_2)))Trochę krótszy sposób wymaga utworzenia obiektu, który jest opisany w następnym rozdziale (listy) za pomocą funkcji list(), a następnie przekształcenie go funkcją unlist().

> aa=factor(c(1,1,3,3,5,5))
> aa
[1] 1 1 3 3 5 5
Levels: 1 3 5
> bb=factor(c(2,2,3,3,4,4))
> bb
[1] 2 2 3 3 4 4
Levels: 2 3 4
> cc=factor(c(as.vector(aa),as.vector(bb)))
> cc
[1] 1 1 3 3 5 5 2 2 3 3 4 4
Levels: 1 2 3 4 5
> cc=unlist(list(aa,bb))
> cc
[1] 1 1 3 3 5 5 2 2 3 3 4 4
Levels: 1 3 5 2 4

 

Czynnik utworzony za pomoca złożenia funkcji unlist(list()) jest poprawny, choć jego wartości w poziomie levels: już nie zawsze są uporządkowane alfabetycznie lub liczbowo.

Funkcje i operacje na czynnikach

Funkcje mode(czynnik) i typeof(czynnik) analizują wyłącznie ciąg liczbowy i pokazują typ jego elementów, a funkcja str(czynnik) dokładnie pokazuje strukturę czynnika.

> cc=factor(c("c", "a", "b", "a", "b", "c", "c", "b", "c"))
> mode(cc)
[1] "numeric"
> typeof(cc)
[1] "integer"
> class(cc)
[1] "factor"
> str(cc)
  Factor w/ 3 levels "a","b","c": 3 1 2 1 2 3 3 2 3

 

Do odczytywania wartości występujących w czynnikach służy funkcja levels(). Do wyliczenia liczby różnych wartości występujących w czynnikach służy funkcja nlevels(). Natomiast do wyliczenia długości czynnika (liczbę wszystkich jego wyrazów) służy funkcja length().

> cc=factor(c("Af", "Af", "Aa", "Aa", "Af", "Aa", "Aa", "Af"))
> levels(cc)
[1] "Aa" "Af"
> nlevels(cc)
[1] 3
> length(cc)
[1] 8

 

Podstawowa operacja statystyczna, stosowana przy opracowywaniu czynników, polega na wyliczeniu, ile obiektów w czynniku ma pierwszą wartość, drugą, trzecią itd. Następnie liczby te pokazuje się jako ułamek ogólnej liczebności (frakcję) lub procent ogólnej liczebności. W R najczęściej stosuje się do tego funkcję table() lub summary().

> cc=factor(c("b", "a", "c", "c", "b", "c", "a", "b"))
> table(cc)
cc
a b c
2 3 3
table(cc)/length(cc)
cc
a     b     c
0.250 0.375 0.375 
> table(cc)/length(cc)*100
cc
a    b    c
25.0 37.5 37.5
> summary(cc)
a b c
2 3 3
> summary(cc)/length(cc)*100
   a    b    c
25.0 37.5 37.5

 

Funkcje table() i summary() generują obiekty typu tablicowego, o którym mowa będzie później. Pokazują one, ile jest w czynniku wyrazów o danej wartości. Wartości te są uporządkowane taki sam sposób, w jakim są one uporządkowane w czynniku na poziomie levels:

> a=c("wiosna", "lato", "wiosna", "zima", "zima", "jesień", "wiosna")
> table(a)
a
jesień   lato wiosna   zima
1      1      3      2 
> table(factor(a))

jesień   lato wiosna   zima
1      1      3      2 
> table(factor(a, levels=c("wiosna", "lato", "jesień", "zima"), ordered=TRUE))

wiosna   lato jesień   zima
3      1      1      2

 

Czynników, nawet jeśli ich wartościami są liczby, nie przekształca się funkcjami matematycznymi. Próba takich operacji kończy się błędem. Kiedy istnieje potrzeba takich przekształceń trzeba najpierw czynnik przekształcić funkcją as.vector() na wektor. W wyniku uzyska się wektor o wartościach tekstowych. Trzeba go przekształcić funkcją as.numeric(), wykonać operację, a wynik, który chcemy traktować jako czynnik, należy przekształcić funkcją as.factor().

> cc=factor(c(94, 94, 96, 96, 99, 99))
> cc+1900
[1] NA NA NA NA NA NA
Komunikat ostrzegawczy:
W poleceniu 'Ops.factor(cc, 1900)': ‘+’ nie ma sensu dla czynników
> as.vector(cc)
[1] "94" "94" "96" "96" "99" "99"
> as.numeric(as.vector(cc))
[1] 94 94 96 96 99 99
> as.numeric(as.vector(cc))+1900
[1] 1994 1994 1996 1996 1999 1999
> as.factor(as.numeric(as.vector(cc))+1900)
[1] 1994 1994 1996 1996 1999 1999
Levels: 1994 1996 1999

 

Choć na wartościach czynników nie wykonuje się działań, może być z nimi związany ciąg liczbowy, np. wypłat z bankomatu w każdym dniu tygodnia. Działania mogą być ważne po zgrupowaniu liczb względem wartości czynnika. Takie działania wykonuje się funkcją tapply().

> cc=factor(c(rep("poniedziałek",2),rep("wtorek",3),rep("środa",5),rep("czwartek",2), rep("piątek",1)))
> cc=ordered(cc, levels=c("poniedziałek", "wtorek", "środa", "czwartek", "piątek"))
> cc
[1] poniedziałek poniedziałek wtorek       wtorek       wtorek       środa        środa
[8] środa        środa        środa        czwartek     czwartek     piątek
Levels: poniedziałek < wtorek < środa < czwartek < piątek
> dd=c(150,50,100,100,200,50,50,100,50,50,300,100,350)
> tapply(dd,cc,sum)
poniedziałek    wtorek     środa  czwartek    piątek
200       400       300       400       350

 

Funkcje w tapply() mogą być dowolnymi funkcjami zdefiniowanymi dla ciągów (np. mean(), sd(), var(), range()) lub zdefiniowanymi przez użytkownika według schematu ‘function(x) formuła’. Przykładowo może być to: tapply(dd,cc,function(x) mean((x-mean(x))^2)).

Czasami trzeba połączyć ze sobą parami wartości kilku wektorów lub czynników równej długości by uzyskać ciąg będący połączeniem nazw występujących na danym miejscu w ciągach składowych. Służy do tego funkcja interaction(). Wynikiem takiej operacji jest nie ciąg, ale czynnik.

> rodzaj=c("Apodemus", "Apodemus", "Myodes")
> rozsz.gatunkowe = c("flavicolis", "agrarius", "glareolus")
> interaction(rodzaj, rozsz.gatunkowe)
[1] Apodemus.flavicolis Apodemus.agrarius Myodes.glareolus
6 Levels: Apodemus.agrarius Myodes.agrarius Apodemus.flavicolis Myodes.flavicolis ... Myodes.glareolus 
> gat=factor(interaction(rodzaj, rozsz.gatunkowe),exclude=c("Myodes.agrarius", "Myodes.flavicolis", "Apodemus.glareolus"))
Komunikat ostrzegawczy:
W poleceniu 'as.vector(exclude, typeof(x))':
pojawiły się wartości NA na skutek przekształcenia
> gat
[1] Apodemus.flavicolis Apodemus.agrarius Myodes.glareolus
Levels: Apodemus.agrarius Apodemus.flavicolis Myodes.glareolus

 

R utworzył obiekt typu factor dla 6 gatunków (w tym trzech nie istniejących). Trzeba było posłużyć opcją exclude, albo zamienić wynikowy czynnik na wektor i znowu utworzyć z niego czynnik. Innym sposobem na wyczyszczenie poziomu levels: czynnika po zastosowaniu funkcji interaction() jest zastosowanie złożenia funkcji as.vector(interaction()) i ewentualna zamiana tego wektora na czynnik.

Jedną z bardziej interesujących możliwości R jest automatyczna zamiana zmiennej ciągłej na zmienną dyskretną poprzez przydzielenie każdej wartości zmiennej przedziału do którego ta wartość należy. Stosuje się do tego celu funkcję cut(). Powstaje w ten sposób obiekt typu factor.

> dane=c(2.7, 4.1, 3.6, 3, 4, 3.4, 2.6, 3.2, 3.6, 3.4, 4.2, 3.4, 4.8, 3.1, 3.7, 3.5, 4.1, 3.3, 2.9, 3.1, 4.2, 4.1, 3, 3.5, 4.4, 3.9, 4.8, 4, 4.5, 4, 4.5, 3.9, 3.6, 3.5, 3.5, 2.4, 4.2, 3.8, 3.3, 3.9, 3.3, 3.5, 3.8, 3.7, 4.4, 3.9, 3.8, 3, 4.5, 4.4)
> dane2=cut(dane,1.5+(1:8/2))
> dane2
[1] (2.5,3] (4,4.5] (3.5,4] (2.5,3] (3.5,4] (3,3.5] (2.5,3] (3,3.5] (3.5,4]
[10] (3,3.5] (4,4.5] (3,3.5] (4.5,5] (3,3.5] (3.5,4] (3,3.5] (4,4.5] (3,3.5]
[19] (2.5,3] (3,3.5] (4,4.5] (4,4.5] (2.5,3] (3,3.5] (4,4.5] (3.5,4] (4.5,5]
[28] (3.5,4] (4,4.5] (3.5,4] (4,4.5] (3.5,4] (3.5,4] (3,3.5] (3,3.5] (2,2.5]
[37] (4,4.5] (3.5,4] (3,3.5] (3.5,4] (3,3.5] (3,3.5] (3.5,4] (3.5,4] (4,4.5]
[46] (3.5,4] (3.5,4] (2.5,3] (4,4.5] (4,4.5]
Levels: (2,2.5] (2.5,3] (3,3.5] (3.5,4] (4,4.5] (4.5,5] (5,5.5]
> table(dane2)
dane2
(2,2.5] (2.5,3] (3,3.5] (3.5,4] (4,4.5] (4.5,5] (5,5.5]
1       6      14      15      12       2       0 

 

W przytoczonym przykładzie granice wybierano co 0.5kg. Nic nie stoi na przeszkodzie by wybierać granice końca przedziałów tak, by powstały przedziały o nierównej szerokości. Czasami to może się przydać.

W funkcji cut() pierwszym argumentem jest ciąg liczbowy, drugim granice przedziałów. Najmniejsza liczba w drugim ciągu jest początkiem pierwszego przedziału, a największa – końcem ostatniego przedziału. Jeżeli dane w pierwszym ciągu są mniejsze od najmniejszej wartości drugiego ciągu przypisany im przedział ma wartość NA. Podobnie, danym pierwszego ciągu większym od największej wartości drugiego ciągu przypisywana jest wartość NA – przedział pomijalny.

Wśród interesujących opcji funkcji cut() jest labels= umożliwiająca zamianę przedziałów na wybrane nazwy albo np. ich środki, right=TRUE powodująca zamianę danych na przedziały lewostronnie domknięte (standardowo są prawostronnie domknięte) oraz include.lowest=TRUE powodująca, że najmniejszy z przedziałów jest obustronnie domknięty.

> d=c(1,5,4,2,3,4,1,5,4,1,2,6,4,7)
> cut(d,c(5,2,4))
 [1] <NA>  (4,5] (2,4] <NA>  (2,4] (2,4] <NA>  (4,5] (2,4] <NA>  <NA>  <NA>  (2,4] <NA>
Levels: (2,4] (4,5]
> cut(d,c(5,2,4), right=FALSE)
 [1] <NA>  <NA>  [4,5) [2,4) [2,4) [4,5) <NA>  <NA>  [4,5) <NA>  [2,4) <NA>  [4,5) <NA>
Levels: [2,4) [4,5) 
> cut(d,c(5,2,4), right=FALSE, include.lowest=TRUE)
 [1] <NA>  [4,5] [4,5] [2,4) [2,4) [4,5] <NA>  [4,5] [4,5] <NA>  [2,4) <NA>  [4,5] <NA>
Levels: [2,4) [4,5]
> cut(d,c(5,2,4), right=FALSE, include.lowest=TRUE, labels=c(3,4.5))
 [1] <NA> 4.5  4.5  3    3    4.5  <NA> 4.5  4.5  <NA> 3    <NA> 4.5  <NA>
Levels: 3 4.5

 

Funkcja cut() działa także na obiektach klasy POSIXt umożliwiając podział względem jednostek czasu. Wygląda to następująco:

> x=seq(ISOdate(2022,12,09,tz=""),ISOdate(2023,03,10,tz=""),by="week")
> x
 [1] "2022-12-09 12:00:00 CET" "2022-12-16 12:00:00 CET" "2022-12-23 12:00:00 CET"
[4] "2022-12-30 12:00:00 CET" "2023-01-06 12:00:00 CET" "2023-01-13 12:00:00 CET"
[7] "2023-01-20 12:00:00 CET" "2023-01-27 12:00:00 CET" "2023-02-03 12:00:00 CET"
[10] "2023-02-10 12:00:00 CET" "2023-02-17 12:00:00 CET" "2023-02-24 12:00:00 CET"
[13] "2023-03-03 12:00:00 CET" "2023-03-10 12:00:00 CET"
> cut(x, breaks="month")
[1] 2022-12-01 2022-12-01 2022-12-01 2022-12-01 2023-01-01 2023-01-01 2023-01-01
[8] 2023-01-01 2023-02-01 2023-02-01 2023-02-01 2023-02-01 2023-03-01 2023-03-01
Levels: 2022-12-01 2023-01-01 2023-02-01 2023-03-01

 

W efekcie powstał czynnik złożony z obiektów klasy Date, w którym dzień miesiąca został zmieniony do 1. Pozwala to zobaczyć i policzyć tygodnie których piątki (dlatego, że w dniu 09/12/2022 był piątek) występowały w danym miesiącu.

Szczegółowe omówienie funkcji cut() wynika z jej przydatności w opracowaniu danych biologicznych.

Listy

 

Tworzenie list

 Lista jest czymś więcej niż wektor, czynnik lub szereg czasowy. Jest zestawem obiektów o różnych typach, a mogą być nimi wektory numeryczne różnej długości, czynniki, wektory tekstowe i inne obiekty. Tworzy się poleceniem list(). Warto zwrócić uwagę na różnicę między wektorem a listą.

> w = c(1,"2")
> w
[1] "1" "2"
> w = list(1,"2")
> w
[[1]]
[1] 1[[2]]
[1] "2"

 

W listach można zapisywać kolejne wyniki różnych operacji, wyniki dodatkowych analiz (typu błędy obliczeń) oraz komentarze. Dlatego też wiele obliczeń uzyskanych za pomocą złożonych algorytmów (np. testy statystyczne) zapisuje się w obiektach typu listy. Czasem tylko część z nich jest wyświetlana po zastosowaniu takich funkcji. Natomiast po zapisaniu takiej funkcji pod nazwą jakiegoś obiektu można przeanalizować jego strukturę i dotrzeć do elementów listy, które mogą być przydatne w zastosowaniach.

Listy mogą służyć do zapisania skomplikowanych struktur jakie tworzą dane biologiczne. Oto prosty przykład związany z powtarzającymi się co jakiś czas badaniami jednej populacji: łowieniem z niej osobników, ich oznaczeniem, ważeniem, określeniem stopnia pokrewieństwa z innymi osobnikami.

> osobnik1=list(plec="samica", wiek=12, potomstwo=c("osobnik11","osobnik14"), wzrost=c(w1=2, w3=4, w6=5, w7=5.5, w10=6, w12=6))
> osobnik11=list(plec="samiec", wiek=2, potomstwo=NULL, wzrost=c(w1=2.5, w2=3.5))
> osobnik14=list(plec="samiec", wiek=1, potomstwo=NULL, wzrost=c(w1=2))
> populacja=list(osobnik1,osobnik11,osobnik14)
> populacja
[[1]]
[[1]]$plec
[1] "samica"

[[1]]$wiek
[1] 12

[[1]]$potomstwo
[1] "osobnik11" "osobnik14"

[[1]]$wzrost
w1  w3  w6  w7 w10 w12
2.0 4.0 5.0 5.5 6.0 6.0


[[2]]
[[2]]$plec
[1] "samiec"

[[2]]$wiek
[1] 2

[[2]]$potomstwo
NULL

[[2]]$wzrost
w1 w2
2.5 3.5


[[3]]
[[3]]$plec
[1] "samiec"

[[3]]$wiek
[1] 1

[[3]]$potomstwo
NULL

[[3]]$wzrost
w1
2

 

Zapis tego typu danych w postaci listy umożliwia wyciąganie różnorakiej informacji z jednego obiektu o nazwie “populacja”.

Nazywanie elementów listy

 Elementy listy nie muszą być nazwane i można odwoływać się do nich poprzez nazwa[[1]], nazwa[[2]], … ale w praktyce lepiej jest je nazywać. Łatwiej jest wtedy operować tymi danymi.

Elementy listy mogą zostać nazwane w podobny sposób, jak nazywa się elementy wektora.

> w = list(liczba=1, znak="2")
> w
$liczba
[1] 1

$znak
[1] "2"

 

Gdy jako elementy listy wprowadza się nazwane wcześniej obiekty, ich nazwy nie stają się nazwami elementów listy. Trzeba je wprowadzić ponownie.

> Liczby=1:10
> Znaki=letters[1:10]
> w = list(Liczby, Znaki)
> w
[[1]]
[1]  1  2  3  4  5  6  7  8  9 10

[[2]]
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

> w = list(Liczby=Liczby, Znaki=Znaki)
> w
$Liczby
[1]  1  2  3  4  5  6  7  8  9 10

$Znaki
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

 

Listy można tworzyć nazywając obiekty składowe, których elementy zostały też nazwane. Wygląda to następująco:

> LL=list(wektor=c("A"=1,"B"=3,"C"=4),
+ czynnik=factor(c("A"=3,"B"=1,"A"=3,"A"=3)), szereg=ts(c("1maj"=1,"2maj"=2,"3maj"=3)))
> LL
$wektor
A B C
1 3 4

$czynnik
A B A A
3 1 3 3
Levels: 1 3

$szereg
Time Series:
Start = 1
End = 3
Frequency = 1
1maj 2maj 3maj
1    2    3

 

Aby wprowadzić nazwy elementów do istniejącej listy, lub zamienić na inne, można posłużyć się funkcją names().

> L=list(17,letters[1:7])
> L
[[1]]
[1] 17

[[2]]
[1] "a" "b" "c" "d" "e" "f" "g"


> names(L)=c("liczba","litery")
> L
$liczba
[1] 17

$litery
[1] "a" "b" "c" "d" "e" "f" "g"



> names(L)=c("LICZBA","LITERY")
> L
$LICZBA
[1] 17

$LITERY
[1] "a" "b" "c" "d" "e" "f" "g"


> names(L)
[1] "LICZBA" "LITERY"

 

Zastosowanie instrukcji names(L)=NULL spowoduje usunięcie nazw wszystkich elementów listy.

Listy zaimplementowane do R

 Przykładem listy zaimplementowanej do R jest lista współrzędnych geograficznych środków poszczególnych stanów USA. Nosi ona nazwę state.center.

> state.center
$x
[1]  -86.7509 -127.2500 -111.6250  -92.2992 -119.7730 -105.5130 -72.3573 -74.9841 -81.6850
[10]  -83.3736 -126.2500 -113.9300  -89.3776  -86.0808 -93.3714 -98.1156  -84.7674 -92.2724
[19]  -68.9801  -76.6459  -71.5800  -84.6870  -94.6043 -89.8065 -92.5137 -109.3200 -99.5898
[28] -116.8510  -71.3924  -74.2336 -105.9420  -75.1449 -78.4686 -100.0990 -82.5963 -97.1239
[37] -120.0680  -77.4500  -71.1244  -80.5056  -99.7238 -86.4560 -98.7857 -111.3300 -72.5450
[46]  -78.2005 -119.7460  -80.6665  -89.9941 -107.2560

$y
[1] 32.5901 49.2500 34.2192 34.7336 36.5341 38.6777 41.5928 38.6777 27.8744 32.3329 31.7500
[12] 43.5648 40.0495 40.0495 41.9358 38.4204 37.3915 30.6181 45.6226 39.2778 42.3645 43.1361
[23] 46.3943 32.6758 38.3347 46.8230 41.3356 39.1063 43.3934 39.9637 34.4764 43.1361 35.4195
[34] 47.2517 40.2210 35.5053 43.9078 40.9069 41.5928 33.6190 44.3365 35.6767 31.3897 39.1063
[45] 44.2508 37.5630 47.4231 38.4204 44.5937 43.0504

 

Obiekty udostępniane w datasets programu R mają swoje strony internetowe z dodatkowymi objaśnieniami, zatem nie umieszcza się ich przy ciągach i tabelach. Wyniki niektórych opracowań statystycznych oprócz tabel wymagają czasami dodatkowych danych aby je dalej przetwarzać. Tego typu opracowanie wygodnie jest udostępnić w postaci listy. Przykładowo, lista ability.cov pokazuje wyniki obliczeń kowariancji między wynikami testów na inteligencję podzielonych na 6 kategorii i jednocześnie udostępnia wielkość próby (liczby zbadanych osób).

> ability.cov
$cov
general  picture  blocks   maze reading   vocab
general  24.641    5.991  33.520  6.023  20.755  29.701
picture   5.991    6.700  18.137  1.782   4.936   7.204
blocks   33.520   18.137 149.831 19.424  31.430  50.753
maze      6.023    1.782  19.424 12.711   4.757   9.075
reading  20.755    4.936  31.430  4.757  52.604  66.762
vocab    29.701    7.204  50.753  9.075  66.762 135.292

$center
[1] 0 0 0 0 0 0

$n.obs
[1] 112

 

Pierwszy z pokazanych obiektów jest macierzą, o której będzie mowa w następnym rozdziale.

Odwołanie się do elementów listy

 Odwołanie się do elementów listy może zachodzić na dwóch poziomach.

> lista=list(1:4/5, 3:7/9, 15:20/2)
> lista
[[1]] [1] 0.2 0.4 0.6 0.8
[[2]] [1] 0.3333333 0.4444444 0.5555556 0.6666667 0.7777778

[[3]] [1] 7.5 8.0 8.5 9.0 9.5 10.0

> lista[2]
[[1]] [1] 0.3333333 0.4444444 0.5555556 0.6666667

> lista[[2]]
[1] 0.3333333 0.4444444 0.5555556 0.6666667
> lista[[2]][2]
[1] 0.4444444
> lista[c(1,3)]
[[1]] [1] 0.2 0.4 0.6 0.8

[[2]] [1] 7.5 8.0 8.5 9.0 9.5 10.0
> lista[c(1,3)][[2]]
[1] 7.5 8.0 8.5 9.0 9.5 10.0

 

Kiedy elementy listy są nazwane, odwołanie się do poszczególnych elementów (na drugim poziomie, który daje podwójny nawias) może zachodzić poprzez nazwę. Umieszcza się ją po nazwie listy i znaku $. Jest to bardziej czytelne od podwójnego nawiasu.

> lista=list(mianownik5=1:4/5, mianownik9=3:7/9, mianownik2=15:20/2)
> lista
$mianownik5
[1] 0.2 0.4 0.6 0.8

$mianownik9
[1] 0.3333333 0.4444444 0.5555556 0.6666667 0.7777778

$mianownik2
[1] 7.5 8.0 8.5 9.0 9.5 10.0

> lista$mianownik9
[1] 0.3333333 0.4444444 0.5555556 0.6666667

 

Edycja list

 Może się zdarzyć, że po utworzeniu listy trzeba w niej będzie zmienić pewne elementy. Należy dotrzeć do nich poprzez zastosowanie właściwego odwołania i w zależności od typu elementu dokonać poprawnego przypisania nowej wartości.

> L=list(liczba=7,wektor=letters[1:7], lista=list(1:3,letters[1:3]))
> L
[1] "1" "2"
$liczba
[1] 7

$wektor
[1] "a" "b" "c" "d" "e" "f" "g"

$lista
$lista[[1]]
[1] 1 2 3

$lista[[2]]
[1] "a" "b" "c"


> L[[1]]=13
> L[[2]]=letters[13:7]
> L[[3]]=list(13:10,letters[13:10])
> L
$liczba
[1] 13

$wektor
[1] "m" "l" "k" "j" "i" "h" "g"

$lista
$lista[[1]]
[1] 13 12 11 10

$lista[[2]]
[1] "m" "l" "k" "j"

 

W ten sposób można także zmieniać typ poszczególnych elementów listy.

> L=list(factor(rep(c("a","b"),5)), lista=list(1:3,letters[1:3]))
> L
[[1]]
[[1]]
[1] a b a b a b a b a b
Levels: a b

$lista
$lista[[1]]
[1] 1 2 3

$lista[[2]]
[1] "a" "b" "c"


> L[[1]]=as.vector(L[[1]])
> L[[2]]=L[[2]][2]
> L
[[1]]
[1] "a" "b" "a" "b" "a" "b" "a" "b" "a" "b"

$lista
$lista[[1]]
[1] "a" "b" "c"

 

Dodanie nowego elementu do listy najwygodniej jest przeprowadzić za pomocą funkcji append().

> L=list(cz=factor(rep(c("a","b"),3)), wek=letters[1:6])
> L
$cz
[1] a b a b a b
Levels: a b

$wek
[1] "a" "b" "c" "d" "e" "f"

> append(L,list("nowy"=1:6))
> L
$cz
[1] a b a b a b
Levels: a b

$wek
[1] "a" "b" "c" "d" "e" "f"

$nowy
[1] 1 2 3 4 5 6

> append(L,list("nowy"=1:6),1)
> L
$cz
[1] a b a b a b
Levels: a b

$nowy
[1] 1 2 3 4 5 6

$wek
[1] "a" "b" "c" "d" "e" "f"

 

Funkcja ta także łączy ze sobą różne listy.

Wykluczenie jakiegoś elementu (np. drugiego) sprowadza się do formuły Lista=Lista[-2].

Działania na listach

 Gdy wśród elementów listy jest przynajmniej jeden wektor funkcja unlist() zamienia całość na wektor. Nie zawsze w ten sposób uzyska się coś sensownego. W dodatku czynniki mogą w ten sposób stracić swoje prawdziwe wartości.

> L=list(cz=factor(rep(c("a","b"),3)), sz=ts(c(1,2,3)), wek=letters[1:6])
> L
$cz
[1] a b a b a b
Levels: a b

$sz
Time Series:
Start = 1
End = 3
Frequency = 1
[1] 1 2 3

$wek
[1] "a" "b" "c" "d" "e" "f"

> unlist(L)
 cz1  cz2  cz3  cz4  cz5  cz6  sz1  sz2  sz3 wek1 wek2 wek3 wek4 wek5 wek6
"1"  "2"  "1"  "2"  "1"  "2"  "1"  "2"  "3"  "a"  "b"  "c"  "d"  "e"  "f"

 

Gdy wszystkie elementy listy są czynnikami wynikiem funkcji unlist() jest czynnik wynikający z ich połączenia.

> L=list(cz=factor(rep(c("a","b"),3)), sz=factor(1:3))
> L
$cz
[1] a b a b a b
Levels: a b

$sz
[1] 1 2 3
Levels: 1 2 3

> unlist(L)
cz1 cz2 cz3 cz4 cz5 cz6 sz1 sz2 sz3
a   b   a   b   a   b   1   2   3
Levels: a b 1 2 3

 

Połączenie szeregów czasowych w taki sposób daje wyłącznie wektor, nawet wtedy, gdy czasy kolejnych obserwacji pierwszego i drugiego szeregu umożliwiłaby utworzenie jednego, długiego szeregu czasowego.

W niektórych sytuacjach, gdy powstaje lista wyników i chce się je przekształcić, funkcja ulist() jest najlepszym sposobem na umożliwienie takich działań.

> akord=list(pon=c(35,54,50),wt=c(65,43), środ=c(44,39,55), czw=c(35,60), piat=12)
> sum(akord))
Błąd w poleceniu 'sum(akord)':niepoprawny 'type' (list) argumentu
> sum(unlist(akord))
[1] 492

 

Generalnie na listach nie wykonuje się działań matematycznych. Nie po to je wymyślono. Jednak czasem elementami listy są obiekty tego samego typu, na których możliwe są takie działania. Do ich wykonania stosuje się funkcje lapply(nazwa_listy,nazwa_funkcji). W efekcie uzyskujemy listę z wynikami działania zastosowanej funkcji na jej kolejnych elementach. Jej działanie obrazuje następujący przykład.

> L = list(a=1:4, b=5:6, c=7:9)
> L
$a
[1] 1 2 3 4

$b
[1] 5 6

$c
[1] 7 8 9

> lapply(L,sum)
$a
[1] 10

$b
[1] 11

$c
[1] 24

 

Inny przykład:

> L = list(1:7, letters[1:7])
> L
[[1]]
[1] 1 2 3 4 5 6 7

[[2]]
[1] "a" "b" "c" "d" "e" "f" "g"

> lapply(L, as.character)
[[1]]
[1] "1" "2" "3" "4" "5" "6" "7"

[[2]]
[1] "a" "b" "c" "d" "e" "f" "g"

 

Jednym z praktycznych zagadnień spotykanych przy przetwarzaniu danych jest zamiana tekstu postaci “tekst1, tekst2, tekst3,…,tekstn” (gdzie zamiast przecinków mogą być dowolne inne znaki rozdzielające) na wektor danych tekstowych c(“tekst1”, “tekst2”, “tekst3”, …, “tekstn”). Zagadnienie to dotyczy danych zapisanych w plikach tekstowych, które przy przenoszeniu do R tworzą jedną wartość tekstową. Jest to operacja odwrotna do łączenia tekstów. Do jej wykonania stosuje się funkcję strsplit(), której wartością jest lista, aczkolwiek często jest to lista jednoelementowa.

> tekst1="Ala, Ola, Ela, Ula"
> tekst2=strsplit(tekst1,"\\,")
> tekst2
[[1]]
[1] "Ala" "Ola" "Ela" "Ula"

> tekst3=unlist(tekst2)
> tekst3
[1] "Ala" "Ola" "Ela" "Ula"

 

Zapisanie wyniku w postaci listy jest uzasadnione tym, że funkcja strsplit() może działać na wektorze danych tekstowych, z których każda powinna zostać podzielona na teksty.

> tekst1=c("Ala,Ola,Ela,Ula", "Iga,Iza,Ida")
> tekst2=strsplit(tekst1,"\\,")
> tekst2
[[1]]
[1] "Ala" "Ola" "Ela" "Ula"

[[2]]
[1] "Iga" "Iza" "Ida"

> tekst3=tekst2[[1]]
> tekst3
[1] "Ala" "Ola" "Ela" "Ula"
> tekst4=tekst2[[2]]
> tekst4
[1] "Iga" "Iza" "Ida"
> tekst5=unlist(tekst2)
> tekst5
[1] "Ala" "Ola" "Ela" "Ula" "Iga" "Iza" "Ida"

 

 

Macierze

 

Tworzenie macierzy

Macierze to dwuwymiarowe tablice złożone z danych tego samego typu. Można je utworzyć z każdego ciągu złożonego z n×k elementów za pomocą instrukcji matrix(ciag,nrow=n,ncol=k) zaznaczając w ten sposób, że macierz ma mieć n wierszy i k kolumn. Dane są dzielone wzdłuż kolumn od góry w dół, a kolumny układane od lewej do prawej.

> ciag=rep(1:4,3)
> matrix(ciag, ncol=4, nrow=3)
[,1] [,2] [,3] [,4]
[1,]    1    4    3    2
[2,]    2    1    4    3
[3,]    3    2    1    4
> matrix(ciag, nrow=4, ncol=3)
[,1] [,2] [,3]
[1,]    1    1    1
[2,]    2    2    2
[3,]    3    3    3
[4,]    4    4    4
> matrix(ciag,6,4)
[,1] [,2] [,3] [,4]
[1,]    1    3    1    3
[2,]    2    4    2    4
[3,]    3    1    3    1
[4,]    4    2    4    2
[5,]    1    3    1    3
[6,]    2    4    2    4
> matrix(ciag,5,3)
[,1] [,2] [,3]
[1,]    1    2    3
[2,]    2    3    4
[3,]    3    4    1
[4,]    4    1    2
[5,]    1    2    3
Warning message:
In matrix(ciag, 5, 3) :
data length [12] is not a sub-multiple or multiple of the number of rows [5] 

 

Kolejne elementy wektora wypełniają kolumny od góry do dołu i proces wypełniania przechodzi na następna kolumnę. Gdy liczba elementów ciągu jest mniejsza niż komórek w macierzy po dojściu do końca wektora kolejne komórki wypełnią się elementami wektora począwszy od pierwszego elementu. Wszystko jest dobrze, gdy liczba komórek w macierzy jest wielokrotnością liczby elementów w wektorze. Gdy nie jest spełniony ten warunek R generuje komunikat ostrzegawczy, ale macierz tworzy. Można ją zapamiętać pod wybraną nazwą i posługiwać się nią, jako obiektem typu macierz.

Nazwy opcji nrow, ncol mogą być pominięte. Wtedy pierwszą z liczb traktuje się domyślnie jako nrow, a druga jako ncol. Można też skrócić definicje macierzy do formuły matrix(ciąg, ncol=n) lub matrix(ciag,nrow=k).

Czasami wygodnie jest wypełniać macierz elementami wektora rzędami, nie kolumnami. Należy wtedy dopisać opcję byrow=TRUE (byrow=T lub byrow=1) w funkcji matrix().

> ciag=1:10
> matrix(ciag,2,5)
[,1] [,2] [,3] [,4] [,5]
[1,]    1    3    5    7    9
[2,]    2    4    6    8   10
> matrix(ciag,2,5,byrow=TRUE)
[,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10 

 

Macierze można tworzyć przez sklejanie wektorów o jednakowej liczbie elementów za pomocą funkcji cbind() (wtedy składowe wektory są kolumnami) lub rbind() (wtedy sklejane wektory są wierszami). Kiedy wektory nie są równej długości, obowiązuje zasada równania do dłuższego wektora poprzez cykliczne powtarzanie wyrazów z krótszego wektora.

> cbind(1:6, rep(3,6), c(2,4,5,3,8,2))
[,1] [,2] [,3]
[1,]    1    3    2
[2,]    2    3    4
[3,]    3    3    5
[4,]    4    3    3
[5,]    5    3    8
[6,]    6    3    2
> rbind(1:6, rep(3,6), c(2,4,5,3,8,2))
[,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    2    3    4    5    6
[2,]    3    3    3    3    3    3
[3,]    2    4    5    3    8    2
> rbind(1:6, 1:2)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    2    3    4    5    6
[2,]    1    2    1    2    1    2

 

Funkcja outer() pozwala na tworzenie macierzy wypełnianych liczbami zgodnie z wartościami wybranej funkcji, w domyśle jest to funkcja f(x,y)=x*y.

> outer(1:3,1:5)
[,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10
[3,]   11   12   13   14   15
> outer(rep(7,3),c(3,1,2))
[,1] [,2] [,3]
[1,]   21    7   14
[2,]   21    7   14
[3,]   21    7   14

 

Działanie w funkcji outer() można zmienić dopisując opcje “+”, “/” itd albo zdefiniować własną funkcję wg schematu:

f(x,y) wyrażenie matematyczne

> outer(1:3,1:5,"+")
[,1] [,2] [,3] [,4] [,5]
[1,]    2    3    4    5    6
[2,]    3    4    5    6    7
[3,]    4    5    6    7    8
> outer(1:3,1:5, function(x,y) log(x^2+y^2))
     [,1]     [,2]     [,3]     [,4]     [,5]
[1,] 0.6931472 1.609438 2.302585 2.833213 3.258097
[2,] 1.6094379 2.079442 2.564949 2.995732 3.367296
[3,] 2.3025851 2.564949 2.890372 3.218876 3.526361

 

 

Nazywanie wierszy i kolumn macierzy

Przydatną opcją przy tworzeniu macierzy może okazać się “dimnames”. Pozwala ona na nazwanie wierszy i kolumn macierzy odpowiednimi nazwami. Jej wartością jest lista ciągów nazw. Nazwy te nie stają się obiektami R.

> ciag=1:10
> matrix(ciag,2,5,dimnames=list(c("rząd1", "rząd2"),
+ c("kol1", "kol2", "kol3", "kol4", "kol5" )))
kol1 kol2 kol3 kol4 kol5
rząd1   1    3    5    7    9
rząd2   2    4    6    8   10

 

Mając nazwane wiersze i kolumny można odwoływać się do wartości macierzy używając składni:

> macierz=matrix(`1:10,2,5,dimnames=list(c("rząd1", "rząd2"),
+ c("kol1", "kol2", "kol3", "kol4", "kol5" )))
> macierz["rząd2","kol3"]
[1] 6
> macierz=matrix(`1:10,2,5,dimnames=list(c("rząd1", "rząd2"),
+ c("kol3", "kol2", "kol3", "kol2", "kol3" )))
> macierz["rząd2","kol3"]
[1] 2

 

Nazwy kolumn albo wierszy mogą się powtarzać, ale odwołując się poprzez nazwy do wartości macierzy uzyskujemy tylko pierwszą z możliwych wartości.

Jednocześnie obok opcji dimnames istnieje funkcja dimnames(). Jej argumentem jest macierz z nazwanymi wierszami i kolumnami, a wartością – lista nazw wierszy i kolumn macierzy.

> ciag=1:10
> macierz=matrix(ciag,2,5,dimnames=list(c("rząd1", "rząd2"),
+ c("kol1", "kol2", "kol3", "kol4", "kol5" )))
> dimnames(macierz)
[[1]]
[1] "rząd1" "rząd2"

[[2]]
[1] "kol1" "kol2" "kol3" "kol4" "kol5"

 

Mając macierz z nazwanymi wierszami i kolumnami można sprawdzić, który wiersz albo kolumna mają daną nazwę:

> ciag=1:10
> macierz=matrix(ciag,2,5,dimnames=list(c("rząd1", "rząd2"),
+ c("kol1", "kol2", "kol3", "kol4", "kol5" )))
> #badamy nazwy wierszy kolumny liczby znajdującej się w drugim wierszu i trzeciej kolumnie.
> dimnames(macierz)[[1]][2]
[1] "rząd2"
> dimnames(macierz)[[2]][3]
[1] "kol3"

 

 

Macierze zaimplementowane do R

 Prostą macierzą jest USPersonalExpenditure zawierającą wykaz wydatków (w milionach dolarów) na wyroby zaliczane do 5 różnych kategorii ponoszonych przez Amerykanów w latach 1940, 1945, 1950, 1955 i 1960.

> USPersonalExpenditure
                     1940   1945  1950 1955  1960
Food and Tobacco    22.200 44.500 59.60 73.2 86.80
Household Operation 10.500 15.500 29.00 36.5 46.20
Medical and Health   3.530  5.760  9.71 14.0 21.10
Personal Care        1.040  1.980  2.45  3.4  5.40
Private Education    0.341  0.974  1.80  2.6  3.64

 

Macierz WorldPhones pokazuje natomiast liczbę telefonów w różnych częściach świata w latach 1951-1961.

> WorldPhones
    N.Amer Europe Asia S.Amer Oceania Africa Mid.Amer
1951  45939  21574 2876   1815    1646     89      555
1956  60423  29990 4708   2568    2366   1411      733
1957  64721  32510 5230   2695    2526   1546      773
1958  68484  35218 6662   2845    2691   1663      836
1959  71799  37598 6856   3000    2868   1769      911
1960  76036  40341 8220   3145    3054   1905     1008
1961  79831  43173 9053   3338    3224   2005     1076

 

Inną macierzą jest euro.cross pokazujące przelicznik 11 walut europejskich w końcu grudnia 1998 roku. W krajach tych obowiązywały różne waluty poprzedzające wprowadzenie Euro, których oficjalne skróty są nazwami kolumn i wierszy.

> euro.cross
             ATS         BEF         DEM         ESP         FIM         FRF
ATS  1.000000000  2.93161486 0.142135709  12.0917422 0.432093050 0.476702543
BEF  0.341108927  1.00000000 0.048483759   4.1246012 0.147390797 0.162607493
DEM  7.035529673 20.62546336 1.000000000  85.0718109 3.040003477 3.353854885
ESP  0.082701069  0.24244768 0.011754775   1.0000000 0.035734557 0.039423810
FIM  2.314316324  6.78468413 0.328946992  27.9841163 1.000000000 1.103240477
FRF  2.097744212  6.14977811 0.298164361  25.3653822 0.906420695 1.000000000
IEP 17.471976881 51.22110711 2.483391826 211.2666399 7.549519785 8.328935807
ITL  0.007106602  0.02083382 0.001010102   0.0859312 0.003070713 0.003387735
LUF  0.341108927  1.00000000 0.048483759   4.1246012 0.147390797 0.162607493
NLG  6.244151907 18.30544854 0.887516960  75.5026750 2.698054644 2.976603092
PTE  0.068636087  0.20121457 0.009755639   0.8299299 0.029657176 0.032718997
IEP         ITL         LUF         NLG         PTE
ATS 0.0572345080  140.714229  2.93161486 0.160149851  14.5695951
BEF 0.0195232016   47.998880  1.00000000 0.054628544   4.9698190
DEM 0.4026750791  989.999131 20.62546336 1.126739032 102.5048189
ESP 0.0047333550   11.637217  0.24244768 0.013244564   1.2049211
FIM 0.1324587561  325.657236  6.78468413 0.370637415  33.7186519
FRF 0.1200633578  295.182459  6.14977811 0.335953424  30.5632839

 

Inną macirzą (bardzo dużą) zaimplementowaną do R jest volcano. Pokazuje ona wysokość względną wzniesienia terenu wokół wulkanu Maungawhau na Nowej Zelandii dla współrzednych wyznaczanych co 10 m. Macierz ta używana jest do testowania w R funkcji graficznych stosowanych w kartografii.

Odwoływanie się do poszczególnych elementów macierzy

 Do poszczególnych komórek macierzy odwołujemy się zgodnie ze schematem: macierz[i,j] gdzie i jest numerem wiersza a j numerem kolumny. Odwołanie macierz[,j] oznacza ciąg złożony z wyrazów j-tej kolumny, a macierz[i,] oznacza ciąg złożony z i-tego wiersza. Aby ciągi macierz[i,] i macierz[,j] traktowane były jako macierze z jednym wierszem lub jedną kolumną konieczna jest opcja drop=FALSE.

> ciag=c(2.1, 3.4, 5.6, 2.8, 3.1, 6.0, 2.9, 3.5, 5.1, 2.1, 3.8, 5.4)
> macierz=matrix(ciag,3,4)
> macierz
     [,1] [,2] [,3] [,4]
[1,]  2.1  2.8  2.9  2.1
[2,]  3.4  3.1  3.5  3.8
[3,]  5.6  6.0  5.1  5.4
> macierz[2,2]
[1] 3.1
> macierz[2,]
[1] 3.4 3.1 3.5 3.8
> macierz[2,,drop=FALSE]
     [,1] [,2] [,3] [,4]
[1,]  3.4  3.1  3.5  3.8

 

Odwoływać się można do kilku elementów na raz poprzez tworzenie ciągów w zbiorze indeksów. Ważne by te indeksy nie wyszły poza zakres macierzy.

> mat=matrix(1:12,3,4)
> mat
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> mat[1:2,c(2,4)]
     [,1] [,2]
[1,]    4   10
[2,]    5   11
> mat[,2:4]
     [,1] [,2] [,3]
[1,]    4    7   10
[2,]    5    8   11
[3,]    6    9   12
> mat[c(1,3),]
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    3    6    9   12
> mat[,4:6]
Błąd w poleceniu 'mat[, 4:6]':indeks jest poza granicami 

 

Gdy wiersze lub/i kolumny macierzy zostały nazwane można (ale nie obowiązkowo) odwoływać się poprzez te nazwy do poszczególnych elementów macierzy.

> mac=matrix(letters[1:12],4,3,dimnames=list(c("A","AA","AAA","AAAA"),c("B","BB","BBB")))
> mac
     B   BB  BBB
A    "a" "e" "i"
AA   "b" "f" "j"
AAA  "c" "g" "k"
AAAA "d" "h" "l"
> mac["AA","BBB"]
[1] "j"
> mac["AA",]
  B  BB BBB
"b" "f" "j"
> mac[,"BBB"]
  B  BB BBB
"b" "f" "j"
> mac["C",]
Błąd w poleceniu 'mac["C", ]':indeks jest poza granicami

 

Odwołanie się do samych nazw wierszy i kolumn umożliwia funkcja dimnames().

> mac=matrix(1:12,4,3,dimnames=list(letters[1:4],LETTERS[1:3]))
> mac
  A B  C
a 1 5  9
b 2 6 10
c 3 7 11
d 4 8 12
> dimnames(mac)
[[1]]
[1] "a" "b" "c"
#
[[2]]
[1] "A" "B" "C" "D"
#

 

Edycja i modyfikowanie macierzy

 Zamienienie wartości jednego z elementów macierzy odbywa się poprzez podstawienie macierz[i,j]=x. Można zamieniać całe kolumny lub wiersze na inne wektory.

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

 

Poprzez operacje na indeksach wyrazów można przestawiać wiersze i wyrazy macierzy.

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

 

Usunięcie wybranych wierszy lub kolumn można zrealizować poprzez wymienienie wierszy/kolumn, które mają pozostać albo wierszy lub kolumn, które mają być usunięte.

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

 

Dodanie kolumn do macierzy lub poziome łączenie kilku macierzy w jedną macierz można zrealizować funkcją cbind().

> mat1=matrix(1:6,2,3)
> mat1
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> mat2=matrix(c(7,8),2,3)
> mat2
     [,1] [,2] [,3]
[1,]    7    7    7
[2,]    8    8    8
> cbind(mat1,c(7,8,9))
> cbind(mat1,c(7,8,9))
     [,1] [,2] [,3] [,4]
[1,]    1    3    5    7
[2,]    2    4    6    8
Komunikat ostrzegawczy:
W poleceniu 'cbind(mat1, c(7, 8, 9, 10))':
number of rows of result is not a multiple of vector length (arg 2)
> cbind(c(7,8,9,10),mat1)
     [,1] [,2] [,3] [,4]
[1,]    7    1    3    5
[2,]    8    2    4    6
Komunikat ostrzegawczy:
W poleceniu 'cbind(mat1, c(7, 8, 9, 10))':
number of rows of result is not a multiple of vector length (arg 2)
> cbind(mat1,mat2)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    3    5    7    7    7
[2,]    2    4    6    8    8    8
> mat2=matrix(c(7,8,9),3,3)
> mat2
     [,1] [,2] [,3]
[1,]    7    7    7
[2,]    8    8    8
[3,]    9    9    9
> cbind(mat1,mat2)
Błąd w poleceniu 'cbind(mat1, mat2)': liczby wierszy macierzy muszą być zgodne (zobacz argument 2)

 

Przy dołączaniu kolumny w postaci wektora liczba wierszy ustala się poprzez liczbę wierszy w macierzy i liczba elementów wektora jest obcięta albo zaczyna się sukcesywnie powtarzać. Natomiast łączenie poziome macierzy o różnej liczbie wierszy nie jest możliwe.

Dodanie wiersza do macierzy albo pionowe połączenie dwóch macierzy można zrealizować funkcją rbind().

> mat1=matrix(1:6,2,3)
> mat1
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> mat2=matrix(c(7,8),2,3)
> mat2
     [,1] [,2] [,3]
[1,]    7    7    7
[2,]    8    8    8
> rbind(mat1,c(7,8))
> mat2
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
[3,]    7    8    7
Komunikat ostrzegawczy:
W poleceniu 'rbind(mat1, c(7, 8))':
number of rows of result is not a multiple of vector length (arg 2)
> rbind(mat1,mat2)
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
[1,]    7    7    7
[2,]    8    8    8

 

Dołączanie wiersza do macierzy za pomocą funkcji rbind() o innej liczbie elementów niż liczba kolumn w macierzy powoduje dopasowanie liczby jego elementów do liczby kolumn macierzy: ich wielokrotne powtarzanie lub obcięcie. Połączenie dwóch macierzy o nierównej liczbie kolumn nie jest możliwe.

Modyfikowanie nazw wierszy i kolumn można zrealizować za pomocą funkcji dimnames(). Należy nowe wartości wprowadzić w postaci listy, albo modyfikować poszczególne elementy listy dimnames(macierz). Ponadto polecenie dimnames(macierz)=NULL usuwa wszystkie nazwy kolumn i wierszy.

> mat=matrix(1:9,3,3,dimnames=list(1:3,1:3))
> mat
  1 2 3
1 1 4 7
2 2 5 8
3 3 6 9
> dimnames(mat)[[1]]=c("L1","L2","L3")
> mat
   1 2 3
L1 1 4 7
L2 2 5 8
L3 3 6 9
> dimnames(mat)[[2]]=c("C1","C2","C3")
> mat
   C1 C2 C3
L1  1  4  7
L2  2  5  8
L3  3  6  9
> dimnames(mat)=NULL
> mat
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> dimnames(mat)=list(c("W1","W2","W3"),c("K1","K2","K3"))
> mat
   K1 K2 K3
W1  1  4  7
W2  2  5  8
W3  3  6  9

 

Funkcje i działania na macierzach

Funkcje mode() i typeof() pokazują typ elementów macierzy, funkcja class() pokazuje typ obiektu (dla macierzy daje odpowiedź “matrix”), a funkcja str() pokazuje strukturę macierzy.

> mac1=matrix(1:12,3,4)
> mac1
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> mode(mac1)
[1] "numeric"
> typeof(mac1)
[1] "integer"
> class(mac1)
[1] "matrix"
> str(mac1)
int [1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ...
> mac2=matrix(letters[1:12],3,4,dimnames=list(1:3,1:4))
> mac2
 1   2   3   4
1 "a" "d" "g" "j"
2 "b" "e" "h" "k"
3 "c" "f" "i" "l"
> mode(mac2)
[1] "character"
> typeof(mac1)
[1] "character"
> class(mac2)
[1] "matrix"
> str(mac1)
chr [1:3, 1:4] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l"
- attr(*, "dimnames")=List of 2
..$ : chr [1:3] "1" "2" "3"
..$ : chr [1:4] "1" "2" "3" "4"

 

Podstawowe funkcje związane z macierzami pokazują ich budowę i strukturę. Są to:

  • dim(macierz) – wymiary macierzy – funkcja dwukrotna (liczba wierszy, liczba kolumn).
  • ncol(macierz) – liczba kolumn macierzy.
  • nrow(macierz) – liczba wierszy w macierzy.
  • length(macierz) – liczba elementów w macierzy.
  • dimnames(macierz) – nazwy wierszy i kolumn macierzy w postaci listy)
> mat=matrix(1:10,ncol=2,dimnames=list(letters[1:5],c("K1","K2")))
> dim(mat)
[1] 5 2
> ncol(mat)
[1] 2
> nrow(mat)
[1] 5
> length(mat)
[1] 10
> dimnames(mat)
[[1]]
[1] "a" "b" "c" "d" "e"

[[2]]
[1] "K1" "K2"

> mode(mat)
[1] "numeric"
> str(mat)
int [1:5, 1:2] 1 2 3 4 5 ...
- attr(*, "dimnames")=List of 2
..$ : chr [1:5] "a" "b" "c" "d" ...
..$ : chr [1:2] "K1" "K2"

 

Dla macierzy możliwe jest wyświetlanie głównej przekatnej (od górnego lewego rogu w dół po skosie) dzięki funkcji diag(),. Najczęściej operacja ta stosowana jest dla macierzy kwadratowych.

> mac1=matrix(1:12,3,4)
> mac1
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> diag(mac1)
[1] 1 5 9
> mac2=matrix(1:12,4,3)
> mac2
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> diag(mac2)
[1] 1 6 11
> mac3=matrix(1:16,4,4)
> mac3
     [,1] [,2] [,3] [,4]
[1,]    1    5    9   13
[2,]    2    6   10   14
[3,]    3    7   11   15
[4,]    4    8   12   16
> diag(mac3)
[1]  1  6 11 16

 

W przypadku macierzy o wielu wierszach ich wyświetlenie w całości w konsoli R nie jest możliwe i niepotrzebne. Czasem jednak potrzebne jest zobaczenie kilku wierszy początkowych, co umożliwia funkcja head(macierz) lub końcowych realizowane za pomocą funkcji tail(macierz).

> mat=matrix(1:100,ncol=2,dimnames=list(c(letters,letters)[1:50],c("K1","K2")))
> head(mat)
  K1 K2
a  1 51
b  2 52
c  3 53
d  4 54
e  5 55
f  6 56
> tail(mat)
  K1  K2
s 45  95
t 46  96
u 47  97
v 48  98
w 49  99
x 50 100

 

Aby operować elementami macierzy jako wektorem można użyć funkcji as.vector(). Funkcja as.matrix() nie jest do niej odwrotna. Zamienia ona wektor na macierz jednokolumnową.

> macierz=matrix(letters[1:6],3,2,dimnames=list(1:3,1:2))
> macierz
   1   2
1 "a" "d"
2 "b" "e"
3 "c" "f"
> as.vector(macierz)
  [1] "a" "b" "c" "d" "e" "f"
> as.matrix(as.vector(macierz))
     [,1]
[1,] "a"
[2,] "b"
[3,] "c"
[4,] "d"
[5,] "e"
[6,] "f" 
> macierz=matrix(letters[1:6],3,2,dimnames=list(1:3,1:2),byrow=TRUE)
> macierz
   1   2
1 "a" "b"
2 "c" "d"
3 "e" "f"
> as.vector(macierz)
  [1] "a" "c" "e" "b" "d" "f"

 

Funkcja as.vector() zastosowana do wielokolumnowych macierzy zawsze tworzy wektor łącząc kolumny ze sobą od pierwszej do ostatniej.

W przypadku macierzy o wartościach liczbowych wiele działań jest dla nich tak samo określonych jak na wektorach. Przykładowo zapis macierz+x, macierz*x, macierz^x oznacza, że dla każdego elementu macierzy dodano x, każdy element pomnożono przez x lub każdy element podniesiono do potęgi x. Analogicznie do działań między wektorami przebiegają działania macierz+wektor, macierz*wektor, macierz^wektor itd.

> mat=matrix(1:6,2,3)
> mat
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> mat+1
     [,1] [,2] [,3]
[1,]    2    4    6
[2,]    3    5    7
> mat+c(1,2)
     [,1] [,2] [,3]
[1,]    2    4    6
[2,]    4    6    8
> mat+c(1,2,3)
     [,1] [,2] [,3]
[1,]    2    6    7
[2,]    4    5    9
> mat+c(1,2,3,4)
     [,1] [,2] [,3]
[1,]    2    6    6
[2,]    4    8    8
Komunikat ostrzegawczy:
W poleceniu 'mat + c(1, 2, 3, 4)':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu

 

W przypadku działań między macierzą a wektorem wykonywane są działania na poszczególnych elementach macierzy i wektora przy czym macierz zostaje zamieniona najpierw na ciąg (standardowo przez sklejenie kolumn jedna za drugą). Jeżeli wektor jest dłuższy od tego ciągu zostanie użyta do działań tylko jego część i pojawi się komunikat ostrzegawczy. Jeżeli wektor jest krótszy zostanie wielokrotnie powtórzony, tak by jego długość była równa liczbie elementów w macierzy. Jeżeli nie jest to możliwe (wielokrotność wektora jest dłuższa niż liczba elementów macierzy), działanie zostanie wykonane, ale pojawi się komunikat ostrzegawczy.

W podobny sposób wykonywane są działania między dwoma macierzami, ale muszą one mieć te same wymiary.

> mat1=matrix(1:6,2,3)
> mat1
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> mat2=matrix(6:1,2,3)
> mat2
     [,1] [,2] [,3]
[1,]    6    4    2
[2,]    5    3    1
> mat1^mat2
     [,1] [,2] [,3]
[1,]    1   81   25
[2,]   32   64    6
> mat3=matrix(6:1,3,2)
> mat3
     [,1] [,2]
[1,]    6    3
[2,]    5    2
[2,]    4    1
> mat1^mat3
Błąd w poleceniu 'mat1^mat3':niezgodne tablice

 

Funkcje matematyczne działają na macierzach tak samo, jak na wektorach. Wykonywane są na każdym elemencie macierzy osobno.

> mat=matrix(1:6,2,3)
> mat
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> log(mat)
          [,1]     [,2]     [,3]
[1,] 0.0000000 1.098612 1.609438
[2,] 0.6931472 1.386294 1.791759
> sin(mat)
          [,1]       [,2]       [,3]
[1,] 0.8414710  0.1411200 -0.9589243
[2,] 0.9092974 -0.7568025 -0.2794155

 

Większość funkcji statystycznych (min(), max(),sum(), mean(), sd() i inne) działa na macierzach tak, jak na ciągach utworzonych z wyrazów macierzy. Wyjątkiem jest funkcja var(), która tworzy macierz kowariancji dla wektorów utworzonych z poszczególnych kolumn macierzy. Poszczególne elementy tej macierzy wyliczane są ze wzoru:

gdzie  jest L-tym elementem w i-tej kolumnie, jest średnią z elementów i-tej kolumny. Analogicznie dla kolumny j-tej. Na przekątnej tej tabeli lokują się nieobciążone wariancje wyliczone dla kolejnych kolumn macierzy, natomiast pozostałe elementy to kowariancje.

> mat=matrix(1:6,2,3)
> mat
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> max(mat)
[1] 6
> mean(mat)
[1] 3.5
> sd(mat)
[1] 1.870829
> var(mat)
     [,1] [,2] [,3]
[1,]  0.5  0.5  0.5
[2,]  0.5  0.5  0.5
[3,]  0.5  0.5  0.5
> mat1=matrix(1:6,3,2)
> mat1
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[2,]    3    6
> var(mat1)
     [,1] [,2]
[1,]    1    1
[2,]    1    1

 

Dość częstą czynnością jest wyliczanie różnych podsumowań związanych z macierzami. Są to różne obliczenia wykonywane osobno na każdym wierszu lub kolumnie. Do wykonywania podobnych obliczeń służy funkcja apply(). Jej składnia wygląda następująco:

apply(nazwa macierzy, liczba 1 lub 2, funkcja)
Wpisanie 1 w drugiej pozycji powoduje, że funkcja wykonywana będzie na wierszach. Wpisanie 2 w tym miejscu oznacza wykonywanie funkcji w kolumnach.

> mat=matrix(1:9,3,3)
> mat
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> apply(mat, 1, sum)
[1] 12 15 18
> apply(mat, 2, sum)
[1] 6 15 24
> apply(mat, 2, function(x) x-mean(x)) #Od wartości odejmij średnią z kolumny
     [,1] [,2] [,3]
[1,]   -1   -1   -1
[2,]    0    0    0
[3,]    1    1    1
> apply(mat, 1, function(x) x-mean(x)) #Od wartości odejmij średnią z wiersza
     [,1] [,2] [,3]
[1,]   -3   -3   -3
[2,]    0    0    0
[3,]    3    3    3
> # Działanie wykonane dobrze, tylko wyniki zapisane kolumnami, nie wierszami.

 

Poniższy przykład pokazuje działanie funkcji sort(), która porządkuje osobno wiersze albo kolumny.

> mat=cbind(c(1,3,2,0), c(5,7,6,8), c(4,3,6,5))
> mat
     [,1] [,2] [,3]
[1,]    1    5    4
[2,]    3    7    3
[3,]    2    6    6
[4,]    0    8    5
> apply(mat, 2, sort)
     [,1] [,2] [,3]
[1,]    0    5    3
[2,]    1    6    4
[3,]    2    7    5
[4,]    3    8    6
> apply(mat, 1, sort)
     [,1] [,2] [,3] [,4]
[1,]    1    3    2    0
[2,]    4    3    6    5
[3,]    5    7    6    8

 

Najczęściej jednak istnieją powody by zsumować wyrazy w wierszach macierzy lub/i w jej komumnach. Można to robić za pomocą funkcji apply() z funkcją wewnetrznąsum(), ale to samo zrobią funkcje rowSums(), colSums() oraz addmargins().

> mat=cbind(c(1,3,2,0), c(5,7,6,8), c(4,3,6,5))
> mat
     [,1] [,2] [,3]
[1,]    1    5    4
[2,]    3    7    3
[3,]    2    6    6
[4,]    0    8    5
> rowSums(mat))
[1] 10 13 14 13
> colSums(mat)
[1] 6 26 18
> addmargins(mat)
            Sum
1  5  4  10
3  7  3  13
2  6  6  14
0  8  5  13
Sum 6 26 18  50

 

Na koniec pokażemy, jak znajdywać wartości ekstremalne w macierzy i określić, na przecięciu którego wiersza i kolumny one znajdują się.

> ciag=c(4.1, 3.4, 2.6, 2.8, 5.1, 4.0, 5.9, 2.5, 5.1, 3.1, 1.8, 2.4, 5.9, 3.2, 4.3, 2.7)
> macierz=matrix(ciag,4,4)
> macierz
    [,1] [,2] [,3] [,4]
[1,]  4.1  5.1  5.1  5.9
[2,]  3.4  4.0  3.1  3.2
[3,]  2.6  5.9  1.8  4.3
[4,]  2.8  2.5  2.4  2.7
> max(macierz)
[1] 5.9
> which.max(macierz)
[1] 7
> wiersz = which.max(macierz) - 1) %% nrow(macierz) + 1
> kolumna = (which.max(macierz) - 1) %/% nrow(macierz) + 1
> wiersz
[1] 3
> kolumna
[1] 2
> min(macierz)
[1] 1.8
> which.min(macierz)
[1] 11
> wiersz = (which.min(macierz) - 1) %% nrow(macierz) + 1
> kolumna = (which.min(macierz) - 1) %/% nrow(macierz) + 1
> wiersz
[1] 3
> kolumna
[1] 3

 

Funkcje which.max(), which.min() pokazują miejsce pierwszego ekstremum w ciągu liczb, w którym ustawione są kolumny macierzy jedna za drugą. Zamiana tego wyniku na numer kolumny i wiersza wymaga wykonania odpowiednich działań arytmetycznych. Działanie a %% b wylicza resztę z dzielenia a przez b. By zamiast 0 otrzymać liczbę równą liczbie wierszy macierzy, zastosowano wyliczanie reszty z dzielenia od liczby o 1 mniejszej i dodawanie 1 do wyniku. Działanie %/% pokazuje ile razy b mieści się w a. Aby uzyskać właściwy wynik, również zastosowano odejmowanie 1 od dzielnej i dodawanie 1 do wyniku.

Wyznaczenie liczby wartości ekstremalnych można uzyskać za pomocą następujących działań:

> ciag=c(4.1, 3.4, 2.6, 2.8, 5.1, 4.0, 5.9, 2.5, 5.1, 3.1, 1.8, 2.4, 5.9, 3.2, 4.3, 2.7)
> macierz=matrix(ciag,4,4)
> macierz
    [,1] [,2] [,3] [,4]
[1,]  4.1  5.1  5.1  5.9
[2,]  3.4  4.0  3.1  3.2
[3,]  2.6  5.9  1.8  4.3
[4,]  2.8  2.5  2.4  2.7
> macierz[macierz==max(macierz)]
[1] 5.9 5.9
> length(macierz[macierz==max(macierz)])
[1] 2
> macierz[macierz==min(macierz)]
[1] 1.8
> length(macierz[macierz==min(macierz)])
[1] 1

 

Gdy ekstremów funkcji jest więcej niż jeden, aby poznać ich położenie należy użyć funkcji which(ciąg logiczny) zamiast funkcji which.min() albo which.max().

> ciag=c(4.1, 3.4, 2.6, 2.8, 5.1, 4.0, 5.9, 2.5, 5.1, 3.1, 1.8, 2.4, 5.9, 3.2, 4.3, 2.7)
> macierz=matrix(ciag,4,4)
> macierz
    [,1] [,2] [,3] [,4]
[1,]  4.1  5.1  5.1  5.9
[2,]  3.4  4.0  3.1  3.2
[3,]  2.6  5.9  1.8  4.3
[4,]  2.8  2.5  2.4  2.7
> macierz==max(macierz)
[1]      [,1]  [,2]  [,3]  [,4]
[1,] FALSE FALSE FALSE  TRUE
[2,] FALSE FALSE FALSE FALSE
[3,] FALSE  TRUE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE
> which(macierz==max(macierz))
[1] 7 13
> wiersze=(which(macierz==max(macierz)) - 1) %% nrow(macierz) + 1
> wiersze
[1] 3 1
> kolumny=(which(macierz==max(macierz))-1) %/% nrow(macierz) + 1
> kolumny
[1] 2 4

 

Rachunek macierzowy

Rachunek macierzowy tylko pozornie nie jest potrzebny biologom. Na 100 prac biologicznych jedna stosuje macierze do opisu sposobu opracowania danych, do zapisania formuł, jakimi autor posługuje się wyliczając np. strukturę wiekową banku nasion gromadzących się w glebie leśnej, kondycję komórek nowotworowych, których zewnętrzne warstwy niszczone są lekiem itp. Są istotne w modelowaniu ekologicznych, teorii sztucznych inteligencji, metodach numerycznych oraz w statystyce, choć tu gotowe procedury skrzętnie ukrywają stosowanie rachunku macierzowego. Pewne jego elementy powinien jednak biolog znać, zwłaszcza, że żmudne obliczenia wykonywać będzie R, a nie on. Dodatkowym bodźcem do uzyskania umiejętności posługiwania się rachunkiem macierzowym, jest coraz powszechniejsze pojawianie się w pracach biologicznych specjalnych wykresów zwanych biplotami, w których pokazuje się rozrzut danych wielowymiarowych w postaci punktów i poszczególnych zmiennych w postaci wektorów ze strzałkami. Ich zrozumienie i wykonanie wymaga poznania podanych niżej definicji.

Rachunek macierzowy dotyczy tylko macierzy, w której komórkach są liczby. Jeżeli macierz A ma L wierszy i K kolumn, macierz B ma K wierszy i N kolumn to możemy te macierze pomnożyć. Macierz C=A·B będzie mieć L wierszy i N kolumn. Elementem tej macierzy w i-tym wierszu i j-tej kolumnie jest liczba Ci,j równa:

gdzie Ai,k jest elementem macierzy A z i-tego wiersza i k-tej kolumny a Bk,j jest elementem macierzy B i k-tego wiersza i j-tej kolumny. Taki iloczyn nie musi być przemienny. W R iloczyn macierzy wykonuje się za pomocą funkcji crossprod() albo za pomocą operatora “%*%”.

> A=matrix(1:10,2,5)
> A
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    3    5    7    9
[2,]    2    4    6    8   10
> B=matrix(1:15,5,3)
> B
     [,1] [,2] [,3]
[1,]    1    6   11
[2,]    2    7   12
[3,]    3    8   13
[4,]    4    9   14
[5,]    5   10   15
> A%*%B
     [,1] [,2] [,3]
[1,]   95  220  345
[2,]  110  260  410
> B%*%A
Error in B %*% A : non-conformable arguments 

 

Prostym działaniem wykonywanym na macierzy jest jej transpozycja. Polega ona na zamianie macierzy [Ai,j] na macierz [Aj,i], czyli takim zapisaniem elementów macierzy aby jej kolumny stały się wierszami a wiersze kolumnami. W R taką operacje można wykonać za pomocą funkcji t(). Operacja ta działa także na wektorach, przy czym okazuje się, że pojedynczy wektor po transpozycji jest traktowany tak, jak macierz o n wierszach i 1 kolumnie.

> A=matrix(1:15,5,3)
> A
     [,1] [,2] [,3]
[1,]    1    6   11
[2,]    2    7   12
[3,]    3    8   13
[4,]    4    9   14
[5,]    5   10   15
> t(A)
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10
[3,]   11   12   13   14   15
> B=c(1,5,2,4,3)
> t(B)
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    5    2    4    3

 

Macierze o takiej samej liczbie wierszy i kolumn można mnożyć używając operatora “*”, co opisano w poprzednim podrozdziale. Wynikiem takiego mnożenia nie jest jednak iloczyn macierzy, ale macierz złożona z iloczynów odpowiadających sobie wyrazów.

Macierze kwadratowe w biologii pojawiają się wszędzie tam, gdy sytuacja wymaga wyliczania n pierwiastków układu n równań liniowych. Taki układ równań liniowych zapisuje się często w postaci Ax=B, gdzie A jest macierzą liczbową złożoną z n wierszy i n kolumn, x jest wektorem niewiadomych (x1,…,xn), B jest wektorem liczbowych (B1,…,Bn). Liczby w macierzy A i wektorze B są dane. Pierwiastki te istnieją i są jednoznacznie wyznaczone, gdy wyznacznik macierzy A jest różny od 0. W szkole wyliczany był wyznacznik macierzy o 2 wierszach i 2 kolumnach. Istnieje uogólnienie tego pojęcia dla macierzy kwadratowych o n wierszach i n kolumnach. W R wyliczamy go za pomocą funkcji det().

> A
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> det(A)
[1] 0
> A[1,1]=-1
> A[3,3]=-3
> A
     [,1] [,2] [,3]
[1,]   -1    4    7
[2,]    2    5    8
[3,]    3    6   -3
> det(A)
[1] 162

 

Dla macierzy, która ma wyznacznik różny od 0 istnieje macierz odwrotna. Macierz odwrotna do macierzy A (oznaczana A-1) spełnia warunek: A*A-1=I, gdzie I jest macierzą, która wszędzie ma same zera, a na przekątnej jedynki. Do wyznaczania macierzy odwrotnej służy funkcja solve().

> A
     [,1] [,2] [,3]
[1,]   -1    4    7
[2,]    2    5    8
[3,]    3    6   -3
> odwA=solve(A)
> odwA
           [,1]        [,2]        [,3]
[1,]-0.38888889  0.33333333 -0.01851852
[2,] 0.18518519 -0.11111111  0.13580247
[3,]-0.01851852  0.11111111 -0.08024691
> A%*%odwA
             [,1]          [,2]          [,3]
[1,] 1.000000e+00  5.551115e-17 -2.775558e-17
[2,] 5.551115e-17  1.000000e+00  0.000000e+00
[3,] 3.816392e-17  5.551115e-17  1.000000e+00
> C=matrix(1:9,3,3)
> det(C)
[1] 0
> solve(C)
Error in solve.default(C) :
Lapack routine dgesv: system is exactly singular: U[3,3] = 0 

 

Rozwiązanie układu równań postaci:
-1x1 + 4x2 + 7x3 = -7
2x1 + 5x2 + 8x3 = 6
3x1 + 6x2 – 3x3 = -9
wymaga następujących operacji:

> A=matrixei(1:9, 3, 3); A[1,1]=-1; A[3,3]=-3
> B=c(-7, 6, -9)
> solve(A)%*%B
          [,1]
[1,]  4.888889
[2,] -3.185185
[3,]  1.518519
> # albo:
> solve(A,B)
[1] 4.888889 -3.185185 1.518519

 

Pierwiastki pokazanego wcześniej układu równań są równe: x1 = 4.888889, x2 = -3.185185, x3 = 1.518519.

Wykonując pomiary liczbowe k zmiennych dla wielu obiektów operujemy macierzą złożona z k kolumn i dane można traktować jako elementy przestrzeni k-wymiarowej. Tworzą one macierz o k-kolumnach i przeważnie (ale nie zawsze) większej liczbie wierszy (np. n). Większość operacji związanych z rachunkiem macierzowym dotyczy właśnie operacji matematycznych na takich macierzach. Dotyczy to także zależności zarówno statystycznej jak i liniowej poszczególnych zmiennych od siebie, co sprowadza się do analizy zależności poszczególnych kolumn macierzy danych od siebie. Na początek omówiona zostanie liniowa zależność.

Wektor liczb x jest liniowo niezależny od wektorów y1, y2…,yk jeżeli nie istnieją takie liczby (a1,…,ak), że x=a1y1+a2y2+…+akyk. Za symbolami x, y1, y2…,yk kryją się pewne ciągi n-elementowe, zatem mówimy o ciągu n liczb liniowo niezależnym od wierszy macierzy y=[yi,j]i=1..k, j=1..n. Liniową zależność można przedstawić jako istnienie jednokolumnowej macierzy α, że x=y*α. Natomiast liniowa niezależność oznacza, że dla dowolnych macierzy jednokolumnowych α zachodzi x≠y*α. Zysk z zapisu macierzy i wektorów w postaci jednoliterowych symboli jest duży. Strata polega na tym, że wymiary macierzy i liczba elementów w wektorze nie są widoczne – należy je domyślnie kontrolować.

Liniowa niezależność jest związana ściśle z wymiarem przestrzeni, w której można umieścić dane. k liniowo niezależnych wektorów złożonych z n liczb może istnieć tylko wtedy gdy k≤n. Dla k liniowo niezależnych wektorów n-wymiarowych tworzących wiersze w macierzy A, ogół wektorów postaci A*α tworzy przestrzeń kartezjańską k-wymiarową. Jeżeli n>k to kolumny tej macierzy muszą być od siebie zależne, a dokładnie to istnieje k-kolumn liniowo niezależnych, a pozostałe n-k można wyliczyć jako ich liniową kombinację. Z każdą macierzą związany jest wymiar przestrzeni liniowej, w jakiej można umieścić n-elementowe kolumny tej macierzy. Nazywany jest on rzędem macierzy (ang. rank of matrix).

Wyliczanie rzędu dużych macierzy jest dość kłopotliwe. Po prostu nie zawsze widoczna jest liniowa zależność pewnych ciągów od pozostałych. Komputer stosuje do tego celu kilka algorytmów, ale jego obliczenia obarczone są błędem wynikającym z wykonywania obliczeń z pewną dokładnością. Tymczasem definicja rzędu nie uwzględnia zjawiska prawie liniowej zależności, to znaczy że x≈A*α z dokładnością do 0.00000000000001. Jeżeli przyjmiemy, że taka prawie liniowa zależność jest liniową zależnością bo uniemożliwia wykonywanie pewnych operacji macierzowych za pomocą komputera – to możemy programem R posługiwać się przy wyliczaniu rzędu macierzy.

Rząd macierzy wyliczamy za pomocą funkcji rankMatrix(), która znajduje się w bibliotece Matrix, na ogół ładowanej przy instalowaniu R. Należy ją jednak wywołać w konsoli by korzystać z funkcji w niej zawartych.

> A=matrix(1:9,3,3)
> A
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> library(Matrix)
> rankMatrix(A)
[1] 2
attr(,"method")
[1] "tolNorm2"
attr(,"useGrad")
[1] FALSE
attr(,"tol")
[1] 6.661338e-16
> rzadA=rankMatrix(A)[1]
> rzadA
[1] 2

 

Można zauważyć, że w macierzy A ostatni rząd (3,6,9) jest równy 2⋅(2,5,8)-1⋅(1,4,7), czyli kombinacji liniowej rzędów drugiego i pierwszego. Natomiast wektory (2,5,8) i (1,4,7) są liniowo niezależne. Wszystkie wektory postaci α⋅(2,5,8)+β⋅(1,4,7) tworzą płaszczyznę w przestrzeni trójwymiarowej, czyli przestrzeń dwuwymiarową. Stąd rząd macierzy A jest rzeczywiście równy 2.

Wynikiem funkcji rankMatrix() jest złożony obiekt – ciąg jednoelementowy z wyjaśnieniami. Ponieważ często do obliczeń potrzebny jest tylko rząd macierzy należy go wywołać jako rankMatrix( )[1].

Statystyczna zależność k zmiennych od siebie najczęściej liczona jest względem regresji liniowej y=a1x1+a2x2+…+akxk+b, co jest pewną hiperpłaszczyzną w przestrzeni k-wymiarowej. W przypadku liniowej zależności zmiennych x1, x2, …, xk od siebie ta hiperpłaszczyzna ma mniejszy wymiar i nie jest jednoznacznie określona. Dość często generuje to błąd w programach statystycznych. Stąd istnieje w biologii potrzeba wyliczania rzędu macierzy, jaką tworzą dane

Wektory i wartości własne (ang.: eingenvalues and eigenvectors) są integralną częścią pewnego sposobu opracowania danych zwanym analizą składowych głównych (principal component analysis PCA). Pojęcia te odgrywają też dużą rolę w fizyce (analizie drgań, fizyce kwantowej itp.) i być może są lub będą liczone w pewnych pracach biofizycznych. Postaram się zatem wyjaśnić, co one oznaczają.

Mówimy, że macierz A o n-wierszach i n kolumnach ma wartość własną λ (liczbę) i wektor własny x o n wyrazach, jeżeli Ax=λx. Po lewej stronie mnożymy macierze n×n i n×1 (tak traktowany przez R jest wektor), czego wynikiem jest jakiś wektor n-elementowy. Po prawej stronie mnożymy przez liczbę λ wektor x=(x1,x2,…,xn) co jest równe wektorowi (λx1,λx2,…,λxn). Do wyznaczania wektorów i wartości własnych macierzy służy w R funkcja eigen().

> A=matrix(1:9,3,3); A[1,1]=-1; A[3,3]=-3
> eigen(A)
$values
[1] 10.942898 -8.119652 -1.823246
#
$vectors
[,1]       [,2]       [,3] [1,] -0.5033257 -0.5305492 -0.8965518
[2,] -0.7491261 -0.3808798  0.4216953
[3,] -0.4306661  0.7572636 -0.1355284
#
> lambda1=eigen(A)[[1]][1] > lambda2=eigen(A)[[1]][2] > lambda3=eigen(A)[[1]][3] > wek1=eigen(A)[[2]][,1] > wek2=eigen(A)[[2]][,2] > wek3=eigen(A)[[2]][,3] > as.vector(A%*%wek1)
[1] -5.507842 -8.197611 -4.712735
> lambda1%*%wek1
[1] -5.507842 -8.197611 -4.712735
> as.vector(A%*%(2.7*wek2))
[1]  11.631263  8.350051 -16.601536
> lambda2%*%(2.7*wek2)
[1]  11.631263  8.350051 -16.601536
> A%*%wek3-lambda3%*%wek3
               [1] [1,]  2.220446e-16
[2,] -1.110223e-16
[3,] -2.498002e-16

 

Pokazywane przez R współrzędne wektorów własnych są przykładowe. Łatwo bowiem sprawdzić, że jeżeli w=(w1,w2,…,wn) jest wektorem własnym o wartości własnej równej λ to dla dowolnej liczby α również wektor u=αw=(αw1,αw2,…,αwn) jest wektorem własnym o tej samej wartości własnej. R wylicza te wektory własne, które znajdują się na sferze jednostkowej (czyli suma kwadratów ich współrzędnych jest równa 1). Pomnożenie ich współrzędnych przez dowolną stałą daje także wektor własny.

Kolejną rzeczą stosowana przy analizie danych jest wyliczanie tzw. wartości osobliwych macierzy i tworzenie jej rozkładu SVD (rozkładu według wartości osobliwych – Singular Value Decomposition). Dla danej macierzy A (nie koniecznie kwadratowej, o n wierszach i m kolumnach) rzędu r (tzn., że co najwyżej r rzędów w tej macierzy jest niezależnych) należy znaleźć takie macierze U (o n wierszach i r kolumnach) i V (o m wierszach i r kolumnach) oraz wartości liczbowe (tzw. wartości osobliwe) σ1, σ2, …,σmin(n,m), aby:

  • t(U)*U = Ir
  • V*t(V) = Ir
  • S = macierz [si,j]i=1,…,n, j=1,…,m spełniała warunek si,ii dla i=1,2,…,min(n,m) oraz si,j=0 gdy i≠j
  • U*S*V-1=A

gdzie t(U), t(V) oznaczają transpozycje macierzy U i V, Ir jest macierzą kwadratową r×r złożoną z samych zer i jedynek występujących tylko na jednej przekątnej (z lewego górnego rogu do prawego dolnego). Jest to macierz jednostkowa r-wymiarowa. Macierze o własnościach t(U)*U = Ir i V*t(V) = Ir nazywamy ortonormalnymi. Gdy któraś z macierzy U lub V jest kwadratowa, to jej transpozycja jest jednocześnie macierzą odwrotną. Wtedy taka macierz nazywana jest ortogonalną.

Biologom takie coś wydaje się kompletnie bez sensu. Dopiero przy tworzeniu wykresów typu biplot pojawia się sensowność wyliczania rozkładu svd danej macierzy, najczęściej wielokolumnowej macierzy danych. Pozwala on bowiem zamienić wielokolumnową macierz danych na macierz dwukolumnową, której zobrazowanie pozwala na wyciąganie wniosków o wzajemnych zależnościach między zmiennymi.

W R rozkład SVD macierzy uzyskujemy za pomocą funkcji svd().

> A=matrix(1:9, 3, 3); A[1,1]=-1; A[3,3]=-3
> svd(A)
$d
[1] 12.472399 7.371181 1.762089

$u
[,1]        [,2]       [,3]
[1,] 0.63433242  0.17395674 -0.7532340
[2,] 0.76771998 -0.02742333  0.6401984
[3,] 0.09071064 -0.98437138 -0.1509456

$v
[,1]       [,2]       [,3]
[1,] 0.09406686 -0.4316700  0.8971134
[2,] 0.55484061 -0.7254629 -0.4072536
[3,] 0.82662163  0.5360640  0.1712660

 

Funkcja svd() umieszcza wartości w liście, której pierwszym elementem jest wektor wartości osobliwych, drugim lewostronna macierz U, a prawym prawostronna macierz V. Można sprawdzić, że są spełnione wszystkie warunki, które powinien spełniać rozkład SVD.

> A=matrix(1:9, 3, 3); A[1,1]=-1; A[3,3]=-3
> svd(A)[[2]]->U
> svd(A)[[3]]->V
> S=matrix(rep(0,9),3,3)
> S[1,1]=svd(A)[[1]][1]
> S[2,2]=svd(A)[[1]][2]
> S[3,3]=svd(A)[[1]][3]
> U%*%t(U)
              [,1]          [,2]          [,3]
[1,]  1.000000e+00 -1.698403e-16  2.456260e-16
[2,] -1.698403e-16  1.000000e+00 -2.144213e-16
[3,]  2.456260e-16 -2.144213e-16  1.000000e+00
> V%*%t(V)
              [,1]          [,2]          [,3]
[1,]  1.000000e+00 -1.498910e-17 -7.337338e-17
[2,] -1.498910e-17  1.000000e+00 -1.594929e-16
[3,] -7.337338e-17 -1.594929e-16  1.000000e+00
> U%*%S%*%t(V)
[,1] [,2] [,3]
[1,]  -1    4    7
[2,]   2    5    8
[3,]   3    6   -3

 

Macierze wielowymiarowe – tablice

 

Tworzenie macierzy wielowymiarowej

Macierze są obiektami dwuwymiarowymi. Tymczasem w biologii często zachodzi potrzeba operowania podobnymi obiektami o wielu wymiarach. Potrzeba ich stosowania pojawia się głównie w programowaniu, gdy dane potrzebne do działań zależą od wartości k czynników. Numerując wartości i-tego czynnika liczbami 1,2,…,ni można odpowiednią wartość zapisać pod kryptonimem wart[j1,j2,…,jk]. Przykładowo, średnie ciężary osobników obu płci, pięciu gatunków w trzech lokalizacjach, można zapisać w 3-wymiarowej macierzy o rozmiarach 2×5×3, gdzie macierz[1,1,1] oznacza średni ciężar samic pierwszego gatunku w 1 lokalizacji, macierz[2,4,3] średni ciężar samców trzeciego gatunku w trzeciej lokalizacji itd. Nie jest to wygodne do obrazowania danych, ale przy przetwarzaniu danych względem wielu czynników może być bardzo przydatne. Tego typu macierze wielowymiarowe tworzy się funkcją array() z obowiązkową opcją dim=c(n1,n2,…,nk), gdzie k jest liczbą wymiarów, a ni liczbą różnych wartości i-tego czynnika (rozmiarem i-tego wymiaru).

> tab=array(1:24,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

 

Gdy liczba elementów ciągu, z którego tworzy się tablicę o rozmiarach c(n1, n2, …,nk), jest mniejsza od n1n2…nk ciąg ten jest kilkakrotnie powtarzany.

> tab=array(1:7,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    2
[2,]    2    6    3
[3,]    3    7    4
[4,]    4    1    5

, , 2

[,1] [,2] [,3]
[1,]    6    3    7
[2,]    7    4    1
[3,]    1    5    2
[4,]    2    6    3

 

Funkcja table() tworzy szczególną macierz wielowymiarową, tzw. tablicę. Argumentami tej funkcji są jeden, dwa lub więcej wektorów równej długości. Wynikiem jest liczba układów (wek1[i],wek2[i],wek3[i],…), które się w tych wektorach powtarzają.

> wek1=c("a","a","b","b","c","d")
> wek2=c(1,2,3,3,2,1)
> wek3=c("duże", "duże", "małe", "duże", "małe", "małe")
> table(wek1)
wek1
a b c d
2 2 1 1
> table(wek2, wek2)
    wek2
wek1 1 2 3
a 1 1 0
b 0 0 2
> table(wek2, wek2, wek3)
, , wek3 = duże

wek2
wek1 1 2 3
a 1 1 0
b 0 0 1
c 0 0 0
d 0 0 0

, , wek3 = małe

wek2
wek1 1 2 3
a 0 0 0
b 0 0 1
c 0 1 0
d 1 0 0

 

Zastosowanie funkcji class() do obiektu utworzonego funkcją array() pokazuje typ “array”, natomiast do tablicy utworzonej funkcją table() pokazuje typ “table”. Typy te różnią się drobnymi zmianami w sposobie nazywania poszczególnych wymiarów i wartości leżących na poszczególnych osiach. Pod innymi względami są identyczne. W dalszym ciągu pojęcia macierze wielowymiarowe jak i tablice będą używane wymiennie.

Nazywanie poszczególnych elementów macierzy wielowymiarowej

 Wymiar zarówno macierzy jak i tablic może być dowolny, równy także 1 (wektor), jak i 2 (macierz). Wymiary są ciągami złożonymi z tylu elementów, ile podano na odpowiednim miejscu w opcji dim. Opcja “dimnames” pozwala na nazywanie elementów poszczególnych wymiarów. Jej wartością jest lista ciągów nazw. Nazwy te nie są i nie stają się obiektami R.

> ciezary=c(22.84, 15.44, 19.47, 17.85, 18.84, 14.38, 22.26, 15.50, 18.19, 16.73, 19.26, 11.41, 21.00, 14.69, 12.19, 20.11, 12.62, 16.45, 19.57, 20.22, 18.45, 14.43, 17.35, 14.51)
> tab=array(ciezary,dim=c(4,3,2),dimnames=list(lokalizacja=c("Park","Zagajnik","Las","Puszcza"),gatunek=c("Aa", "Af", "Mg"),plec=c("samiec", "samica")))
> tab
, , plec = samiec

gatunek
lokalizacja    Aa    Af    Mg
Park     22.84 18.84 18.19
Zagajnik 15.44 14.38 16.73
Las      19.47 22.26 19.26
Puszcza  17.85 15.50 11.41

, , plec = samica

gatunek
lokalizacja    Aa    Af    Mg
Park     21.00 12.62 18.45
Zagajnik 14.69 16.45 14.43
Las      12.19 19.57 17.35
Puszcza  20.11 20.22 14.51

 

Macierze wielowymiarowe zaimplementowane do R

 Do R zaimplementowano interesującą tablicę czterowymiarową o nazwie Titanic, w które pokazano liczby pasażerów, którzy przeżyli i nie przeżyli katastrofy z podziałem na dzieci i dorosłych, kobiety i mężczyźni oraz status majątkowy pasażerów (pasażerowie pierwszej, drugiej i trzeciej klasy oraz załoga).

> Titanic
, , Age = Child, Survived = No

Sex
Class  Male Female
1st     0      0
2nd     0      0
3rd    35     17
Crew    0      0

, , Age = Adult, Survived = No

Sex
Class  Male Female
1st   118      4
2nd   154     13
3rd   387     89
Crew  670      3

, , Age = Child, Survived = Yes

Sex
Class  Male Female
1st     5      1
2nd    11     13
3rd    13     14
Crew    0      0

, , Age = Adult, Survived = Yes

Sex
Class  Male Female
1st    57    140
2nd    14     80
3rd    75     76
Crew  192     20

 

Inna macierz trzywymiarowa o nazwie HairEyeColor, wykonana została dla potrzeb nauki różnych technik R i pokazuje liczby studentów statystki w 1974 roku na Uniwersytecie w Delavare z podziałem na płeć, kolor oczu i kolor włosów.

> HairEyeColor
, , Sex = Male

Eye
Hair    Brown Blue Hazel Green
Black    32   11    10    3
Brown    53   50    25    15
Red      10   10     7     7
Blond     3   30     5     8

, , Sex = Female

Eye
Hair    Brown Blue Hazel Green
Black    36    9     5     2
Brown    66   34    29    14
Red      16    7     7     7
Blond     4   64     5     8

 

 

Odwoływanie się do elementów macierzy wielowymiarowej

 Do poszczególnych komórek macierzy k-wymiarowej odwołujemy się zgodnie ze schematem: Macierz[i1,i2,…,ik] gdzie ij jest j-ta wartości i-tego czynnika. Możliwe są odwołania do wszystkich wartości i-tego czynnika poprzez zastosowanie pustych wartości na pozostałych miejscach jak i inne kombinacje liczb i pustych miejsc.

> tab=array(1:24,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24
> tab[1,,]
     [,1] [,2]
[1,]    1   13
[2,]    5   17
[3,]    9   21
> tab[,1,]
     [,1] [,2]
[1,]    1   13
[2,]    2   14
[3,]    3   15
[4,]    4   16
> tab[,,1]
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> tab[1,1,]
[1] 1 13

 

W macierzach wielowymiarowych z nazwanymi elementami na osiach, odwoływać się można wymieniając nazwy poszczególnych elementów.

> tab=array(24:1,dim=c(4,3,2),dimnames=list(letters[1:4],letters[5:7],letters[8:9]))
> tab
, , h

e  f  g
a 24 20 16
b 23 19 15
c 22 18 14
d 21 17 13

, , i

e f g
a 12 8 4
b 11 7 3
c 10 6 2
d  9 5 1

> tab["b","e","h"]
[1] 23
> tab["b",,]
   h  i
e 23 11
f 19  7
g 15  3

 

Edycja macierzy wielowymiarowych

Poszczególne wyrazy tablicy wielowymiarowej można zmieniać odwołując się do nich i wprowadzając nowe wartości. Zamieniać można pojedyncze wartości, wiersze i kolumny w poszczególnych przekrojach jak i części tablicy o większej liczbie wymiarów.

> tab=array(1:24,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24
> tab[1,,]=-tab[1,,]
> tab
, , 1

[,1] [,2] [,3]
[1,]   -1   -5   -9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]  -13  -17  -21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

 

Usuwanie zbędnych kolumn, wierszy w przypadku tablicy wielowymiarowych sprowadza się do usunięcia danych dla pewnych wartości czynników stanowiących wymiary tablicy. Można przy tym wymieniać, które wartości mają zostać lub te, które mają być usunięte.

> tab=array(1:24,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24
> tab1=tab[c(1,3),,]
> tab1
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    3    7   11

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   15   19   23

#
> tab2=tab[-c(2,4),,]
> tab2
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    3    7   11

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   15   19   23

 

Nazwy poszczególnych wymiarów mogą być wprowadzone, zmienione lub usunięte poprzez funkcje dimnames().

> tab=array(1:24,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24
> dimnames(tab)=list(c("a","b","c","d"),c("aa","bb","cc"),c("aaa","bbb"))
> tab
, , aaa

aa bb cc
a  1  5  9
b  2  6 10
c  3  7 11
d  4  8 12

, , bbb

aa bb cc
a 13 17 21
b 14 18 22
c 15 19 23
d 16 20 24

 

Modyfikowanie nazw poszczególnych wymiarów może być wykonane poprzez modyfikację listy dimnaes(). Usunięcie nazw może zostać zrealizowane poprzez dimnames(macierz wielowymiarowa)=NULL.

Funkcje i działania na macierzach wielowymiarowych

 Macierze wielowymiarowe analizowane są przez funkcje mode(), typeof(), class() i str() dając następujące odpowiedzi:

> tab=array(1:24,dim=c(4,3,2))
> mode(tab)
[1] "numeric"
> typeof(tab)
[1] "integer"
> class(tab)
[1] "array"
> str(tab)
int [1:4, 1:3, 1:2] 1 2 3 4 5 6 7 8 9 10 ...

> tab=array(letters[1:24],dim=c(4,3,2))
> mode(tab)
[1] "character"
> typeof(tab)
[1] "character"
> class(tab)
[1] "array"
> str(tab)
chr [1:4, 1:3, 1:2] "a" "b" "c" "d" "e" "f" "g" "h" "i" ...

 

Funkcja str() zastosowana do tablicy z nazwanymi elementami na wymiarach, dodaje te informacje do wyników, jako atrybut obiektu.

> tab=array(1:24,dim=c(4,3,2))
> str(tab)
int [1:4, 1:3, 1:2] 1 2 3 4 5 6 7 8 9 10 ...

> tab=array(1:24,dim=c(4,3,2),
+ dimnames=list(c("a","aa","aaa","aaaa"),c("b","bb","bbb"),c("c","cc")))
> str(tab)
chr [1:4, 1:3, 1:2] "a" "b" "c" "d" "e" "f" "g" "h" "i" ...
- attr(*, "dimnames")=List of 3
..$ : chr [1:4] "a" "aa" "aaa" "aaaa"
..$ : chr [1:3] "b" "bb" "bbb"
..$ : chr [1:2] "c" "cc"

 

Podstawowe funkcje związane z tablicami pokazują ich budowę i strukturę. Są to:

  • dim(tablica) – liczba elementów poszczególnych wymiarów macierzy wielowymiarowej.
  • nrow(tablica) – liczba elementów pierwszego wymiaru macierzy wielowymiarowej.
  • ncol(tablica) – liczba elementów drugiego wymiaru macierzy wielowymiarowej.
  • length(tablica) – liczba elementów w macierzy wielowymiarowej.
  • dimnames(tablica) – nazwy poszczególnych wymiarów macierzy wielowymiarowej.
> tab=array(1:24,dim=c(4,3,2),dimnames=list(letters[1:4],c("K1","K2","K3"),c("pierwszy","drugi")))
> dim(tab)
[1] 4 3 2
> ncol(tab)
[1] 3
> nrow(tab)
[1] 4
> length(tab)
[1] 24
> dimnames(tab)
[[1]]
[1] "a" "b" "c" "d"

[[2]]
[1] "K1" "K2" "K3"

[[3]]
[1] "pierwszy" "drugi"


> mode(tab)
[1] "numeric"
> str(tab)
int [1:4, 1:3, 1:2] 1 2 3 4 5 6 7 8 9 10 ...
- attr(*, "dimnames")=List of 3
..$ : chr [1:4] "a" "b" "c" "d"
..$ : chr [1:3] "aa" "bb" "cc"
..$ : chr [1:2] "aaa" "bbb"

 

Utworzona macierz wielowymiarowa jest jednym obiektem, ale po jej wywołaniu widzimy tylko jej dwuwymiarowe przekroje. Standardowo przekroje te pojawiają się dla pierwszego (rzędy) i drugiego wymiaru (kolumny). Sposób wyświetlania takiej macierzy można zmienić przez zastosowanie funkcji aperm() i opcji perm. Jako wartość tej opcji należy podać w jaki sposób zmieniany jest porządek wymiarów (standardowo 1:d, gdzie d jest wymiarem tablicy). Zatem musi to być ciąg złożony z liczb {1,2,…,d} zapisanych w innej kolejności (np. c(2,1,5,4,3) dla d=5).

> tab=array(1:24,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

> aperm(tab, perm=c(2,1,3))
, , 1

[,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    5    6    7    8
[3,]    9   10   11   12

, , 2

[,1] [,2] [,3] [,4]
[1,]   13   14   15   16
[2,]   17   18   19   20
[3,]   21   22   23   24

> aperm(tab, perm=c(1,3,2))
, , 1

[,1] [,2]
[1,]    1   13
[2,]    2   14
[3,]    3   15
[4,]    4   16

, , 2

[,1] [,2]
[1,]    5   17
[2,]    6   18
[3,]    7   19
[4,]    8   20

, , 3

[,1] [,2]
[1,]    9   21
[2,]   10   22
[3,]   11   23
[4,]   12   24

 

Między tablicami a wektorami możliwe są standardowe działania +, *, -, /, i ^, przy czym obowiązują tu takie same zasady jak podczas działań na tablicach i wektorach. Wektory są skracane lub wydłużane do liczby elementów w tablicy i między odpowiadającymi sobie liczbami wykonywane są działania.

> tab=array(1:24,dim=c(4,3,2))
> tab
, , 1

[,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

[,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

> tab+1:7
, , 1

[,1] [,2] [,3]
[1,]    2   10   11
[2,]    4   12   13
[3,]    6   14   15
[4,]    8    9   17

, , 2

[,1] [,2] [,3]
[1,]   19   20   28
[2,]   21   22   23
[3,]   16   24   25
[4,]   18   26   27

Komunikat ostrzegawczy:
W poleceniu 'tab + 1:7':
długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu

 

Funkcje statystyczne na tablicach działają tak samo jak na wektorach tworzonych przez elementy tablicy.

> tab=array(1:24,dim=c(4,3,2))
> sum(tab)
[1] 300
> mean(tab)
[1] 12.5
> sd(tab)
[1] 7.071068
> var(tab)
[1] 50

 

Ramki, czyli tabele mające format baz danych

 

Tworzenie baz danych i nazywanie jej kolumn

Format zapisu danych, który dla biologów oznacza bazę danych, mają obiekty typu data.frame. Są to tabele złożone z jednakowej długości kolumn jednego typu danych odpowiadające zmiennym pomiarowym. Najczęściej są to wektory, szeregi czasowe i czynniki, ale czasami także macierze o odpowiedniej liczbie wierszy. Obiekty te tworzy się za pomocą funkcji data.frame().Wyniki pomiarów i eksperymentów w biologii mają często ten feler, że nie dla każdego obiektu uda się oznaczyć wartości wszystkich zmiennych. W bazach danych (nie tylko w R) istnieje zatem możliwość wprowadzania wartości pomijalnej. W R jest to NA. Większość programów pozwalająca na opracowanie baz danych pomija te wiersze i nie generuje błędu (np. przez wprowadzenie tam 0).

> plec.ciezar = data.frame(1:5, c("samica","samiec","samica",NA,"samiec"),
+ c(21,22,20.5,21,22.5))
> plec.ciezar
X1.5 c..samica....samiec....samica...NA...samiec.. c.21..22..20.5..21..22.5.
1  1                                        samica                     21.0
2  2                                        samiec                     22.0
3  3                                        samica                     20.5
4  4                                          <NA>                     21.0
5  5                                        samiec                     22.5

 

Nazywanie zmiennych, czyli kolumn w bazach danych, jest standardem. Gdy się tego nie zrobi programy komputerowe wprowadzają nazwy domyślne, a R robi to wyjątkowo złośliwie tworząc nazwę z wartości zmiennych. Należy zatem zawsze pamiętać o nazywaniu zmiennych.

> plec.ciezar = data.frame(1:5, plec=c("samica","samiec","samica",NA,"samiec"),
+ ciezar=c(21,22,20.5,21,22.5))
> plec.ciezar
  X1.5   plec ciezar
1    1 samica   21.0
2    2 samiec   22.0
3    3 samica   20.5
4    4   <NA>   21.0
5    5 samiec   22.5

 

Kiedy tworzymy bazę danych z obiektów (wektorów, szeregów czasowych, czynników), ich nazwy te stają się nazwami kolumn baz danych. Można je nazwać inaczej przez zastosowanie formuły: nazwa_kolumny=nazwa_obiektu.

> Nr=1:5
> plec=c("samica","samiec","samica",NA,"samiec")
> ciezar=c(21, 22,20.5,21,22.5)
> plec.ciezar=data.frame(Nr, plec, ciezar)
> plec.ciezar
  Nr   plec ciezar
1  1 samica   21.0
2  2 samiec   22.0
3  3 samica   20.5
4  4   <NA>   21.0
5  5 samiec   22.5
plec.ciezar=data.frame(NR=Nr,PLEC=plec,CIEZAR=ciezar)
> plec.ciezar
  NR   PLEC CIEZAR
1  1 samica   21.0
2  2 samiec   22.0
3  3 samica   20.5
4  4   <NA>   21.0
5  5 samiec   22.5

 

Baza danych wyświetla się zawsze z numeracją wierszy po lewej stronie. Wydaje się zatem, że numeracja osobników wprowadzana do bazy nie jest potrzebna. Jednak ta numeracja może być stosowana w dokumentacji pomiarów i wszelkie sortowania, filtrowania zaburzą porządek w tym ciągu, ale zostawią możliwość dotarcia do danych oryginalnych. Numeracja R nie jest związana z danymi – to aktualnie istniejący w bazie numer wiersza.

Po połączeniu za pomocą funkcji data.frame() wektorów, szeregów czasowych i czynników zachodzą następujące zmiany typów:

  • Wektor numeryczny zachowuje swój typ i kolumna przez niego tworzona jest wektorem numerycznym.
  • Wektor tekstowy zostaje zamieniony na czynnik, nawet wtedy, gdy traktowany jako wektor zajmowałby mniej miejsca.
  • Czynnik numeryczny lub tekstowy zachowuje swój typ.
  • Szereg czasowy zachowuje swój typ.

Najwygodniej to sprawdzić za pomocą funkcji str(), która pokazuje typ obiektu oraz typy poszczególnych kolumn.

> X=rep(1:2,13)
> Y=letters
> Z=ts(letters,200,frequency=12)
> XYZ=data.frame(X,Y,Z)
> str(XYZ)
'data.frame': 26 obs. of 3 variables:
$ X: int 1 2 1 2 1 2 1 2 1 2 ...
$ Y: Factor w/ 26 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10 ...
$ Z: Time-Series from 200 to 202: a b c d ...
> XYZ=data.frame(as.factor(X),as.vector(Y),as.vector(Z))
> str(XYZ)
'data.frame': 26 obs. of 3 variables:
$ as.factor.X.: Factor w/ 2 levels "1","2": 1 2 1 2 1 2 1 2 1 2 ...
$ as.vector.Y.: Factor w/ 26 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10 ...
$ as.vector.Z.: Factor w/ 26 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10 ...

 

Specyficzną bazę danych tworzy funkcja expand.grid(). Funkcja ta służy do tworzenia tabeli z ciągów c1,c2,…,ck, tak że każdy element ciągu c2 jest w rzędzie z każdym elementem ciągu c1, każdy element ciągu c3 jest w rzędzie z każdą wcześniej wygenerowaną parą itd.

> expand.grid(letters[1:2],5:6,c(TRUE,FALSE))
  Var1 Var2  Var3
1    a    5  TRUE
2    b    5  TRUE
3    a    6  TRUE
4    b    6  TRUE
5    a    5 FALSE
6    b    5 FALSE
7    a    6 FALSE
8    b    6 FALSE
> expand.grid(letters[1:2],5:6,c(T,F))->tabela
> tabela[[1]]
[1] a b a b a b a b
Levels: a b
> tabela[[2]]
[1] 5 5 6 6 5 5 6 6
> tabela[[3]]
[1] TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE

 

Są sytuacje, gdy funkcja ta znacznie przyśpiesza utworzenie bazy danych, albo jej szkieletu. Przykładowo, gdy w kilku sezonach i lokalizacjach rozstawiono pułapki Barbera, a następnie oznaczono gatunki owadów, które złapały się do nich. Najlepiej jest wykonać funkcje expand.grid() dla ciągów oznaczających sezony, lokalizacje i wykaz gatunków. Potem można dodać kolumną z samymi zerami, która już ręcznie zostanie zmodyfikowana przez zastąpienie odpowiednich zer liczbą owadów danego gatunku złowionych w danym sezonie, w danej lokalizacji, o ile były tam takie owady. W takich bazach danych zera są także wynikiem.

Wczytywanie do R baz danych zapisanych w formacie tekstowym

Na ogół badacze posiadają własny podstawowy typ baz danych, w których przechowują komplet informacji o wykonanych doświadczeniach i obserwacjach. W takiej bazie danych wykonuje się wszelkie poprawki, uzupełnia o nowe obserwacje. Te bazy danych, często redukowane w celu opracowania tylko jednego zagadnienia, eksportuje się potem do innych programów zmieniając ich format, pozostawiając jednocześnie oryginał do dalszych analiz. W latach 90-tych podstawowe bazy danych przeważnie przechowywane były w formacie “dbf” (dbase), z czasem rozpowszechnił się Excel (na małe projekty) i Access (na duże bazy danych). Wynikało to z wygody tworzenia takich baz w tych programach, zwłaszcza gdy dane do nich przepisywano ręcznie z notatek. Czasy się zmieniają i obecnie w wielu dziedzinach biologii, chemii, fizyki i nauk środowiskowych stosuje się aparaturę zapisującą wyniki pomiarów na nośniki elektroniczne. Z reguły dane takie zapisywane są do plików tekstowych. Podstawową umiejętnością badacza stało się importowanie takich danych do różnych programów, łączenie w jedną bazę danych i przetwarzanie. Można obecnie twierdzić, że podstawowe dane przechowuje się w plikach tekstowych.

Pliki tekstowe tworzone automatycznie mają format bazy danych, z reguły pierwsza linijka przeznaczona jest na nazwy, a pod nią pojawiają się wartości dotyczące danego pomiaru. Przykładowo plik generowany przez aparaturę do pomiaru kierunku i prędkości wiatru produkuje następujący plik tekstowy:

Natomiast licznik przerywań notujący numer pomiaru, datę, godzinę, minutę i sekundę przechodzenia ptaka przez otwór budki lęgowej daje plik tekstowy:

Pliki tekstowe umieszcza się w katalogu zrobionym dla danego tematu badawczego. W tym folderze trzyma się tekstowe bazy danych, programy i zestawy instrukcji zapisane w plikach tekstowych wykonujące w R określone analizy oraz wyniki takich obliczeń. Katalog taki robi się samemu, a potem informuje R o jego nazwie i ścieżce dostępu, czyli zmienia w R katalog roboczy.

Jaki jest aktualny katalog roboczy odkrywa się za pomocą instrukcji getwd(), a ewentualną zmianę katalogu roboczego można określić za pomocą instrukcji setwd(“C:/….”). W nazwach katalogów stosuje się ukośniki “/”, także w systemie windows. Przykładowo, gdy pliki tekstowe znajdują się na pulpicie użytkownika “milka” można wykonać wybrane operacje pokazane poniżej.

Dla Windows XP

> # Windows XP
> getwd()
[1] "C:/Documents and Settings/Milka/Moje dokumenty"
> setwd("C:/Documents and Settings/milka/Desktop/...") 

 

Dla Windows 7, 8, 10:

> # Windows 10
> getwd()
[1] "C:/Users/milka/Documents"
> setwd("C:/Users/milka/Desktop/...") 

 

Zmiany katalogu roboczego można dokonać także metodą kliknięć. Najpierw należy wybrać “Plik”/”File”, potem “Zmień katalog”/”Change dir” i w okienku pokazanym poniżej dojść do wybranego katalogu roboczego i kliknąć OK/Zastosuj.

 

 

 

 

 

 

 

 

 

Jeżeli nie chcemy zmieniać katalogu roboczego a plik, który chcemy zaimportować, jest w innym miejscu, trzeba jego nazwę podać wraz z adresem (ścieżką dostępu), w którym stosowane będą ukośniki “/”.

Po wybraniu właściwego katalogu można sprawdzić, czy znajdują się w nim gotowe do transportu pliki tekstowe. Za pomocą funkcji list.files() uzyskamy listę nazw plików znajdujących się w katalogu roboczym.

> getwd()
[1] "C:/Users/milka/Downloads"
> list.files()
[1] "desktop.ini"
[2] "FileZilla_3.7.4.1_win32-setup.exe"
[3] "iview437_setup_www.INSTALKI.pl.exe"
[4] "PDFCreator-1_9_2-setup-beta.exe"
[5] "przerywania.txt"
[6] "wiatr.dat" 

 

W pierwszej bazie separatorami były tabulatory, a w drugiej przecinki. Poszczególne obserwacje rozdzielone są znakiem końca wiersza. Dość często separatorami są średniki, spacje, ukośniki lub inne symbole widoczne na klawiaturze. Programy tekstowe mają różne rozszerzenia, najczęściej .txt lub .csv lub .dat lub .prn. Ich zaimportowanie do R polega na używaniu instrukcji read.table(“nazwa.txt”) z różnymi opcjami. Wystarczy przyjrzeć się przykładom.

> # Zaimportowanie pliku wiatr.dat, pokazanego na pierwszym rysunku
> wiatr=read.table("wiatr.dat", header=TRUE, sep="\t")
> wiatr
     date Dir Spd
1 1961071 360 7.2
2 1961072  10 7.7
3 1961072 360 9.3
4 1961072 360 9.3
5 1961073 360 8.7
6 1961073  30 7.2
7 1961073  20 7.2
8 1961074 110 1.5
> # zaimportowanie pliku przerywania.txt pokazanego na drugim rysunku
> przeryw=read.table("przerywania.txt", header=FALSE, sep=",")
> przeryw
  V1       V2       V3
1  1 14-04-12 09:58:13
2  2 14-04-12 10:10:52
3  3 14-04-12 10:23:31
4  4 14-04-12 10:28:47
5  5 14-04-12 10:44:11
6  6 14-04-12 10:51:07
7  7 14-04-12 11:00:28
8  8 14-04-12 11:05:22
9  9 14-04-12 11:28:37 

 

Pod opcjami kryją się pewne wybrane wartości, które są przyjmowane automatycznie w sytuacji braku jakiejś opcji. Przykładowo za header podstawiana jest automatycznie wartość FALSE i w drugim przykładzie w ogóle nie trzeba pisać tej opcji. Za separatory podstawiana jest wartość “\t” (co oznacza tabulatory) i opcji tej nie trzeba pisać w pierwszym przykładzie.

> # Zaimportowanie pliku wiatr.dat, pokazanego na pierwszym rysunku
> wiatr=read.table("wiatr.dat", header=TRUE)
> wiatr
     date Dir Spd
1 1961071 360 7.2
2 1961072  10 7.7
3 1961072 360 9.3
4 1961072 360 9.3
5 1961073 360 8.7
6 1961073  30 7.2
7 1961073  20 7.2
8 1961074 110 1.5
> # zaimportowanie pliku przerywania.txt pokazanego na drugim rysunku
> przeryw=read.table("przerywania.txt", sep=",")
> przeryw
  V1       V2       V3
1  1 14-04-12 09:58:13
2  2 14-04-12 10:10:52
3  3 14-04-12 10:23:31
4  4 14-04-12 10:28:47
5  5 14-04-12 10:44:11
6  6 14-04-12 10:51:07
7  7 14-04-12 11:00:28
8  8 14-04-12 11:05:22
9  9 14-04-12 11:28:37 

 

Lista opcji instrukcji read.table(“”) jest dość rozbudowana. Podajemy tylko najważniejsze.

  • header Pytanie, czy w pierwszej linijce są nagłówki mające wartości TRUE i FALSE, automatycznie FALSE.
  • sep Wskazanie separatora zmiennych, mający wartości będące symbolami znajdującymi się na klawiaturze zapisywane w “”. Automatycznie tabulator oznaczany dwuznakiem “\t”.
  • quote Wskazanie znaków, jakie w pliku tekstowym oznaczają zmienne tekstowe.
  • dec Wskazanie znaku stosowanego przy zapisie liczb dziesiętnych (, lub .).
  • row.names Zastąpienie standardowej pierwszej kolumny (kolejne liczby całkowite dodatnie) kolumną nazw wierszy.
  • col.names Zastąpienie standardowych nazw kolumn V1 V2,…,V3 podanym wektorem nazw.
  • nrrows Maksymalna liczba wierszy, jaka ma być zaimportowana.
  • skip Liczba wierszy, jaką na początku należy pominąć.

Wariantami funkcji read.table() są: read.csv(), read.csv2(), read.delim() i read.delim2(). Służą do przenoszenia danych zapisanych w sformatowanych plikach tekstowych. Ponadto nie robią one nic nowego, czego nie robiłaby funkcja read.table() z odpowiednimi opcjami.

Wykonywanie jakichkolwiek operacji na zaimportowanej bazie danych wymaga przypisania jej jakiemuś obiektowi. Trzeba koniecznie po ustaleniu właściwych opcji i poprawnym imporcie, wymyślić klarowną nazwę dla ramki danych i zastosować instrukcję przypisania.

> # Zaimportowanie pliku wiatr.dat, pokazanego na pierwszym rysunku
> read.table("wiatr.dat", header=TRUE)
     date Dir Spd
1 1961071 360 7.2
2 1961072  10 7.7
3 1961072 360 9.3
4 1961072 360 9.3
5 1961073 360 8.7
6 1961073  30 7.2
7 1961073  20 7.2
8 1961074 110 1.5
> read.table("wiatr.dat", header=TRUE)->wiatr

 

Oczywiście może być wiatr=read.table(…). Po imporcie danych trzeba koniecznie sprawdzić, czy dane numeryczne są rzeczywiście numeryczne. Najlepiej do tego nadaje się funkcja str().

> wiatr = read.table("wiatr.dat", header=TRUE)
> str(wiatr)
'data.frame': 8 obs. of 3 variables:
$ date: int 1961071 1961072 1961072 1961072 1961073 1961073 1961073 1961074
$ Dir : int 360 10 360 360 360 30 20 110
$ Spd : num 7.2 7.7 9.3 9.3 8.7 7.2 7.2 1.5

 

Gdyby przy kolumnie Spd (prędkość wiatru) pojawiła się informacja Spd : char lub Spd : factor oznaczyłoby to, że dane są źle zaimportowane. Może są przecinki w liczbach rzeczywistych, podczas gdy system wymaga kropek lub gdzieś pojawia się słowo brak lub inne tego typu wyrażenie nieliczbowe. Słowa te należy w pliku tekstowym zamienić na NA i ponownie wykonać funkcję read.data().

Importowanie baz danych z arkuszy kalkulacyjnych

 Stosowanie funkcji data.frame() nie jest wygodnym sposobem na tworzenie bazy danych. Najpierw trzeba utworzyć zmienne wektorowe z danymi jednego typu pilnując kolejności ich wpisywania, by na każdym i-tym miejscu ciągu dane pochodziły z pomiarów i-tego obiektu. R nie ma możliwości skontrolowania, czy wyniki na trzecim miejscu w każdym ciągu pochodziły od tego samego eksperymentu czy zwierzęcia. Bardzo łatwo pomylić się w dopasowaniu wartości różnych zmiennych i uzyskać poprawny obiekt pod względem informatycznym, a dający błędne wyniki przy opracowaniu danych. Na szczęście można przenieść na raz wszystkie dane z innych programów, gdzie tworzenie baz danych jest dużo prostsze.

Arkusze kalkulacyjne (Excel, Open Office(arkusz kalkulacyjny) lub Libre Office4(Arkusz kalkulacyjny)) są znacznie wygodniejsze od R do tworzenia bazy danych. Konieczne jest zatem znalezienie prostego sposobu przeniesienia utworzonej już bazy danych np. z Excela do R. Tymczasem przejrzenie procedur wczytywania plików do R pokazuje, że prostej funkcji wykonującej taką operację, nie ma. Trzeba poradzić sobie metodą:

  • transportu danych do pliku tekstowego,
  • importu danych tekstowych do R.

Uniwersalna metoda jest dość pracochłonna i polega na:

  • Wykonaniu bazy danych w arkuszu kalkulacyjnym, w którym nie będzie pustych miejsc. Braki danych zastąpić słowem “NA”. Z kolumn tekstowych usunąć spacje lub zastąpić kropką lub podkreśleniem.
  • Nazwy kolumn wpisać w jednej linijce. Usunąć z nich spacje, przecinki, nawiasy i inne znaki, które nie mogą występować w nazwach obiektów R (patrz rozdział “Instrukcja przypisania i obiekty”, podrozdział “Literały”)
  • Zaznaczeniu całej bazy danych i przeniesienie zaznaczonej zawartości do schowka przez wciśnięcie Ctrl+C.
  • Utworzeniu z katalogu roboczym pustego pliku tekstowego (z rozszerzeniem .txt). Nazwanie go.
  • Otworzenie pliku tekstowego i skopiowanie do niego zawartości schowka (wciśnięcie Ctrl_V).
  • Zamiana w pliku tekstowym separatorów w liczbach dziesiętnych “,” na “.” (Zamień/zastąp “,” na “.” wszędzie/cały dokument itd.).
  • Zapamiętaniu.
  • Otworzeniu R
  • Zmianę katalogu R na odpowiedni katalog roboczy, gdzie znajduje się wykonany plik tekstowy.
  • Sprawdzeniu, czy pracujemy we właściwym katalogu. Najwygodniej jest to wykonać za pomocą funkcji list.files(), w której nie wpisujemy żadnego argumentu, a która powoduje wyświetlenie wszystkich plików w katalogu roboczym. Jeżeli jest tam nazwa zrobionego właśnie pliku tekstowego – dobrze.
  • Wykonaniu funkcji read.table(“nazwa.pliku.tekstowego.txt”, header=TRUE).
  • Gdy obraz danych po wykonaniu takiej instrukcji wygląda na bazę danych, zapisaniu go jako obiektu R, czyli wykonaniu przypisania wyniku funkcji read.table(“nazwa.pliku.tekstowego.txt”, header=TRUE) jakiemuś obiektowi o odpowiedniej nazwie.
  • Sprawdzeniu czy utworzony obiekt ma właściwą strukturę przez wykonanie funkcji str(nazwa obiektu). Jeżeli kolumny liczbowe mają typ “num” lub “int” – dobrze. Jeżeli nie, w pierwszej kolejności sprawdzić plik tekstowy: czy nie ma tam separatorów “,” w liczbach, czy w tym ciągu kolumn nie pojawia się gdzieś jakiś tekst, znak nieliczbowy, podwójna kropka, słowo “Brak” zamiast NA itd. Poprawić, zapisać. Funkcję read.table() i przypisanie jej obiektowi, wykonać ponownie.
  • Po uzyskaniu poprawnej bazy danych, wykonaniu save.image(“Nazwa_wejścia_do_R.RData”), gdzie za Nazwę_wejścia_do_R należy wstawić swoją nazwę. W katalogu roboczym powinna pojawić się odpowiednia ikonka R z wprowadzoną nazwą. Wykonana baza danych będzie w niej zapamiętana, jako obiekt o wprowadzonej nazwie.

Opisana metoda polega na edycji baz danych zarówno w arkuszu kalkulacyjnym, jak i pliku tekstowym, pod katem ich prawidłowego przeczytania przez funkcję read.table() opisaną szczegółowo w poprzednim podrozdziale. Sprawdza się dla nie bardzo dużych baz danych z niewielką liczbą kolumn.

Niekiedy zależy nam na zachowaniu spacji w kolumnach tekstowych (ale spacji zawsze nie może być w nazwach kolumn). W zależności od wersji systemu i programu tworzącego arkusz jedne metody są lepsze inne gorsze. Trzeba popróbować by wybrać najlepszą. Poniżej podane są najczęściej publikowane przepisy.

Windows XP/Excel, OpenOffice.Calc i LibreOffice.Calc

  • Otworzyć arkusz kalkulacyjny z bazą danych. Powinna ona znajdywać się cała w jednym arkuszu. Usunąć zbędne tabele, wykresy, posumowania (ewentualnie skopiować samą bazę danych do nowego arkusza).
  • W miejsca braków danych (puste całe komórki) wpisać (kazać wpisać poprzez edytor) NA
  • Zapamiętać plik jako “nazwa.csv” w swoim katalogu roboczym. Za nazwę wybrać możliwie prosty tekst, bo będzie on później ręcznie wpisywany do R.
  • Zgodzić się na “Ostrzeżenie o zapisaniu tylko jednego arkusza w wybranym formacie”.
  • Potwierdzić gotowość zapisu.
  • Zamknąć arkusz kalkulacyjny nie zapisując zmian w bazie danych (ważne!!!).
  • Znaleźć plik “nazwa.csv”.
  • Otworzyć go przez Notatnik i wyrzucić wszelkie niepotrzebne znaki. Ma zostać tylko baza danych z nazwanymi kolumnami, przy czym nazwy kolumn są prawidłowymi literałami jako nazwy obiektów w R (patrz rozdział “Instrukcja przypisania i obiekty”, podrozdział “Literały”)
  • Zapamiętać plik nazwa.csv.

Otworzyć R. Przejść do katalogu, gdzie znajduje się zbiór “nazwa.csv”. Jego transport do R wymaga zastosowania jednej z funkcji read.csv(), read.csv2(), read.delim(), read.delim2(), albo opisaną w poprzednim podrozdziale funkcję read.table() z opcjami. Pojawia się czasem problem z polskojęzycznym systemem Windows. Czasami pokazuje on separatory “,” podczas gdy R widzi “;” oraz znaki liczb dziesiętnym “,” podczas gdy R widzi “.”. W XP jest mniej takich problemów i generalnie jeżeli w pliku “nazwa.csv” rozdzielnikami kolumn były przecinki stosujemy funkcję read.csv(), a jeżeli rozdzielnikami były średniki stosujemy funkcję read.csv2()

> read.csv2("nazwa.csv")
Nr;PLEC;SEZON;CIEZAR;FLUOR
1 1;samica;jesień;2.7;26.5
2 2;samica;jesień;4.1;26.0
3 3;samiec;zima;3.6;36.0
4 4;samica;wiosna;3;24.7
5 5;samica;jesień;4;25.9
6 6;samiec;zima;3.4;37.1
7 7;samiec;wiosna;2.6;30.2
8 8;samiec;zima;3.2;36.6
9 9;samiec;wiosna;3.6;295
10 10;samica;zima;3.4;31.0
... 
> # jeżeli zobaczyliśmy powyższą tabelę to wracamy do wpisywania danych do "nowanazwa" z użycie read.csv2
> read.csv2("nazwa.csv")
   Nr   PLEC  SEZON CIEZAR FLUOR
1   1 samica jesień    2.7 26.5
2   2 samica jesień    4.1 26.0
3   3 samiec   zima    3.6 36.0
4   4 samica wiosna      3 24.7
5   5 samica jesień      4 25.9
6   6 samiec   zima    3.4 37.1
7   7 samiec wiosna    2.6 30.2
8   8 samiec   zima    3.2 36.6
9   9 samiec wiosna    3.6 29.5
10 10 samica   zima    3.4 31.0
...
> # tę tabelę przypisujemy jakiemuś obiektowi.
> skunks=read.csv2("nazwa.csv") 

 

Ostatni obraz jest typowy dla poprawnej ramki danych. Można ją sprawdzić np. funkcją str(), czy dane numeryczne są rzeczywiście numeryczne, kolumny prawidłowo nazwane itp.

Windows 7, 8 i 10/Excel, OpenOffice.Calc i LibreOffice.Calc

  • Utworzyć na pulpicie/własnym katalogu plik tekstowy o własnej nazwie, np. “nazwa.txt”
  • Otworzyć plik excelowy przez Excela, lub LbreOffice.calc z bazą danych.
  • W miejsca braków danych (puste całe komórki) wpisać (kazać wpisać) NA
  • Zaznaczyć bazę danych wraz z nagłówkami kolumn i przenieść ją do pamięci systemowej klikając na Ctrl+C.
  • Otworzyć przygotowany plik tekstowy i skopiować bazę danych klikając Ctrl+V.
  • Poprawić bazę danych tak by uniknąć konfliktów przy jej imporcie do R
    • – zamienić przecinki w liczbach dziesiętnych na kropki używając w “edytuj” polecenia “zamień” (czasem “znajdź/zamień”) i stosując zamień wszystko,
    • – zamienić niewidoczne pauzy na “_” także używając etytuj-zamień-zamień wszystko,
    • – sprawdzić, czy nie pojawiły się w tekście niepotrzebne znaki, zmiany porządku w tekście, czy nazw kolumn jest tyle samo co kolumn, czy każdy wiersz składa się z takiej samej liczby kolumn, Błędy usuwamy ręcznie lub, gdy jest ich więcej, stosując automatyczne polecenia.
  • Zapamiętać.

Przejść do R. Zmienić katalog na ten, w którym jest plik tekstowy. Można sprawdzić poleceniem list.files() czy w katalogu roboczym jest wasz plik tekstowy. Zastosować polecenie read.table() z opcją header=TRUE.

> read.table("nazwa.txt")
    V1    V2      V3     V4  V5
1   Nr  PLEC   SEZON CIEZAR FLUOR
2   1 samica jesień    2.7 26.06
3   2 samica jesień    4.1 26.0
4   3 samiec   zima    5.6 36.0
5   4 samica wiosna      3 24.7
6   5 samica jesień      8 25.9
7   6 samiec   zima    3.4 37.1
8   7 samiec wiosna    2.6 30.2
9   8 samiec   zima    3.2 36.6
10  9 samiec wiosna    3.6 29.5
11 10 samica   zima    3.4 31.0
...
> # Nagłówki kolumn zostały potraktowane jako wartości i wszystkie dane są typu "character".
> read.table("nazwa.txt", header=TRUE)
   Nr   PLEC   SEZON CIEZAR FLUOR
1   1 samica jesień    2.7 26.5
2   2 samica jesień    4.1 26.0
3   3 samiec   zima    3.6 36.0
4   4 samica wiosna      3 24.7
5   5 samica jesień      4 25.9
6   6 samiec   zima    3.4 37.1
7   7 samiec wiosna    2.6 30.2
8   8 samiec   zima    3.2 36.6
9   9 samiec wiosna    3.6 29.5
10 10 samica   zima    3.4 31.0
...
> # tę tabelę przypisujemy jakiemuś obiektowi.
> skunks=read.table("nazwa.txt", header=TRUE) 

 

W pliku tekstowym zrobionym metodą Ctrl+C (dla danych z Excela), Ctrl+V (w pliku tekstowym) separatorami kolumn są tabulatory. Jest to wartość domyślna dla opcji sep=””. Gdy nie wychodzi prawidłowa ramka danych trzeba tę opcję dodać (sep=”\t”). Gdy liczby są typu character można dodać opcję dec=”.” albo dec=”,” i sprawdzić czy to poprawia transport danych.

> read.table("nazwa.txt", header=TRUE, sep="\t", dec=",")

 

Większość tych kłopotów wynika stąd, że w tłumaczonych na język polski niektórych wersjach systemu windows inne znaki i nazwy wyświetlane są na ekranie, a inne widzi R.

Po pozornie poprawnym zaimportowaniu bazy danych sprawdzamy ją stosując funkcję str().

> str(skunks)
'data.frame': 50 obs. of 5 variables:
$ Nr    : int 1 2 5 14 32 39 44 4 24 28 ...
$ PLEC  : Factor w/ 2 levels "samica","samiec": 1 1 1 1 1 1 1 1 1 1 ...
$ SEZON : Factor w/ 3 levels "jesień","wiosna",..: 1 1 1 1 1 1 1 $
$ CIEZAR: num 2.7 4.1 4 3.1 3.9 3.3 3.7 3 3.5 4 ...
$ FLUOR : num 26.2 24.7 24.3 28.1 26.4 ...

 

Zwracamy uwagę czy typy zmiennych są prawidłowe. Kolumny tekstowe w bazach danych są zapisem zmiennej dyskretnej mając często tylko 2 lub 3 lub nieco więcej wartości. Zmienne te zostały potraktowane, jako czynniki (factor) i najczęściej jest to prawidłowe. Zapis takich czynników został skrócony przez przypisanie nazwom liczb i wartości zapisane są w postaci liczb.

Dopiero gdy uzyskamy prawidłową ramkę danych zapisujemy obszar roboczy lub stosujemy funkcję save.image(“nazwa.RData”) do stworzenia “własnego wejścia do R”, w którym utworzony obiekt zostanie zapamiętany.

Bazy danych zaimplementowane do R

Do R zaimplementowano 104 obiekty z danymi, z czego ponad połowa ma format bazy danych. Można je zobaczyć na stronie: http://127.0.0.1:25589/library/datasets/html/datasets-package.html, do której można przejść wpisując do konsoli R ?datasets. Na stronie należy kliknąć na “index”. Wyświetli się lista nazw obiektów z krótkim opisem. Nazwy są linkami do stron z nieco szerszym opisem. Sam obiekt zobaczymy wpisując jego nazwę (bez literówek i uwzględniając wielkość liter) do konsoli R. Poniżej podano tylko pierwszych 6 wierszy wybranych baz danych.

Niektóre z zaimplementowanych do R baz danych zyskały sobie miano “kultowych” wśród użytkowników R. Są bowiem na ich przykładzie opisane różne procedury graficzne i statystyczne publikowane w podręcznikach internetowych i drukowanych. Do najbardziej znanych należy baza iris pokazująca długość i szerokość działek kielicha i płatków kwiatów 150 irysów zaliczonych do trzech gatunków. Została ona opublikowana przez R. Fishera i od lat służy studentom dla nauki statystyki (zwłaszcza wykonywania testów wieloczynnikowych). Doczekała się nawet własnej strony na angielskojęzycznej Wikipedii.

> head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

 

Drugą tego typu bazą jest ToothGrowth zawierająca dane o długości zębów kawii domowych (świnek morskich) w zależności od dawki witaminy C i sposobu jej dozowania (kwas askorbinowy, sok pomarańczowy).

> head(ToothGrowth)
    len supp dose
1   4.2   VC  0.5
2  11.5   VC  0.5
3   7.3   VC  0.5
4   5.8   VC  0.5
5   6.4   VC  0.5
6  10.0   VC  0.5

 

Ciekawą baza danych jest quakes charakteryzująca trzęsienia ziemi na Fidżi od 1964. Jej zmiennymi jest długość i szerokość geograficzna, głębokość, skala Richtera oraz liczba stacji rejestrująca to trzęsienie.

> head(quakes)
     lat   long depth mag stations
1 -20.42 181.62   562 4.8       41
2 -20.62 181.03   650 4.2       15
3 -26.00 184.10    42 5.4       43
4 -17.97 181.66   626 4.1       19
5 -20.42 181.96   649 4.0       11
6 -19.68 184.31   195 4.0       12

 

Prostszych i bardziej skomplikowanych baz danych jest w R około 50. Są wykorzystywane w przykładach zastosowania różnych funkcji w R w pliku pomocy. Przykładowo, bazy InsectSprays i OrchardSprays zostały wykorzystane do zobrazowania możliwości funkcji graficznej boxplot()

Odwołania do elementów baz danych

Każdy element bazy danych ma swoją pozycję (nr wiersza i nr kolumny) i wyświetlenie go polega na napisaniu w konsoli nazwa.bazy[i,j], gdzie i jest numerem wiersza, j numerem kolumny. Poprzez indeksowanie baza[c(i1,i2,i3,…in),c(j1,j2,…,jk)] uzyskuje się podtabelę złożoną tylko z części wierszy i kolumn. Dodanie znaku – przed c eliminuje z bazy wymienione wiersze lub kolumny.

> skunks
    PLEC  SEZON CIEZAR     F
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks[c(1,3,5),c(1,3,4)]
    PLEC CIEZAR FLUOR
1 samica    2.7 26.51
3 samiec    3.6 36.02
5 samica    4.5 25.97
> skunks[-c(1,3,5),c(1,3,4)]
    PLEC CIEZAR FLUOR
2 samica    4.1 26.06
4 samica    3.0 24.78
> skunks[c(1,3,5),-c(1,3,4)]
[1] jesień zima   jesień
Levels: jesień wiosna zima

 

Dopuszczalna jest także składnia baza[,2:4] (wszystkie wiersze i 3 ostatnie kolumny) oraz baza[1:3,] (trzy pierwsze wiersze i wszystkie kolumny). Przykładowo baza[c(1:3,5),] oznacza wszystko bez czwartego wiersza.

Zastosowanie składni baza[n] powoduje pokazanie n-tej kolumny. Natomiast składnia baza[[n]] oraz baza[,n] spowodują pokazanie wartości n-tej kolumny w postaci wektora lub czynnika.

> skunks
    PLEC  SEZON CIEZAR     F
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks[2]
    SEZON
1 jesień
2 jesień
3   zima
4 wiosna
5 jesień
> skunks[[2]]
[1] jesień jesień zima   wiosna jesień
Levels: jesień wiosna zima
> skunks[,2]
[1] jesień jesień zima   wiosna jesień
Levels: jesień wiosna zima

 

Innym sposobem odwołania się do kolumny o danej nazwie (np. “PLEC”) jest składnia “nazwa bazy”$”nazwa kolumny” (np. skunks$PLEC).

> skunks
    PLEC  SEZON CIEZAR     F
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks$PLEC
 [1] samica samica samiec samica samica
Levels: samica samiec

 

Edycja baz danych

Zmiany wartości komórek w bazach danych wykonuje się przez zwykłe przypisanie określonej komórce nowej wartości.

> skunks
    PLEC  SEZON CIEZAR     F
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks[1,4]=20.0
> skunks[[4]][2]=300
> skunks$FLUOR[4]=400
> skunks
    PLEC  SEZON CIEZAR     F
1 samica jesień    2.7 20.00
2 samica jesień    4.1 30.00
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 40.00

 

W bazach danych kolumny z danymi tekstowymi są obiektami o typie czynnikowym (factor). Mają one z góry ustaloną liczbę wartości, co powoduje niemożność edycji związanej z wprowadzaniem nowych. Gdy zaistnieje potrzeba wprowadzenia nowej wartości trzeba usupełnić o nią wektor tekstowy levels(nazwa_bazy$nazwa_czynnika).

> skunks
    PLEC  SEZON CIEZAR     F
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks$PLEC[1]="samica ciezarna"
Komunikat ostrzegawczy:
In `[<-.factor`(`*tmp*`, 2, value = c(2L, NA, 2L, 1L, 1L)) :
invalid factor level, NA generated
> skunks
    PLEC  SEZON CIEZAR     F
1   <NA> jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> levels(skunks$PLEC)=c(levels(skunks$PLEC),"samica ciezarna")
> skunks$PLEC[1]="samica ciezarna"
> skunks
            PLEC  SEZON CIEZAR FLUOR
1 samica ciezarna jesień    2.7 26.5.1
2          samica jesień    4.1 26.06
3          samiec   zima    3.6 36.02
4          samica wiosna    3.0 24.78
5          samica jesień    4.5 25.97

 

Wiele osób lubi modyfikować bazy danych wtedy gdy je widzi, to znaczy ogląda ich wartości w tabeli, a jednocześnie może je zmieniać. R umożliwia to za pomocą funkcji fix(). Baza danych wyświetla się w następującej postaci:

Poprzez dwukrotne kliknięcie na poszczególne pola tej tabeli można dokonać zmian wartości zmiennych. Zmiany są pamiętane od razu, bez potrzeby ich potwierdzenia. Jednocześnie nie ma możliwości powrotu do poprzednich wartości. Po naciśnięciu na × w prawym górnym rogu wracamy do konsoli R. Nowe wartości już tkwią w obiekcie. W tabeli, którą pokazuje funkcja fix(), można wprowadzać nowe wartości do zmiennych czynnikowych. Po zamknięciu tej tabeli automatycznie przypisane one zostaną czynnikom, choć pojawi się przy tym komunikat:

Komunikat ostrzegawczy:
W poleceniu ‘edit.data.frame(get(subx, envir = parent), title = subx, …)’:
dodane poziomy czynnika w ‘nazwa zmiennej’

Łączenie baz danych

Łączenie tabel można rozpatrywać jako dołączanie kolumn z jednej tabeli do drugiej (łączenie poziome) lub dołączanie wierszy do tabeli (łączenie pionowe). W pierwszym wypadku dołączamy kolumny o takiej samej długości jak liczba wierszy w pierwszej tabeli. Przy łączeniu pionowym poszczególne bazy danych muszą zgadzać się liczbą, typami, a także nazwami poszczególnych kolumn, choć mogą mieć różne liczby wierszy. Jest jeszcze przypadek dołączania dodatkowych informacji do poszczególnych wierszy w pierwszej bazie, które są zawarte w drugiej bazie. Obie bazy muszą posiadać kolumnę o tej samej nazwie i wartościami, która stanowić będzie klucz do kolejności dołączania nowych informacji w dodatkowych kolumnach. Jest to łączenia baz danych wg klucza.

Pionowe łączenie baz danych

Dodanie nowej kolumny odbywa się najczęściej poprzez funkcję cbind(). Dodawany wektor lub czynnik powinien mieć taką samą długość jak liczba wierszy w bazie danych.

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> cbind(Nr=1:7,skunks)
Błąd w poleceniu 'data.frame(..., check.names = FALSE)':
argumenty sugerują różną liczbę wierszy: 7, 5
> cbind(Nr=1:4,skunks)
Błąd w poleceniu 'data.frame(..., check.names = FALSE)':
argumenty sugerują różną liczbę wierszy: 4, 5
> skunks=cbind(Nr=1:5,skunks)
> skunks
> skunks
 Nr   PLEC  SEZON CIEZAR FLUOR
1 1 samica jesień    2.7 26.51
2 2 samica jesień    4.1 26.06
3 3 samiec   zima    3.6 36.02
4 4 samica wiosna    3.0 24.78
5 5 samica jesień    4.5 25.97

 

Funcja cbind() umożliwia także poziome łączenie baz danych, o ile mają one taką samą liczbę wierszy.

Usunięcie kolumny odbywa się poprzez odwołanie się do części, które chcemy zostawić i przypisanie ich nowemu obiektowi. Generalnie obiektowi temu daje się nową nazwę, bo do usuniętych danych, nie zapamiętanych pod inną nazwą, nie można już powrócić.

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks1=skunks[,1:3]
> skunks1
    PLEC  SEZON CIEZAR
1 samica jesień    2.7
2 samica jesień    4.1
3 samiec   zima    3.6
4 samica wiosna    3.0
5 samica jesień    4.5
> skunks2=skunks[,-4]
> skunks2
    PLEC  SEZON CIEZAR
1 samica jesień    2.7
2 samica jesień    4.1
3 samiec   zima    3.6
4 samica wiosna    3.0
5 samica jesień    4.5

 

Poziome łączenie baz danych

Do poziomego łączenia różnych baz danych, ale mających przynajmniej po jednej kolumnie opisującej te same obiekty biologiczne pod tymi samymi nazwami, w taki sposób, by w wyniku uzyskać bazę danych

Do dodawania nowych wierszy służy funkcja rbind(). Dodawany wiersz powinien być jednolinijkowym obiektem data.frame, albo listą o typach poszczególnych elementów takich samych jak typy kolumn wyjściowej bazy danych. Dołączanie danych w postaci wektora z reguły generuje błędy.

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks1=rbind(skunks,1:4)
> skunks1
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
6   <NA>   <NA>    3.0 40
Komunikaty ostrzegawcze:
1: W poleceniu '`[<-.factor`(`*tmp*`, ri, value = 1L)':
niepoprawny poziom czynnika, wygenerowano wartość NA
2: W poleceniu '`[<-.factor`(`*tmp*`, ri, value = 2L)':
niepoprawny poziom czynnika, wygenerowano wartość NA
> skunks2=rbind(skunks,c("samiec","wiosna",3,40))
> skunks2
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
6 samiec wiosna      3 40
> str(skunks2)
'data.frame':   5 obs. of  4 variables:
$ PLEC  : Factor w/ 2 levels "samica","samiec": 1 1 2 1 1 2
$ SEZON : Factor w/ 3 levels "jesień","wiosna",..: 1 1 3 2 1 2
$ CIEZAR: chr "2.7" "4.1" "3.6" "3" ...
$ FLUOR : chr "26.51" "26.06" "36.02" "24.78" ...
> skunks3=rbind(skunks,list("samiec","wiosna",3,400))
> skunks3
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
6 samiec wiosna    3.0 40.00
> str(skunks2)
'data.frame':   5 obs. of  4 variables:
$ PLEC  : Factor w/ 2 levels "samica","samiec": 1 1 2 1 1 2
$ SEZON : Factor w/ 3 levels "jesień","wiosna",..: 1 1 3 2 1 2
$ CIEZAR: num 2.7 4.1 3.6 3 4.5 3
$ FLUOR : num 26.51 26.06 36.02 24.78 25.97 ...

 

Dołączane wektory, to elementy jednego typu. Gdy występują w nich teksty i liczby, wszystkie elementy traktowane są jako dane tekstowe. Dołączenie choć jednego elementu tekstowego do danych numerycznych zmienia typ całej kolumny.

Funkcja rbind() służy także do pionowego łączenia baz danych mających takie same liczby i nazwy kolumn. Jednak gdy dołącza się kolumnę tekstową do kolumny numerycznej całość staje się czynnikiem o wartościach tekstowych.

> skunks1
    PLEC  SEZON CIEZAR
1 samica jesień    2.7
2 samica jesień    4.1
3 samiec   zima    3.6
4 samica wiosna    3.0
5 samica jesień    4.5
> skunks2
    PLEC  SEZON FLUOR
1 samica jesień    26.51
2 samica jesień    26.06
3 samiec   zima    36.02
4 samica wiosna    24.78
5 samica jesień    25.97
> rbind(skunks1,skunks2)
Błąd w poleceniu 'match.names(clabs, names(xi))':
nazwy nie zgadzają się z poprzednimi nazwami
> names(skunks2)[3]="CIEZAR"
> skunks2
    PLEC  SEZON CIEZAR
1 samica jesień    26.51
2 samica jesień    26.06
3 samiec   zima    36.02
4 samica wiosna    24.78
5 samica jesień    25.97
> rbind(skunks1,skunks2)
     PLEC  SEZON CIEZAR
1  samica jesień    2.7
2  samica jesień    4.1
3  samiec   zima    3.6
4  samica wiosna    3.0
5  samica jesień    4.5
6  samica jesień  26.51
7  samica jesień  26.06
8  samiec   zima  36.02
9  samica wiosna  24.78
10 samica jesień  25.97

 

Łączenie baz danych wg klucza

Dość często wartościom pewnych czynników odpowiadają różne charakterystyki ułożone w inne bazy danych. Gdybyśmy chcieli je dołączyć do naszej bazy danych musielibyśmy to zrobić tak, by charakterystyki pasowały do kolejnych obiektów biologicznych, a więc dla danego wiersza z pierwszej bazy wybierany powinien być ten wiersz z drugiej bazy, który ma odpowiednią wartość charakteryzowanego czynnika. Służy do tego funkcja merge() z opcją by=”nazwa czynnika”.

> skunks
    PLEC  SEZON CIEZAR     F
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> pogoda
   SEZON TEMP OPAD
1 wiosna 17.3   23
2 jesień 18.2   43
3   zima  4.5   53
> merge(skunks, pogoda, by="SEZON")
   SEZON   PLEC CIEZAR FLUOR TEMP OPAD
1 jesień samica    2.7 26.51 18.2   43
2 jesień samica    4.1 260.6 18.2   43
3 jesień samica    4.5 259.7 18.2   43
4 wiosna samica    3.0 247.8 17.3   23
5   zima samiec    3.6 360.2  4.5   53

 

W R nie ma możliwości tworzenia relacji wg. klucza między poszczególnymi bazami danych, ale dla większości zastosowań ich łączenie wg. klucza jest zupełnie wystarczające w badaniach biologicznych. Problem mogą sprawiać tylko bardzo długie bazy danych, bo po ich połączeniu powstaje nowa ogromna baza danych zajmująca bardzo dużo miejsca i pamięci. W takich przypadkach lepiej jest zrezygnować z R i pracować w odpowiednich programach typu Access lub bazy danych w Libre Office.

Filtrowanie baz danych

zobaczenie wybranych wierszy możliwe jest przez ich wskazanie: nowa.nazwa.bazy=nazwa.bazy[c(1,3:5),] lub nowa.nazwa.bazy=nazwa.bazy[-2,]. W praktyce jednak najczęściej usuwa się wiersze dla których wartość wybranej zmiennej nie odpowiada określonym kryteriom. Zarówno wybór wierszy jak i usunięcie według kryterium logicznego można zrealizować funkcją subset(nazwa.bazy, kryterium)

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks1=subset(skunks,FLUOR>26.0)
> skunks1
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02

 

Standardowo do podbazy danych przechodzą wszystkie kolumny. Czasami trzeba wybrać ich tylko kilka i do tego celu służy opcja select=c(nazwa.kolumny1,nazwa.kolumny2,…). Zastosowanie znaku – przed ciągiem nazw zmiennych w opcji select powoduje, że wymienione w nim kolumny zostaną usunięte.

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks1=subset(skunks,FLUOR>26,select=c(CIEZAR,FLUOR))
> skunks1
  CIEZAR FLUOR
1    2.7 26.5.1
2    4.1 260.6
3    3.6 360.2
> skunks2=subset(skunks,FLUOR>26,select=-c(PLEC,SEZON))
> skunks2
  CIEZAR FLUOR
1    2.7 26.51
2    4.1 26.06
3    3.6 36.02

 

Sortowanie baz danych

Zobaczenie wierszy w innej kolejności, niż jest to określone w bazie danych możliwe jest przez użycie formuły baza[c(3,2,5,4,…),], gdzie ciąg widoczny wewnątrz jest pewną permutacją liczb 1:n, gdzie n jest liczbą wierszy w bazie. Z rozdziału o wektorach i czynnikach wiadomo, że funkcja order() pokazuje ciąg liczb oznaczających miejsca wartości ciągu lub czynnika uporządkowanych liczbowo albo alfabetycznie. oznacza to, że baza[order(nazwa.kolumny),] pokaże się, jako posortowana względem wartości wybranej kolumny.

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks[c(3,5,1,4,2),]
    PLEC  SEZON CIEZAR FLUOR
3 samiec   zima    3.6 36.02
5 samica jesień    4.5 25.97
1 samica jesień    2.7 26.51
4 samica wiosna    3.0 24.78
2 samica jesień    4.1 26.06
> skunks[order(skunks$PLEC),]
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
3 samiec   zima    3.6 36.02

 

Funkcja order może mieć wiele argumentów. Najpierw sortuje dane względem pierwszej wskazanej zmiennej (najczęściej czynnika), potem w obrębie tych samych wartości pierwszej zmiennej sortuje dane względem drugiej zmiennej itd. Ostatecznie tworzy ciąg liczbowy odpowiadający pozycjom wierszy w pierwotnej bazie danych jakie mają wiersze po wykonanych sortowaniach. Można to wykorzystać by uzyskać bazę posortowana według dowolnie przyjętych kryteriów.

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> skunks[order(skunks$PLEC, skunks$SEZON, skunks$CIEZAR), ]
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
5 samica jesień    4.5 25.97
4 samica wiosna    3.0 24.78
3 samiec   zima    3.6 36.02

 

Najczęściej po sortowaniu zapamiętuje się wynik w postaci obiektu w R, często o takiej samej nazwie jak obiekt przed sortowaniem, np.: skunks=skunks[order(skunks$PLEC, skunks$SEZON, skunks$CIEZAR), ].

Funkcje i działania na bazach danych

Przy pracy nad bazami danych często należy sobie przypominać nazwy kolumn (czy zostały napisane dużymi, czy małymi literami, z kropkami czy podkreśleniami itp.). Gdy baza jest duża nazw tych możemy długo szukać poprzez jej wyświetlanie. Przy szukaniu tych nazw kolumn przydatnymi funkcjami są, names(), str() oraz summary().

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> names(skunks)
[1] "PLEC" "SEZON" "CIEZAR" "FLUOR"
> str(skunks)
'data.frame':   5 obs. of  4 variables:
$ PLEC  : Factor w/ 2 levels "samica","samiec": 1 1 2 1 1
$ SEZON : Factor w/ 3 levels "jesień","wiosna/lato",..: 1 1 3 2 1
$ CIEZAR: num 2.7 4.1 3.6 3 4
$ FLUOR     : num 26.51 26.06 36.02 24.78 25.97
> summary(skunks)
     PLEC      SEZON       CIEZAR         FLUOR
samica:4   jesień:3   Min.   :2.70   Min.   :24.78
samiec:1   wiosna:1   1st Qu.:3.00   1st Qu.:25.97
zima  :1   Median :3.60   Median :26.06
Mean   :3.58   Mean   :27.87
3rd Qu.:4.10   3rd Qu.:26.5.1
Max.   :4.50   Max.   :36.02

 

Dwie ostatnie funkcje pokazują zarówno nazwy zmiennych jak i nazwy wartości zmiennych czynnikowych, co może być bardzo przydatne.

Liczbę wierszy i kolumn bazy danych obliczymy za pomocą funkcji nrow(), ncol() i length() (dwie ostatnie funkcje wyznaczają liczbę kolumn).

> skunks
    PLEC  SEZON CIEZAR FLUOR
1 samica jesień    2.7 26.51
2 samica jesień    4.1 26.06
3 samiec   zima    3.6 36.02
4 samica wiosna    3.0 24.78
5 samica jesień    4.5 25.97
> nrow(skunks)
[1] 5
> ncol(skunks)
[1] 4
> length(skunks)
[1] 4

 

Generalnie bazy danych służą do opracowanej prezentacji wyników. Składają się na nie tabele z liczbami obiektów o różnych wartościach zmiennych dyskretnych, tabele z średnimi i odchyleniami standardowymi zmiennych ciągłych dla różnych wartości zmiennych dyskretnych, korelacje między różnymi wartościami zmiennych ciągłych i wykresy obrazujące te zależności. Czasem wykonuje się też wykresy rozkładów z próby zmiennych ciągłych (tzw. histogramy), co stanowi przybliżenie rozkładu, jaki tworzy zmienna dla wszystkich obiektów populacji i co tak naprawdę jest przedmiotem badań biologów. Na koniec sprawdza się, czy zaobserwowane zależności są istotne statystycznie, co oznacza sprawdzenie, czy zależności zaobserwowane dla prób przenoszą się na całe rozkłady.

opracowanie graficzne wyników i praktycznie wszystkie testy statystyczne. Trzeba się tylko nauczyć jak to zrobić, by były one wykonane poprawnie. Graficznemu opracowaniu danych poświęcona jest część III niniejszego podręcznika, a statystyce – część IV. W tym rozdziale podamy tylko podstawowe informacje jak zastosować funkcje matematyczne i statystyczne dla danych zapisanych w postaci data.frame. Można to poćwiczyć na całej bazie skunks, która można uzyskać po skopiowaniu do R następującego kodu:

skunks=data.frame(PLEC=c("samica", "samica", "samiec", "samica", 
 "samica", "samiec", "samiec", "samiec", "samiec", "samica", "samica",
 "samiec","samiec",  "samica", "samiec", "samiec", "samiec", "samica", 
 "samiec", "samica", "samiec",  "samiec", "samica", "samica", "samica",
 "samiec","samiec", "samica", "samica",  "samica", "samiec", "samica",
 "samiec", "samiec", "samiec", "samica", "samiec",  "samica", "samica",
 "samica", "samiec", "samiec", "samiec", "samica", "samiec",  "samiec",
 "samiec", "samica", "samiec", "samiec"),
SEZON=c("jesień", "jesień", "zima", "wiosna", "jesień", "zima", "wiosna",
 "zima",  "wiosna", "zima", "zima", "wiosna", "jesień", "jesień", "wiosna",
 "jesień", "wiosna", "zima", "jesień", "zima", "jesień", "zima", "zima",
 "wiosna", "zima", "zima", "wiosna", "wiosna", "zima", "zima", "zima",
 "jesień", "wiosna", "wiosna", "zima", "wiosna", "zima", "wiosna",
 "jesień", "zima", "wiosna", "jesień", "zima", "jesień", "wiosna",
 "jesień", "wiosna", "wiosna", "zima", "wiosna"),
CIEZAR=c(2.7, 4.1, 3.6, 3,4, 3.4, 2.6, 3.2, 3.6, 3.4, 4.2, 3.4, 4.8, 3.1, 3.7, 
3.5,  4.1, 3.3, 2.9, 3.1, 4.2, 4.1, 3, 3.5, 4.4, 3.9, 4.8, 4, 4.5, 4, 4.5, 3.9,
 3.6, 3.5,  3.5, 2.4, 4.2, 3.8, 3.3, 3.9, 3.3, 3.5, 3.8, 3.7, 4.4, 3.9, 3.8, 3, 
4.5, 4.4),
FLUOR=c(26.24, 24.69, 35.30, 25.45, 24.30, 38.59, 31.45, 37.14, 29.18, 33.32,
 29.06, 30.40, 36.28, 28.14, 29.18, 30.81, 28.70, 30.74, 35.08, 26.74, 30.31, 
 33.59, 28.61, 25.28, 27.29, 35.56, 28.63, 22.53, 31.49, 29.72, 36.43, 26.39, 
 29.12, 29.31, 36.54,  23.50, 34.36, 24.87, 27.83, 31.18, 28.96, 32.34, 38.56, 
 29.20, 31.31, 30.08, 31.96, 23.94, 37.59, 27.20))

 

Tabele z liczbą zbadanych osobników w dla każdej wartości zmiennych dyskretnych uzyskuje się za pomocą funkcji table(). Jest to funkcja działająca na argumentach różnego typu dająca wyniki, których najczęściej się oczekuje (choć nie zawsze).

> table(skunks$PLEC)


samica samiec
22     28 
> table(skunks$PLEC,skunks$SEZON)


jesień wiosna zima
samica      7      6    9
samiec      6     12   10
> table(skunks$PLEC, skunks$SEZON, skunks$CIEZAR>3.5)
, ,  = FALSE


jesień wiosna zima
samica      3      4    4
samiec      3      4    3

, ,  = TRUE


jesień wiosna zima
samica      4      2    5
samiec      3      8    7

 

Sposób wyświetlania się wyników zależy od kolejności wpisywanych zmiennych. Trzeba tym manipulować, by uzyskać taki wynik, jaki pasuje do zaplanowanej w opracowaniu tabeli. Po uzyskaniu odpowiedniej tabeli należy jej kod przypisać jakiemuś obiektowi. Trzeba w konsoli wykonać instrukcję postaci:

tab1 = table(skunks$PLEC,skunks$SEZON)W przypadku, gdy porządek alfabetyczny dla zmiennych nieliczbowych jest sprzeczny z naturalnym porządkiem określonym np. przez następstwo czasowe lub przestrzenne, można nadać im własny porządek pamiętany później do końca sesji. Wykonuje się to funkcją ordered() opisaną już w rozdziale o czynnikach.

> table(skunks$PLEC,skunks$SEZON)


jesień wiosna zima
samica      7      6    9
samiec      6     12   10
> skunks$SEZON=ordered(skunks$SEZON, levels=c("wiosna", "jesień", "zima"))
> table(skunks$PLEC,skunks$SEZON)


wiosna jesień zima
samica      6      7    9
samiec     12      6   10

 

Funkcją, która błyskawicznie umożliwia wyliczenie średnich i odchyleń standardowych (a także innych charakterystyk rozkładu) w R jest aggregate() o składni:

aggregate(zmienna_ciągła ~ zmienna_dyskretna, nazwabazy, “nazwa funkcji”)

Nazwa funkcji to najczęściej sum, mean, sd i inne funkcje przetwarzające wektory. Można je zapisać za pomocą nazw, albo w postaci: function(x) sum(x),function(x) mean(x), function(x) sd(x) lub jakakolwiek inna funkcja. Ta postać pozwala na używanie niestandardowych funkcji i wyliczenie kilku charakterystyk jednocześnie. Zwracamy w nich uwagę na to, z jaką dokładnością ukazują się wyliczane średnie i odchylenia standardowe, gdyż musi być to taka sama dokładność, z jaką dokonywane były pomiary.

> skunks$SEZON=ordered(skunks$SEZON, levels=c("wiosna/lato", "jesień", "zima"))
> aggregate(CIEZAR ~ SEZON+PLEC, skunks, mean)
   SEZON   PLEC   CIEZAR
1 wiosna samica 3.283333
2 jesień samica 3.542857
3 zima samica 3.755556
4 wiosna samiec 3.766667
5 jesień samiec 3.800000
6 zima samiec 3.870000
> aggregate(CIEZAR ~ SEZON+PLEC, skunks, function(x) c(mean(x),sd(x)))
   SEZON   PLEC  CIEZAR.1  CIEZAR.2
1 wiosna samica 3.2833333 0.5946988
2 jesień samica 3.5428571 0.5223573
3   zima samica 3.7555556 0.5681354
4 wiosna samiec 3.7666667 0.5898125
5 jesień samiec 3.8000000 0.6572671
6   zima samiec 3.8700000 0.4522782
> aggregate(CIEZAR ~ SEZON+PLEC, skunks,
+ function(x) paste(mean(x),"+",sd(x)))
   SEZON   PLEC                               CIEZAR
1 wiosna samica 3.28333333333333 + 0.594698803316996
2 jesień samica  3.54285714285714 + 0.52235729425092
3   zima samica  3.75555555555556 + 0.56813535163531
4 wiosna samiec 3.76666666666667 + 0.589812502307969
5 jesień samiec              3.8 + 0.657267069006199
6   zima samiec              3.87 + 0.45227818381562
> aggregate(CIEZAR ~ SEZON+PLEC, skunks,
+ function(x) paste(round(mean(x),1),"+",round(sd(x),1)))
   SEZON   PLEC    CIEZAR
1 wiosna samica 3.3 + 0.6
2 jesień samica 3.5 + 0.5
3   zima samica 3.8 + 0.6
4 wiosna samiec 3.8 + 0.6
5 jesień samiec 3.8 + 0.7
6   zima samiec 3.9 + 0.5

 

Najbardziej jednak chcielibyśmy utworzyć tabelę postaci:

Można zrobić tylko tekstową namiastkę takiej tabeli bez linii pionowych i poziomych posługując się znanymi już typami obiektów i funkcjami.

> skunks$SEZON=ordered(skunks$SEZON, levels=c("wiosna", "jesień", "zima"))
> agg=aggregate(CIEZAR ~ SEZON+PLEC, skunks,
+ function(x) paste(round(mean(x),1),intToUtf8(177),round(sd(x),1)))
> wyn=agg[,3]
> mac=matrix(wyn,3,2,dimnames=list(c("wiosna","jesień","zima"),c("samice","samce")))
> mac
       samice      samce
wiosna "3.3 ± 0.6" "3.8 ± 0.6"
jesień "3.5 ± 0.5" "3.8 ± 0.7"
zima   "3.8 ± 0.6" "3.9 ± 0.5"
> print(mac,quote=FALSE)
       samice    samce
wiosna 3.3 ± 0.6 3.8 ± 0.6
jesień 3.5 ± 0.5 3.8 ± 0.7
zima   3.8 ± 0.6 3.9 ± 0.5

 

Tabele spełniającą wyznaczone kryteria należy zapamiętać jako obiekt R, czyli wykonać instrukcje:

Tab2 = aggregate(CIEZAR ~ SEZON+PLEC, skunks, ....... )
Tab3 = aggregate(FLUOR ~ SEZON+PLEC, skunks, ....... )

albo

wyn2 = aggregate(CIEZAR ~ SEZON+PLEC, skunks, ....... )[3,]
Tab2 = matrix(wyn2,3,2,dimnames=list(
c("wiosna","jesień","zima"),c("samice","samce")))
wyn3 = aggregate(FLUOR ~ SEZON+PLEC, skunks, ....... )[3,]
Tab3 = matrix(wyn3,3,2,dimnames=list(
c("wiosna","jesień","zima"),c("samice","samce")))

Najpierw, oczywiście należy powyższe instrukcje dokończyć. Po uzyskaniu stosownych obiektów w R, należy je zapamiętać w postaci własnego skrótu do R wykonując instrukcję

save.image("Własna Nazwa.RData")

.

W pokazanych procedurach chodzi o wyświetlenie wyników w takiej postaci tekstowej, by po przeniesieniu ich do edytora tekstów (gdzie opisuje się wyniki) wymagały one minimalnej przeróbki. W konsoli R nie można tworzyć takich tabel, jakie znamy z publikowanych opracowań. Tabele do publikacji tworzy się w edytorach tekstu i dane uzyskane w R kopiuje się do edytora, w którym poddaje się je standardowej obróbce. Przy takim planie pracy najlepiej zrezygnować z używania znaku ± w R, i zamienić + dopiero edytorze tekstu.

Eksportowanie tabel

Wyniki wielu obliczeń są obiektami typu data.frame() lub matrix(). Ich obraz wyświetlany w konsoli i przeniesiony do edytora tekstu nie tworzy tabeli. Aby nie męczyć się z tworzeniem tabeli i wstawianiem w odpowiednie komórki wyników należy:

  1. eksportować tabelę do pliku tekstowego,
  2. otworzyć plik tekstowy w arkuszu kalkulacyjnym w postaci tabeli,
  3. skopiować tabelę do edytora tekstu,

Eksport do pliku tekstowego tabeli lub macierzy wykonuje się za pomocą funkcji write.table().

Przykładem bazy danych mogą być badania stężenia fluoru w wydzielinie gruczołów odbytowych skunksów. Można wykonać trzy tabele, które będą potrzebne do wykonania projektu pracy dyplomowej:

> skunks$SEZON=ordered(skunks$SEZON, levels=c("wiosna", "jesień", "zima"))
> tab_policz=table(skunks$PLEC,skunks$SEZON)
> tab_ciezar=aggregate(CIEZAR~PLEC+SEZON,skunks,
+   function(x) paste(round(mean(x),1),"+",round(sd(x),1)))
> tab_fluor=aggregate(FLUOR~PLEC+SEZON,skunks,
+   function(x) paste(round(mean(x),2),"+",round(sd(x),2)))
> write.table(tab_policz,"tab1.txt",sep="\t")
> write.table(tab_ciezar,"tab2.txt",sep="\t")
> write.table(tab_fluor,"tab3.txt",sep="\t")

 

Efektem wykonania tego programu są następujące pliki tekstowe:

Pliki te można wczytać do Excela metodą:

Otworzyć jakikolwiek plik Excela (może być nowy)

  • W Excelu kliknąć na otwórz
  • Określić folder na ten, gdzie zapisane zostały pliki tab1.txt, tab2.txt, tab3.txt
  • Zmienić opcję “wszystkie pliki programu excel” na “Pliki tekstowe .prn, .txt, .csv”
  • Powinny się pokazać pliki tab1.txt, tab2.txt i tab3.txt.
  • Kliknąć na importowany plik i “Otwórz”
  • Pozostawić “Rozdzielony”, kliknąć na “Dalej”
  • Pozostawić odhaczony tabulator, lub go odhaczyć i kliknąć “Dalej”
  • Kliknąć “Zakończ”

Jedynymi poprawkami, jakie trzeba będzie wykonać – to przesunąć nazwy kolumn o jedno pole w prawo. Zamianę kropek w liczbach na przecinki także jest lepiej wykonać w Excelu, gdyż można wtedy zaznaczyć wyłącznie pola liczbowe i nie dokonywać tych zmian w nazwach wierszy.

W arkuszu kalkulacyjnym libre Office należy wybrać otwórz, zmienić katalog na właściwy i wybrać odpowiedni plik tekstowy. W polu które się pojawi, musi być odhaczony tabulator. Na ogół od razu importowana tabela wygląda dobrze. Należy tylko przesunąć nazw kolumn w prawo o jedno pole, co robi się już po zaimportowaniu tabeli do arkusza kalkulacyjnego. Również zamianę kropek na przecinki w liczbach najlepiej wykonać na końcu zaznaczając wyłącznie pola liczbowe.

Często tabele zaimportowane do arkusza kalkulacyjnego przetwarza się na prostsze formy mające charakter tabeli krzyżowej. Często też łączy się ze sobą komórki z tą sama wartością tekstową, gdy występują obok siebie lub jedna pod drugą. Tabele przenoszone z arkusza kalkulacyjnego do edytora tekstu zachowują format tabeli. Dopiero w edytorze tekstu zamienia się znak “+” na “±”, gdyż podczas eksportowania, importowania lub przenoszenia tabeli metodą kopiuj-wklej znaki “±” może zostać zamieniony na inny znak często nie występujący na klawiaturze i czasem taki, który trudno automatycznie zmienić na “±”.

Koniec części I

 

 

Spis treści