Wstęp


R is the lingua franca of statistical research. Work in all other languages should be discouraged.
– Jan de Leeuw (as quoted by Matt Pocernich on R-help)
JSM 2003, San Francisco (August 2003)

fortune(78)





Identyfikacja wizualna

Logo projektu R

“https://cran.r-project.org/”


R to sam język programowania i środowisko do obliczeń statystycznych.

Język R powstał na początku lat 90. XX wieku w Nowej Zelandii, na Uniwersytecie w Auckland. Jego twórcami byli Ross Ihaka i Robert Gentleman - stąd nazwa “R” pochodzi od pierwszych liter ich imion.

R został stworzony jako darmowa implementacja komercyjnego języka S, który powstał w Bell Laboratories w latach 70. Ihaka i Gentleman chcieli stworzyć narzędzie do nauczania statystyki, które byłoby dostępne dla studentów bez kosztów licencji.

R został po raz pierwszy udostępniony publicznie w 1993 roku. W 1995 roku Martin Mächler z ETH Zurich przekonał twórców do udostępnienia R na licencji GNU GPL, co uczyniło go prawdziwie open source’owym projektem.

W 1997 roku utworzono R Core Team - grupę deweloperów odpowiedzialnych za rozwój języka. To właśnie ta grupa kontroluje kod źródłowy R do dzisiaj (https://cran.r-project.org/).

Wersja 1.0.0: Pierwsza stabilna wersja R 1.0.0 została wydana 29 lutego 2000 roku - symboliczna data w “przestępnym” roku.




Stare środowisko graficzne R (Rgui.exe)

“Konsola ‘gołego’ R”






Logo projektu R GUI





RStudio (GUI)

“Środowisko graficzne”


RStudio to znacznie późniejszy projekt - to zintegrowane środowisko programistyczne (IDE), które ułatwia pracę z językiem R. RStudio powstało dopiero w 2011 roku, założone przez JJ Allaire.

RStudio to:

  • Wygodny edytor kodu z podświetlaniem składni

  • Panel z wizualizacją danych i wykresów

  • Zarządzanie projektami

  • Integrację z systemami kontroli wersji (Git)

  • Podgląd zmiennych i obiektów w pamięci

  • i inne

W 2022 roku firma RStudio zmieniła nazwę na Posit, by lepiej odzwierciedlić fakt, że rozwija narzędzia nie tylko dla R, ale też dla Pythona i innych języków. Samo oprogramowanie RStudio Desktop nadal nosi tę nazwę.

Więc krótko: R ma ~30 lat, RStudio ~13 lat - to narzędzie powstałe dla już istniejącego języka.




Instalacja oprogramowania

Krótko omówimy teraz na zajęciach, dla nieskupionych (nieobecnych) materiał wideo (po polsku):

lub



Wersja oprogramowania

Sprawdźmy wersję R, którą mamy aktualnie zainstalowaną na naszym komputerze.

version
##                _                                
## platform       x86_64-w64-mingw32               
## arch           x86_64                           
## os             mingw32                          
## crt            ucrt                             
## system         x86_64, mingw32                  
## status                                          
## major          4                                
## minor          4.3                              
## year           2025                             
## month          02                               
## day            28                               
## svn rev        87843                            
## language       R                                
## version.string R version 4.4.3 (2025-02-28 ucrt)
## nickname       Trophy Case


Polecenie version w R dostarcza szczegółowych informacji o wersji R, którą aktualnie używamy, określa platformę sprzętową i system operacyjny, podaje informacje o dacie i czasie kompilacji R, wersji kompilatora, etc. Wszystko to może być ważne z punktu widzenia pracy kolektywnej na jednym projekcie (wersja R i jego bibliotek).


Uwaga co do instalowania nowych wersji R. Ogólnie warto. Mniej więcej co roku, w kwietniu, wypuszczana jest duża aktualizacja. W ciągu roku również pojawiają się pomniejsze aktualizacje. Samo zainstalowanie nowej wersji R nie powinno psuć nam kodu, który działał w wersjach wcześniejszych (konserwatywni deweloperzy). Powinna za to poprawić się wydajność.

Jednym z mankamentów R w porównaniu do innych języków jest jego powolność. Jest jednak piekielnie wygodny przy konceptualizacji i pisaniu kodu. Powszechny dowcip mówi, że “gdy programista R ‘kompiluje’ swój kod, programista C szuka na StackOverflow (teraz pyta ChatGPT) jak wczytać plik csv”.

Biblioteki (pakiety)

options(repos = c(CRAN = "https://cloud.r-project.org")) # <- funkcja available.packages() wymaga określenia lustra CRAN (Comprehensive R Archive Network), z którego mają być pobierane informacje o dostępnych pakietach.

paste0("Aktualna liczba pakietów na dzień kompilacji poniższego dokumentu (", Sys.Date(), ") html to: ",
       nrow(available.packages())
       )
## [1] "Aktualna liczba pakietów na dzień kompilacji poniższego dokumentu (2025-10-16) html to: 22827"


Jeśli w przyszłości sami będziemy tworzyć pakiety poniżej przydatny pakiet do sprawdzania:

  • czy ta nazwa jest już zajęta na CRAN, Bioconductor, GitHub etc.,

  • czy nazwa nie jest zastrzeżonym znakiem towarowym,

  • czy nie ma niepożądanych skojarzeń językowych (np. wulgaryzmy), etc.

install.packages("avaliable")
library(available)
available("Moja_nazwa_pakietu")


Na stronie projektu można sprawdzać pakiety “manualnie”:

https://cran.r-project.org/web/packages/available_packages_by_date.html

Co może jednakowoż psuć nam kod, to aktualizacja paczek, których używamy. Pracując na projektach, zazwyczaj “mrozi się” wersje paczek i pilnuje poszczególnych użytkowników, żeby pracowali na spójnych co do wersji paczkach (czytaj: odbiera im się możliwość samodzielnych aktualizacji).

Pakietem R, który służy do zarządzania wersjami pakietów jest np. packrat. Packrat umożliwia izolowanie projektów R od zewnętrznych zależności, tak aby w przypadku aktualizacji pakietów, projekt pozostał stabilny. Packrat zapewnia również narzędzia do zarządzania bibliotekami i łatwe przełączanie między różnymi wersjami pakietów. Ale to póki co tytułem dygresji i żeby było w notatkach.


Instalacja pakietów

  • Aby zainstalować nowy pakiet można ściągnąć odpowiedni plik ze strony https://cran.r-project.org/web/packages/ i zainstalować go ręcznie

  • Wygodniej jest wykonać polecenie: install.packages("nazwa_pakietu")

  • Aby załadować zainstalowany pakiet do pamięci w danej sesji R należy wykonać polecenie: library(nazwa_pakietu) albo require(nazwa_pakietu)

  • Aktualizację wszystkich zainstalowanych pakietów można wykonać za pomocą polecenia: update.packages() Aktualizacje bywają niezwykle pożyteczne, i zdradliwe zarazem. Z jednej strony niosą nowe, rozszerzone, możliwości, przyspieszenie, optymalizację (niepotrzebne skreślić), z drugiej mogą wywalić cały projekt do kosza (zmiana argumentów funkcji, użycie innych metod, wygaszenie jakiejś funkcjonalności). Pakiety mają swoje cykle życia.


Zainstalujmy sobie pierwszy pakiet poleceniem install.packages("fortunes"). Uruchommy pakiet poleceniem library(fortunes), a następnie skorzystajmy z dostępnej tam funkcji fortune() (bez żadnego argumentu).

# install.packages("fortunes") # mam już ten pakiet, stąd zakomentowanie linijki

library(fortunes)

fortune()
## 
## You can't expect statistical procedures to rescue you from poor data.
##    -- Berton Gunter (on dealing with missing values in a cluster analysis)
##       R-help (April 2005)



Pomoc w R

  • Możemy użyć silnika wyszukiwarki wbudowanej w RStudio. Default’owo prawe dolne okienko, przycisk Help (albo Pomoc w przypadku polskojęzycznej instalacji soft’u)

  • Poleceniami w konsoli help() lub z użyciem znaku zapytania przed szukaną frazą, pojedynczy ? (równoważny poleceniu help(package = "nazwa_pakietu"))
    bądź podwójny ?? (równoważny poleceniu help.search("nazwa_pakietu")).

help() # wywołanie dokumentacji - pomoc na temat funkcji help

help.start() # Uruchamia przeglądarkę internetową z interfejsem do dokumentacji R. Oferuje dostęp do pełnej dokumentacji, w tym podręczników, zainstalowanych pakietów i innych zasobów.

help(abs) # wywołanie pomocy na temat konkretnej funkcji 

?abs # jak wyżej - równoznaczne

??abs
help.search("abs") # wyszukuje w dokumentacji funkcje związane z danym hasłem, starszy odpowiednik ??

apropos("abs") # funkcja apropos() w R jest używana do wyszukiwania obiektów w środowisku R, które zawierają określony ciąg znaków w swojej nazwie. Jest to bardzo przydatne narzędzie, gdy chcesz znaleźć funkcje, zmienne lub inne obiekty, ale nie pamiętasz ich dokładnych nazw.
  • W RStudio można ustawić kursor na nazwie funkcji i nacisnąć F1

  • Zewnętrzne źródła np. https://rseek.org/ bądź każdy inny silnik



Napiszmy sobie jeszcze nieśmiertelną w każdym języku programowania linijkę kodu…

print("Hello World!")
## [1] "Hello World!"


Uwagi dot. rozpoczęcia pracy i pisania kodów

  • Znacznik > w konsoli R oznacza, że R czeka na nasze polecenia.

  • + w konsoli oznacza niedokończone polecenie.

  • # służy do komentowania

  • Chcąc uruchomić komendę/ linię kodu można postawić kursor w dowolnym miejscu wiersza z kodem, ewentualnie zaznaczyć interesujący nas fragment, który zamierzamy uruchomić, a następnie albo korzystamy ze skrótu klawiszowego Ctrl + Enter, albo myszką klikamy przycisk RUN

  • Pracę warto rozpocząć dwoma poleceniami getwd() oraz setwd(“…”)

  • Wielkość liter ma znaczenie – „A” i „a” oznaczają co innego, R jest case sensitive

  • Poszczególne komendy mogą być rozdzielane albo ENTER’em albo ; (jak w C++)

2 + 6; 5 * 6; 3 - 6
## [1] 8
## [1] 30
## [1] -3
# równoważne z 
2 + 6
## [1] 8
5 * 6
## [1] 30
3 - 6
## [1] -3
  • Spacje nie mają znaczenia (chyba, że sklejają się dwie funkcje czy wyrażenia)

  • Komentarze można wprowadzać po znaku # Nie będą interpretowane od znaku do końca wiersza. Użyteczny skrót: Ctrl + Shift + C

  • W przypadku niekompletnej komendy konsola R informuje znakiem +

5 + 6 - 3 - 6 + 4 + 2 - 
  
  4 + 8 + 3 + 2 + 7
## [1] 24
  • Nazwy obiektów mogą zawierać: litery, cyfry, _ (słownie: znak podkreślenia) i . (słownie: kropka). Nie mogą być samodzielną lub zaczynać się cyfrą, liczbą, bądź znakiem specjalnym


Operatory

  • Arytmetyczne:

    • dodawanie +,

    • odejmowanie -,

    • mnożenie *,

    • dzielenie /,

    • potęgowanie ^ albo **

  • Porównawcze: >, >=, <, <=, ==, !=

  • Logiczne:

    • negacja !,

    • koniunkcja &,

    • alternatywa | (dodatkowo również && oraz ||)

  • Formuła modelu: ~


Wyrażenia

# Dodawanie
5 + 7 
## [1] 12
# Odejmowanie
8 - 5 
## [1] 3
# Mnożenie
4 * 6
## [1] 24
 # Dzielenie
(5 + 5) / 2 
## [1] 5
# Potęgowanie
2^3 # lub 
## [1] 8
2**3
## [1] 8
# Pierwiastkowanie
36^(1/2) # lub przez funkcję sqrt(36) wbudowaną w R
## [1] 6
36^1/2 # pilnować nawiasów
## [1] 18
36^0.5
## [1] 6
# Część całkowita z dzielenia
31%/%6 
## [1] 5
# Modulo
31%%6
## [1] 1
# działania rozbudowane
2^3+4*(16-10)
## [1] 32
3 ^ 2 - 1 + 3 ** (2 - 1) # ze spacjami być może czytelniej, nie wpływają na ewaluację wyrażenia
## [1] 11
5 + 6 - 3 - 6 + 4 + 2 - 
  4 + 8 + 3 + 2 + 7 # jak odpalić takie 'złamane' na dwie linijki działanie?
## [1] 24


Przypisanie i zmienna

# skalary, wartości 'atomowe'
x <- 666
print(x)
## [1] 666
rm(x) # żeby usunąć ze środowiska i udowodnić, że zadziała
666 -> x
print(x)
## [1] 666
x = 666
print(x)
## [1] 666
# jeśli tworzony obiekt już istnieje jego wartość będzie zastąpiona nową
x = 69
print(x)
## [1] 69
# jeśli polecenie będące przypisaniem umieścimy w nawiasie () to po jego wykonaniu wynikowy obiekt będzie także wyświetlony w konsoli
(x = 69)
## [1] 69
# możemy sprawdzić, czy istnieje obiekt o nazwie x w przestrzeni funkcją exists(), gdzie nazwę obiektu podaje się jako argument tekstowy (ujęty w cudzysłów)
# exists(x)
exists("x")
## [1] TRUE


To samo co robiliśmy przed momentem na liczbach działa również na zmiennych.

x <- 3
y <- 4
z <- x+y
print(z)
## [1] 7
x*y
## [1] 12
x%%y
## [1] 3



Konwencje nazewnictwa zmiennych:

  • snake_case (preferowany przez tidyverse i większość współczesnych pakietów)
moja_zmienna <- 666
  • camelCase (popularne w starszym kodzie)
mojaZmienna <- 333
  • dot.case (tradycyjne dla R, ale dziś odradzane)
moja.zmienna <- 999
  • PascalCase (zazwyczaj dla klas S4/R6)
MojaKlasa <- setClass("Kognitywistyka")



Podstawowe typy danych


W języku R wyróżniamy kilka podstawowych typów danych, które są kluczowe do pracy z danymi. Oto one:

  • Character: Typ danych przechowujący tekst. Każdy element jest traktowany jako ciąg znaków. Przykład: "Hello, World!".

  • Numeric: Typ danych przechowujący liczby zmiennoprzecinkowe. Przykład: 3.14.

  • Integer: Typ danych przechowujący liczby całkowite. W R liczby całkowite są oznaczane literą L po liczbie. Przykład: 42L.

  • Logical: Typ danych przechowujący wartości logiczne TRUE (bądź T) lub FALSE (tudzież F). Przykład: TRUE.

  • Complex: Typ danych przechowujący liczby zespolone. Przykład: 1 + 2i.

  • Factor: Specjalny typ danych używany do przechowywania zmiennych kategorycznych. Przykład: factor(c("male", "female", "female", "male")).

  • Date: Typ danych przechowujący daty. Przykład: as.Date("2024-09-05").

  • POSIXct: Typ danych przechowujący daty i czasy. Przykład: as.POSIXct("2024-09-05 09:25:01").

Każdy z tych typów danych ma swoje specyficzne zastosowania i jest używany w różnych kontekstach analizy danych. Warto znać te podstawowe typy, aby efektywnie pracować w

Typ (klasę) obiektu w R można sprawdzić za pomocą funkcji class().

moja_dokladna_liczba <- 54.7 #  numeric
class(moja_dokladna_liczba)
## [1] "numeric"
moja_liczba <- 53 # teoretycznie integer
class(moja_liczba)
## [1] "numeric"
moja_liczba <- 53L
class(moja_liczba) # mamy to
## [1] "integer"
# Wniosek, jeśli chcemy, aby wektor numeryczny przechowywał wartości całkowite (integer), należy podawać wartości liczbowe z dopisaną literą L

moje_slowo <- "histogram" # character (string), dygresja: możliwe też pojedyncze nawiasy '...'
class(moje_slowo)
## [1] "character"
moja_wart_logiczna <- T # Boolean data type (logical)
class(moja_wart_logiczna)
## [1] "logical"
class(moja_wart_logiczna) == "logical" # do sprawdzenia np. w jakiejś pętli, czy jakaś transformacja nie spowodowała zmiany typu na niechciany
## [1] TRUE
moja_wart_logiczna2 <- TRUE
str(moja_wart_logiczna2)
##  logi TRUE
# output funkcji będzie wyglądał nieco inaczej na różnych typach obiektów


Jeśli nie podobają nam się typy danych zastosowane przez R’a, możemy próbować je zmienić.

# czasem przydaje się trzymać wartość numeryczną jako string
moja_dokladna_liczba <- as.character(moja_dokladna_liczba)
class(moja_dokladna_liczba)
## [1] "character"
moja_dokladna_liczba
## [1] "54.7"
# wybuchnie?
moje_slowo <- as.numeric(moje_slowo)
## Warning: NAs introduced by coercion
class(moje_slowo)
## [1] "numeric"
moje_slowo # buuuuu
## [1] NA
# wtf?
moja_wart_logiczna2 <- as.integer(moja_wart_logiczna2)
class(moja_wart_logiczna2)
## [1] "integer"
# ale...
moja_wart_logiczna2
## [1] 1
1 + as.logical(moja_wart_logiczna2) # serio?
## [1] 2
as.integer(c(5.1, 3.2, 7.9, 1.4)) # a tu?
## [1] 5 3 7 1
# W przypadku użycia funkcji `as.integer` na liczbach, które nie są liczbami całkowitymi, R zaokrągla je w dół

Ogólnie hierarchia typów wygląda następująco: character > numeric > integer > logical



Podstawowe obiekty

Vectors

Wektor jest podstawową strukturą danych w języku R. Jest to uporządkowany zbiór elementów tego samego typu.

Ważne: W R nawet pojedyncza wartość jest wektorem o długości 1!

# To jest skalar, ale też wektor długości 1
x <- 5
x
## [1] 5
length(x)
## [1] 1

Zdefiniujmy zmienne używając wszechobecnej w R funkcji c(). c prawdopodobnie od combine

# Wektor numeryczny
(oceny_stat_R <- c(4,5,2,2,3))
## [1] 4 5 2 2 3
# Wektor znakowy
(za_co <- c("klasowka", "aktywnosc", "sprawdzian", "sprawdzian", "projekt"))
## [1] "klasowka"   "aktywnosc"  "sprawdzian" "sprawdzian" "projekt"
# Wektor logiczny
(warunki <- c(TRUE, FALSE, TRUE, TRUE))
## [1]  TRUE FALSE  TRUE  TRUE


Nazywanie wektora

# nadanie wartościom wektora nazw
names(oceny_stat_R) <- za_co 
# Obejrzyjmy
print(oceny_stat_R)
##   klasowka  aktywnosc sprawdzian sprawdzian    projekt 
##          4          5          2          2          3
# funkcja c() umożliwia nadanie nazw kolejnym elementom w konwencji nazwa = wartość
id <- c(pierwszy = 1, drugi = 2, trzeci = 3)

Swoją drogą, czy da się nakreślić jakiś “sznyt charakterologiczny” osoby z takimi ocenami?


Strukturę obiektu można sprawdzić funkcją str()

str(oceny_stat_R)
##  Named num [1:5] 4 5 2 2 3
##  - attr(*, "names")= chr [1:5] "klasowka" "aktywnosc" "sprawdzian" "sprawdzian" ...


Podstawowe operacje na wektorach

wektor_1 <- c(2,4,7,3)
wektor_2 <- c(3,8,9,2)

# dodawanie wektorów
print(suma_wektorow <- wektor_1 + wektor_2)
## [1]  5 12 16  5
# łączenie wektorów
(concat_wektorow <- c(wektor_1, wektor_2))
## [1] 2 4 7 3 3 8 9 2


To może coś wbudowaną funkcją by zrobić…

# gdybyśmy nie znali żadnej funkcji arytmetycznej w R musielibyśmy liczyć "na piechotę"
(4+5+2+2+3)/5
## [1] 3.2
# tak można
sum(suma_wektorow)
## [1] 38
length(oceny_stat_R)
## [1] 5
sum(concat_wektorow)
## [1] 38
mean(oceny_stat_R) # Średnia ocen z przedmiotu
## [1] 3.2
# albo...
sum(oceny_stat_R)/length(oceny_stat_R)
## [1] 3.2


Obejrzyjmy wektory składające się wyłącznie w elementów logicznych i zaaplikujmy do nich poznane operatory

x <- c(T, F, F)
y <- c(T, F, T)

x|y # W R operator alternatywy <OR (lub)> dla wektorów logicznych to operator |
## [1]  TRUE FALSE  TRUE
!x # negacja
## [1] FALSE  TRUE  TRUE
x&y # W R operator koniunkcji (AND) dla wektorów logicznych to operator &.
## [1]  TRUE FALSE FALSE


Sekwencje

(id <- 1:3)
## [1] 1 2 3
(id <- c(1:3))
## [1] 1 2 3
10:1
##  [1] 10  9  8  7  6  5  4  3  2  1
-5:5
##  [1] -5 -4 -3 -2 -1  0  1  2  3  4  5
5:-5
##  [1]  5  4  3  2  1  0 -1 -2 -3 -4 -5
5.1:8.2 # ostatnia wartość nie jest wartością wskazaną jako kończąca sekwencję
## [1] 5.1 6.1 7.1 8.1

seq(from=, to=, by=, length.out =…)

seq(1, 10, 1) # równoważnik 1:10
##  [1]  1  2  3  4  5  6  7  8  9 10
seq(1, 10, 2) # 9?
## [1] 1 3 5 7 9
(seq(1,10))
##  [1]  1  2  3  4  5  6  7  8  9 10
class(seq(1,10)) # <- wydaje się oczywiste
## [1] "integer"
(seq(1,10,1))
##  [1]  1  2  3  4  5  6  7  8  9 10
class(seq(1,10,1)) # <- wydaje się mnie oczywiste, dodaliśmy jedynie jawnie 3. argument w funkcji
## [1] "numeric"
(seq(1L,10L,1))
##  [1]  1  2  3  4  5  6  7  8  9 10
class(seq(1L,10L,1L)) # <- patent, żeby znów zachowywało się "intuicyjnie"
## [1] "integer"
# lub
(seq(1,10,1))
##  [1]  1  2  3  4  5  6  7  8  9 10
class(seq(1,10,1L))
## [1] "numeric"
(sekwencjaNumericow <- seq(from = 1,
                           to = 10, 
                           length.out = 20))
##  [1]  1.000000  1.473684  1.947368  2.421053  2.894737  3.368421  3.842105
##  [8]  4.315789  4.789474  5.263158  5.736842  6.210526  6.684211  7.157895
## [15]  7.631579  8.105263  8.578947  9.052632  9.526316 10.000000
(sekwencjaNumericow2 <- seq.int(from = 1,
                                to = 10,
                                length.out = 19)) # == seq(1, 10, by = 0.5), gdyż "przepis" na by to:
##  [1]  1.0  1.5  2.0  2.5  3.0  3.5  4.0  4.5  5.0  5.5  6.0  6.5  7.0  7.5  8.0
## [16]  8.5  9.0  9.5 10.0
# by = (to - from)/(length.out - 1)  -> ?seq

(sekwencjaNumericow3 <- seq(from = 0.5, # <- 
                            to = 10, 
                            length.out = 20))
##  [1]  0.5  1.0  1.5  2.0  2.5  3.0  3.5  4.0  4.5  5.0  5.5  6.0  6.5  7.0  7.5
## [16]  8.0  8.5  9.0  9.5 10.0


Powtarzanie wartości

rep(666, times = 10)
##  [1] 666 666 666 666 666 666 666 666 666 666
rep(1:5, times = 10)
##  [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3
## [39] 4 5 1 2 3 4 5 1 2 3 4 5
rep(1:5, each = 10)
##  [1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4
## [39] 4 4 5 5 5 5 5 5 5 5 5 5
(szpital<-c(rep("lekarz",6),
            rep("administracja",7),
            rep("pielęgniarka",9))
  ) # rep() na wektorach
##  [1] "lekarz"        "lekarz"        "lekarz"        "lekarz"       
##  [5] "lekarz"        "lekarz"        "administracja" "administracja"
##  [9] "administracja" "administracja" "administracja" "administracja"
## [13] "administracja" "pielęgniarka"  "pielęgniarka"  "pielęgniarka" 
## [17] "pielęgniarka"  "pielęgniarka"  "pielęgniarka"  "pielęgniarka" 
## [21] "pielęgniarka"  "pielęgniarka"
trufolsy <- (rep(c(TRUE, FALSE), times = 5)) # rep() na trufolsach


Indeksowanie wektora

UWAGA: R indeksuje od 1, nie od 0!

# stwórzmy sobie znakowy wektor
owoce <- c("jabłko", "banan", "gruszka", "pomarańcza", "winogrono")

# Pierwszy element
owoce[1]
## [1] "jabłko"
# Trzeci element
owoce[3]
## [1] "gruszka"
# Wiele elementów
owoce[c(1, 3, 5)]
## [1] "jabłko"    "gruszka"   "winogrono"
# Zakres
owoce[2:4]
## [1] "banan"      "gruszka"    "pomarańcza"
# Wszystko oprócz pierwszego
owoce[-1]
## [1] "banan"      "gruszka"    "pomarańcza" "winogrono"
# Wszystko oprócz pierwszego i ostatniego
owoce[-c(1, 5)]
## [1] "banan"      "gruszka"    "pomarańcza"


Indeksowanie logiczne

liczby <- c(5, 12, 8, 3, 15, 20, 7)

# Które liczby są większe od 10?
liczby > 10
## [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE
# Wybierz liczby większe od 10
liczby[liczby > 10]
## [1] 12 15 20
# Liczby parzyste
liczby[liczby %% 2 == 0]
## [1] 12  8 20
# Liczby między 5 a 15
liczby[liczby >= 5 & liczby <= 15]
## [1]  5 12  8 15  7

Indeksowanie przez nazwy

# Nadajemy nazwy elementom
wiek <- c(25, 30, 22, 28)
names(wiek) <- c("Anna", "Jan", "Kasia", "Piotr")
wiek
##  Anna   Jan Kasia Piotr 
##    25    30    22    28
# Dostęp przez nazwę
wiek["Jan"]
## Jan 
##  30
# Wiele nazw
wiek[c("Anna", "Kasia")]
##  Anna Kasia 
##    25    22
# A co w sytuacji?
names(wiek) <- c("Anna", "Jan", "Kasia", "Anna") # 2x "Anna"
wiek["Anna"] # :(
## Anna 
##   25
wiek[c("Anna", "Anna")] # :(
## Anna Anna 
##   25   25
# wówczas:
wiek[names(wiek) == "Anna"]
## Anna Anna 
##   25   28

Ważne: Gdy wektory mają różne długości, R “recykluje” krótszy wektor:

c(1, 2, 3, 4) + c(10, 20)  # c(10, 20) jest użyte dwa razy
## [1] 11 22 13 24

UWAGA: Gdy długość nie jest wielokrotnością, dostaniemy ostrzeżenie

c(1, 2, 3) + c(10, 20)
## Warning in c(1, 2, 3) + c(10, 20): longer object length is not a multiple of
## shorter object length
## [1] 11 22 13


zmiana elementów wektora

wektor <- seq(10,50, by = 10)
# Zmiana pojedynczego elementu
wektor[3] <- 999
wektor
## [1]  10  20 999  40  50
# Zmiana wielu elementów
wektor[c(1, 5)] <- c(111, 555)
wektor
## [1] 111  20 999  40 555
# Dodawanie elementów na końcu
wektor <- c(wektor, 60, 70)
wektor
## [1] 111  20 999  40 555  60  70
# Usuwanie elementów
wektor <- wektor[-c(2, 4)]
wektor
## [1] 111 999 555  60  70


NA - Not Available (brakujące dane)

wektor_z_brakami <- c(1, 2, NA, 4, 5)
wektor_z_brakami
## [1]  1  2 NA  4  5
# Większość funkcji zwróci NA, jeśli są braki
mean(wektor_z_brakami)
## [1] NA
# Musimy pominąć NA
mean(wektor_z_brakami, na.rm = TRUE)
## [1] 3
# Sprawdzanie NA
is.na(wektor_z_brakami)
## [1] FALSE FALSE  TRUE FALSE FALSE
# Ile jest NA?
sum(is.na(wektor_z_brakami))
## [1] 1


NULL - Brak

# NULL - pusty obiekt
x <- NULL
length(x)
## [1] 0


NaN - Not a Number (nie-liczba)

# NaN - Not a Number (nieokreślona operacja matematyczna)
0/0
## [1] NaN


Inf - Infinity (nieskończoność)

1/0 # Inf (nieskończoność)
## [1] Inf
-1/0 # -Inf (-nieskończoność)
## [1] -Inf

Sortowanie

liczby <- c(23, 5, 17, 8, 42, 11)

# Sortowanie rosnąco
sort(liczby)
## [1]  5  8 11 17 23 42
# Sortowanie malejąco
sort(liczby, decreasing = TRUE)
## [1] 42 23 17 11  8  5
# Indeksy posortowanych elementów
order(liczby)
## [1] 2 4 6 3 1 5
# Użycie order() do sortowania
liczby[order(liczby)]
## [1]  5  8 11 17 23 42

Inne przydatne funkcje

wektor <- c(3, 7, 2, 7, 9, 2, 5, 7)

# Unikalne wartości
unique(wektor)
## [1] 3 7 2 9 5
# Ile razy występuje każda wartość
table(wektor)
## wektor
## 2 3 5 7 9 
## 2 1 1 3 1
# Czy element występuje w wektorze
7 %in% wektor
## [1] TRUE
100 %in% wektor
## [1] FALSE
# Pozycja elementu
which(wektor == 7)
## [1] 2 4 8
# Liczba elementów spełniających warunek
sum(wektor > 5)
## [1] 4
# Odwracanie kolejności
rev(wektor)
## [1] 7 5 2 9 7 2 7 3

Filtrowanie danych

# Wyniki egzaminów studentów
wyniki <- c(45, 78, 92, 67, 83, 54, 88, 71, 96, 62)
imiona_studentow <- c("Anna", "Bartek", "Celina", "Darek", "Ewa", 
                      "Filip", "Gosia", "Heniek", "Iza", "Janek")

# Kto zdał (>= 60 punktów)?
zdali <- wyniki >= 60
imiona_studentow[zdali]
## [1] "Bartek" "Celina" "Darek"  "Ewa"    "Gosia"  "Heniek" "Iza"    "Janek"
# Kto ma najlepszy wynik?
max(wyniki)
## [1] 96
imiona_studentow[which.max(wyniki)]
## [1] "Iza"

Normalizacja danych

# Dane surowe
dane <- c(10, 15, 8, 20, 12)

# Normalizacja do zakresu [0, 1]
dane_norm <- (dane - min(dane)) / (max(dane) - min(dane))
dane_norm
## [1] 0.1666667 0.5833333 0.0000000 1.0000000 0.3333333

Standaryzacja danych

# Standaryzacja (średnia = 0, sd = 1)
dane_std <- (dane - mean(dane)) / sd(dane)
dane_std
## [1] -0.6396021  0.4264014 -1.0660036  1.4924050 -0.2132007
round(mean(dane_std), 10)  # Sprawdzenie (w przybliżeniu 0)
## [1] 0

Modyfikacja wektorów - budowanie wektora z innego wektora

id <- c(pierwszy = 1, drugi = 2, trzeci = 3)
# a budować wektor możemy z "części znalezionych na szrocie"
szesc = 6
id2 <- c(czwarty = id[2]*2, piąty = id[3]+2, szósty = szesc)

Symulacja

# Rzut kostką 1000 razy
set.seed(123)  # Dla powtarzalności
rzuty <- sample(1:6, size = 1000, replace = TRUE)

# Rozkład wyników
table(rzuty)
## rzuty
##   1   2   3   4   5   6 
## 170 176 171 157 162 164
# Prawdopodobieństwo empiryczne każdej ścianki
table(rzuty) / length(rzuty)
## rzuty
##     1     2     3     4     5     6 
## 0.170 0.176 0.171 0.157 0.162 0.164


Wektory “predefiniowane”

# przykłady
letters
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
## [20] "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" "R" "S"
## [20] "T" "U" "V" "W" "X" "Y" "Z"
month.abb
##  [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
month.name
##  [1] "January"   "February"  "March"     "April"     "May"       "June"     
##  [7] "July"      "August"    "September" "October"   "November"  "December"
rev(LETTERS) # funkcja odwracająca kolejność wektora
##  [1] "Z" "Y" "X" "W" "V" "U" "T" "S" "R" "Q" "P" "O" "N" "M" "L" "K" "J" "I" "H"
## [20] "G" "F" "E" "D" "C" "B" "A"


Co można zrobić na takich wektorach? Na przykład zastosować funkcję length()

length(letters)
## [1] 26
length(c(month.abb, month.name))
## [1] 24

Konwersja typów.

Przypomnijmy sobie typy danych 3 wektorów w środowisku

class(letters)
## [1] "character"
class(trufolsy)
## [1] "logical"
class(sekwencjaNumericow)
## [1] "numeric"
(wektor = c(letters, trufolsy)); class(wektor)
##  [1] "a"     "b"     "c"     "d"     "e"     "f"     "g"     "h"     "i"    
## [10] "j"     "k"     "l"     "m"     "n"     "o"     "p"     "q"     "r"    
## [19] "s"     "t"     "u"     "v"     "w"     "x"     "y"     "z"     "TRUE" 
## [28] "FALSE" "TRUE"  "FALSE" "TRUE"  "FALSE" "TRUE"  "FALSE" "TRUE"  "FALSE"
## [1] "character"
(wektor = c(letters, sekwencjaNumericow)); class(wektor) # co wynika ze wspomnianej wcześniej hierarchii typów
##  [1] "a"                "b"                "c"                "d"               
##  [5] "e"                "f"                "g"                "h"               
##  [9] "i"                "j"                "k"                "l"               
## [13] "m"                "n"                "o"                "p"               
## [17] "q"                "r"                "s"                "t"               
## [21] "u"                "v"                "w"                "x"               
## [25] "y"                "z"                "1"                "1.47368421052632"
## [29] "1.94736842105263" "2.42105263157895" "2.89473684210526" "3.36842105263158"
## [33] "3.84210526315789" "4.31578947368421" "4.78947368421053" "5.26315789473684"
## [37] "5.73684210526316" "6.21052631578947" "6.68421052631579" "7.15789473684211"
## [41] "7.63157894736842" "8.10526315789474" "8.57894736842105" "9.05263157894737"
## [45] "9.52631578947368" "10"
## [1] "character"
(wektor = c(trufolsy, sekwencjaNumericow)); class(wektor) # co wynika ze wspomnianej wcześniej hierarchii typów
##  [1]  1.000000  0.000000  1.000000  0.000000  1.000000  0.000000  1.000000
##  [8]  0.000000  1.000000  0.000000  1.000000  1.473684  1.947368  2.421053
## [15]  2.894737  3.368421  3.842105  4.315789  4.789474  5.263158  5.736842
## [22]  6.210526  6.684211  7.157895  7.631579  8.105263  8.578947  9.052632
## [29]  9.526316 10.000000
## [1] "numeric"


Transpozycja wektora

oceny_stat_R # obejrzyjmy
##   klasowka  aktywnosc sprawdzian sprawdzian    projekt 
##          4          5          2          2          3
class(oceny_stat_R)
## [1] "numeric"
is.vector(oceny_stat_R)
## [1] TRUE
t(oceny_stat_R) # transponujmy
##      klasowka aktywnosc sprawdzian sprawdzian projekt
## [1,]        4         5          2          2       3
class(t(oceny_stat_R)); is.vector(t(oceny_stat_R))
## [1] "matrix" "array"
## [1] FALSE
# macierz wierszowa (1×5)

t(t(oceny_stat_R)) # transponujmy "do kwadratu", odwrócimy proces? :)
##            [,1]
## klasowka      4
## aktywnosc     5
## sprawdzian    2
## sprawdzian    2
## projekt       3
class(t(t(oceny_stat_R))); is.vector(t(t(oceny_stat_R)))
## [1] "matrix" "array"
## [1] FALSE
# macierz wierszowa (5X1)



Do zapamiętania:

  1. Wektor = podstawowa struktura danych w R
  2. Wszystkie elementy muszą być tego samego typu
  3. Indeksowanie od 1, nie od 0!
  4. Operacje są wektoryzowane (działają element po elemencie)
  5. Krótsze wektory są recyklingowane w operacjach
  6. NA wymaga specjalnej obsługi (na.rm = TRUE)
  7. Indeksowanie logiczne to potężne narzędzie

Najczęstsze pułapki:

  • Zapomnienie o indeksowaniu od 1
  • Nieobsłużone wartości NA
  • Nieświadomy recykling wektorów o różnych długościach
  • Automatyczna konwersja typów przy mieszaniu



Zadania przy tablicy

  1. Z wektora liczb od 1 do 100, wybierz tylko liczby podzielne przez 7
liczby_1_100 <- 1:100
podzielne_przez_7 <- liczby_1_100[liczby_1_100 %% 7 == 0]

# alternatywnie
podzielne_przez_7_v2 <- seq(from = 7, to = 100, by = 7)
  1. Stwórz wektor 20 losowych liczb (runif()) z przedziału od 0 do 100 i znajdź te większe od średniej
set.seed(42)  # Dla powtarzalności wyników
losowe_liczby <- runif(n = 20, min = 0, max = 100)
srednia <- mean(losowe_liczby)
wieksze_od_sredniej <- losowe_liczby[losowe_liczby > srednia]
  1. Stwórz wektor kilku imion i posortuj go alfabetycznie
imiona <- c("Zofia", "Adam", "Katarzyna", "Bartosz", "Ewa", 
            "Michał", "Anna", "Piotr", "Dorota", "Krzysztof")
imiona_sorted <- sort(imiona)
imiona_sorted_desc <- sort(imiona, decreasing = TRUE)
  1. Wykorzystaj wcześniejszy wektor imion i posortuj rosnąco po długości znaków każdego imienia.
imiona <- c("Zofia", "Adam", "Katarzyna", "Bartosz", "Ewa",
            "Michał", "Anna", "Piotr", "Dorota", "Krzysztof")

# 1. Obliczanie długości znaków
dlugosci <- nchar(imiona)
# [1] 5 4 9 7 3 6 4 5 6 9

# 2. Uzyskanie kolejności (indeksów) sortującej ROSNĄCO
kolejnosc_rosnaco <- order(dlugosci)
# [1] 5 2 7 1 8 6 9 4 3 10

# 3. Sortowanie wektora imion
imiona_posortowane <- imiona[kolejnosc_rosnaco]

# malejąco
imiona_posortowane_malejaco <- imiona[order(nchar(imiona), decreasing = TRUE)]
  1. Symuluj 10000 rzutów monetą (sample()) i oblicz częstość wypadnięcia orła
set.seed(123)  # Dla powtarzalności
# Metoda 1: Używając sample()
# 0 = reszka, 1 = orzeł
rzuty_moneta <- sample(c(0, 1), size = 10000, replace = TRUE)
# Alternatywnie z nazwami:
rzuty_moneta2 <- sample(c("Reszka", "Orzeł"), size = 10000, replace = TRUE)
# Obliczamy częstość orła
liczba_orlow <- sum(rzuty_moneta == 1)
liczba_reszek <- sum(rzuty_moneta == 0)
czestosc_orla <- liczba_orlow / length(rzuty_moneta)

round(czestosc_orla * 100, 2)
## [1] 49.83
round((1 - czestosc_orla) * 100, 2)
## [1] 50.17
table(rzuty_moneta)
## rzuty_moneta
##    0    1 
## 5017 4983
prop.table(table(rzuty_moneta2))
## rzuty_moneta2
##  Orzeł Reszka 
## 0.4959 0.5041
# Prosty wykres słupkowy
# barplot(table(rzuty_moneta), 
#         names.arg = c("Reszka", "Orzeł"),
#         main = "Symulacja 10000 rzutow moneta",
#         xlab = "Wynik",
#         ylab = "Liczba wystąpień",
#         col = c("lightblue", "gold"),
#         ylim = c(0, 6000))
# abline(h = 5000, col = "red", lty = 2, lwd = 2)
# text(1.5, 5200, "Oczekiwana wartość (5000)", col = "red")


Funkcja set.seed() w R służy do ustawienia ziarna generatora liczb losowych, dzięki czemu wyniki losowań są powtarzalne. Za każdym razem, gdy używasz funkcji losowych (np. sample(), runif(), rnorm() itd.), R generuje liczby w sposób pseudo-losowy — tzn. opiera się na algorytmie, który startuje od pewnego ziarna (ang. seed). R korzysta z tzw. PRNG (Pseudo-Random Number Generator). Domyślnie jest to algorytm Mersenne Twister. To bardzo popularny i szybki algorytm generowania liczb pseudo-losowych (opracowany w 1997 roku przez Matsumoto i Nishimura).



Matrices

Stwórzmy pierwszą ‘matrycę’

#  3 wiersze zawierającą cyfry od 1 do 9
matrix(1:9, byrow = TRUE, nrow = 3)
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6
## [3,]    7    8    9


# oceny semestralne z trzech przedmiotów
stat_R <- c(2,3) # vector()
filozofia <- c(5,5)
matematyka <- c(4,4)

# połączone w wektor
oceny <- c(stat_R, filozofia, matematyka)

# ramka (matryca) z ocenami
oceny_matrix <- matrix(oceny, nrow = 3, byrow = T)
print(oceny_matrix)
##      [,1] [,2]
## [1,]    2    3
## [2,]    5    5
## [3,]    4    4


colnames() & rownames() -> nazywanie ramki

# szykowne nazwy wierszy i kolumn
nazwa_przedmiotu <- c("Statystyka w R", "Filozofia analityczna", "Matematyka Euklidesowa")
semestry <- c("Semestr 1", "Semestr 2")
# przypisanie ich do ramki
colnames(oceny_matrix) <- semestry
rownames(oceny_matrix) <- nazwa_przedmiotu
# Voici
print(oceny_matrix)
##                        Semestr 1 Semestr 2
## Statystyka w R                 2         3
## Filozofia analityczna          5         5
## Matematyka Euklidesowa         4         4


# metoda "od razu"
oceny_matrix <- matrix(oceny, 
                       nrow = 3, 
                       byrow = T, 
                       dimnames = list(nazwa_przedmiotu, semestry)) # jeszcze w sumie nie znamy typu lista
oceny_matrix
##                        Semestr 1 Semestr 2
## Statystyka w R                 2         3
## Filozofia analityczna          5         5
## Matematyka Euklidesowa         4         4
# średnia ocen na koniec roku?
rowMeans(oceny_matrix)
##         Statystyka w R  Filozofia analityczna Matematyka Euklidesowa 
##                    2.5                    5.0                    4.0


Atrybuty macierzy

# klasa utworzonego obiektu
class(oceny_matrix)
## [1] "matrix" "array"
# struktura
str(oceny_matrix)
##  num [1:3, 1:2] 2 5 4 3 5 4
##  - attr(*, "dimnames")=List of 2
##   ..$ : chr [1:3] "Statystyka w R" "Filozofia analityczna" "Matematyka Euklidesowa"
##   ..$ : chr [1:2] "Semestr 1" "Semestr 2"
# inne 
rownames(oceny_matrix)
## [1] "Statystyka w R"         "Filozofia analityczna"  "Matematyka Euklidesowa"
colnames(oceny_matrix)
## [1] "Semestr 1" "Semestr 2"
dimnames(oceny_matrix)
## [[1]]
## [1] "Statystyka w R"         "Filozofia analityczna"  "Matematyka Euklidesowa"
## 
## [[2]]
## [1] "Semestr 1" "Semestr 2"
nrow(oceny_matrix)
## [1] 3
ncol(oceny_matrix)
## [1] 2
dim(oceny_matrix)
## [1] 3 2


Rozszerzanie ramki

# zapomnieliśmy o kulturze ciała, skupiając się na kulturze umysłu
wych_fiz <- c(6,6)

# dodajmy zatem nowy przedmiot do naszej ramki
oceny_matrix <- rbind(oceny_matrix, wych_fiz)
oceny_matrix
##                        Semestr 1 Semestr 2
## Statystyka w R                 2         3
## Filozofia analityczna          5         5
## Matematyka Euklidesowa         4         4
## wych_fiz                       6         6
# poprawmy nazwę przedmiotu (jesteśmy wszakże estetami)
rownames(oceny_matrix)[4] <- c("Wychowanie fizyczne")

# obliczmy średnią ocen ponownie, przypiszmy do zmiennej
srednia_ocen <- rowMeans(oceny_matrix)
srednia_ocen
##         Statystyka w R  Filozofia analityczna Matematyka Euklidesowa 
##                    2.5                    5.0                    4.0 
##    Wychowanie fizyczne 
##                    6.0
# dodajmy podsumowującą kolumnę do ramki
oceny_matrix <- cbind(oceny_matrix, srednia_ocen)
oceny_matrix
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R                 2         3          2.5
## Filozofia analityczna          5         5          5.0
## Matematyka Euklidesowa         4         4          4.0
## Wychowanie fizyczne            6         6          6.0
# a jak nam szło w poszczególnych semestrach?
colMeans(oceny_matrix)
##    Semestr 1    Semestr 2 srednia_ocen 
##        4.250        4.500        4.375


Wyciąganie elementów z matrycy

# Pani mówi, że 'włef' nie liczy się do średniej :(
dim(oceny_matrix)
## [1] 4 3
oceny_matrix2 <- oceny_matrix[1:3,] # wyrzucamy w-f
oceny_matrix2
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R                 2         3          2.5
## Filozofia analityczna          5         5          5.0
## Matematyka Euklidesowa         4         4          4.0
srednia_semestr <- (colMeans(oceny_matrix2)) # obliczamy ponownie średnie semestralne

# i dodajemy do naszej ramki
oceny_matrix2 <- rbind(oceny_matrix2, srednia_semestr)
oceny_matrix2
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R          2.000000         3     2.500000
## Filozofia analityczna   5.000000         5     5.000000
## Matematyka Euklidesowa  4.000000         4     4.000000
## srednia_semestr         3.666667         4     3.833333
# przypominam o tym, że jesteśmy estetami
colnames(oceny_matrix2)[3] <- "Średnia ocen z semestru"
oceny_matrix2
##                        Semestr 1 Semestr 2 Średnia ocen z semestru
## Statystyka w R          2.000000         3                2.500000
## Filozofia analityczna   5.000000         5                5.000000
## Matematyka Euklidesowa  4.000000         4                4.000000
## srednia_semestr         3.666667         4                3.833333


# Pan od w-f ogarnął się, że na studiach nie ma szóstek, to nie podstawówka
oceny_matrix[4,1:2] <- oceny_matrix[4,1:2] - 1
oceny_matrix
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R                 2         3          2.5
## Filozofia analityczna          5         5          5.0
## Matematyka Euklidesowa         4         4          4.0
## Wychowanie fizyczne            5         5          6.0
# a Pan od R'a dostał poprawiony, tym razem zrobiony samodzielnie, projekt z Rmarkdown'a
oceny_matrix[1,1] <- 3.5
oceny_matrix 
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R               3.5         3          2.5
## Filozofia analityczna        5.0         5          5.0
## Matematyka Euklidesowa       4.0         4          4.0
## Wychowanie fizyczne          5.0         5          6.0
# tylko średnie trzeba by ponownie przeliczyć i podmienić
oceny_matrix[1,3] <- mean(c(oceny_matrix[1,1], oceny_matrix[1,2])) # funkcja mean()
oceny_matrix
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R               3.5         3         3.25
## Filozofia analityczna        5.0         5         5.00
## Matematyka Euklidesowa       4.0         4         4.00
## Wychowanie fizyczne          5.0         5         6.00


Skoro już napinamy muskuły, że jesteśmy tacy schludni i perfekcyjni, to stwierdzamy, że drażni nas, że R zrobił nam z dwóch kolumn liczby wysokiej precyzji (typu double, numeric, what ever language do you use) i duża liczba znaków się nam wyświetla

# przypomnijmy sobie kształt ramki
oceny_matrix2
##                        Semestr 1 Semestr 2 Średnia ocen z semestru
## Statystyka w R          2.000000         3                2.500000
## Filozofia analityczna   5.000000         5                5.000000
## Matematyka Euklidesowa  4.000000         4                4.000000
## srednia_semestr         3.666667         4                3.833333
# poznajmy round()
oceny_matrix2[,1] <- round(oceny_matrix2[,1], 2)
oceny_matrix2[,3] <- round(oceny_matrix2[,1], 2)

oceny_matrix2 # much better
##                        Semestr 1 Semestr 2 Średnia ocen z semestru
## Statystyka w R              2.00         3                    2.00
## Filozofia analityczna       5.00         5                    5.00
## Matematyka Euklidesowa      4.00         4                    4.00
## srednia_semestr             3.67         4                    3.67

round()

# btw

x <- 123.456789

round(x, digits = 2)   # 123.46  (2 miejsca po przecinku)
## [1] 123.46
round(x, digits = 1)   # 123.5   (1 miejsce po przecinku)
## [1] 123.5
round(x, digits = 0)   # 123     (liczba całkowita)
## [1] 123
round(x, digits = -1)  # 120     (zaokrąglenie do dziesiątek, do wielokrotności 10**2)
## [1] 120
round(x, digits = -2)  # 100     (zaokrąglenie do setek)
## [1] 100
round(x, digits = -3)  # 0       (zaokrąglenie do tysięcy)
## [1] 0


I taka jeszcze własność matryc

oceny_matrix # przypomnijmy sobie kształt naszej ramki
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R               3.5         3         3.25
## Filozofia analityczna        5.0         5         5.00
## Matematyka Euklidesowa       4.0         4         4.00
## Wychowanie fizyczne          5.0         5         6.00
# koleżanka nie lubi kolegi, nie odpowiedział na jej afekt, a że jest kocórką IT, zhakowała USOS i obniżyła mu oceny ze wszystkich przedmiotów
oceny_matrix - 1
##                        Semestr 1 Semestr 2 srednia_ocen
## Statystyka w R               2.5         2         2.25
## Filozofia analityczna        4.0         4         4.00
## Matematyka Euklidesowa       3.0         3         3.00
## Wychowanie fizyczne          4.0         4         5.00
# szczęśliwie nie nadpisała wyników ;)
oceny_matrix[oceny_matrix == 4]
## [1] 4 4 4
which(oceny_matrix == 4, arr.ind = TRUE)
##                        row col
## Matematyka Euklidesowa   3   1
## Matematyka Euklidesowa   3   2
## Matematyka Euklidesowa   3   3


Drobny wtręt. Badając klasę (class()) macierzy otrzymaliśmy informację, że nasz obiekt to zarówno matrix, jak i array. Krótka różnica między dwoma obiektami na przykładzie poniżej:

# Macierz 2x3
(macierz_przyklad <- matrix(data = 1:6, 
                            nrow = 2, 
                            ncol = 3)
 )
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6
# Tablica - # Array z 3 wymiarami (wiersze × kolumny × "warstwy")
(tablica_przyklad <- array(1:24, dim = c(3, 4, 2)))
## , , 1
## 
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4]
## [1,]   13   16   19   22
## [2,]   14   17   20   23
## [3,]   15   18   21   24
# To tworzy strukturę:
# - 3 wiersze
# - 4 kolumny  
# - 2 "warstwy" (można myśleć o tym jak o dwóch matrycach 3×4)

Główną różnicą między tymi dwoma obiektami jest to, że macierz (matrix) jest szczególnym, bo dwuwymiarowym typem tablicy (array). Tablica jest co do zasady wielowymiarową strukturą danych, która - ważne zastrzeżenie - przechowuje elementy tego samego typu (w przeciwieństwie do listy, która co prawda jest jednowymiarowa, ale może przechowywać elementy różnych typów, a zatem również listy, co de facto czynią ją w jakimś sensie wielowymiarową… bełkot, szczegóły później).

“Dane o sprzedaży”

# Wyobraźmy sobie dane o sprzedaży:
# - 3 produkty (wiersze)
# - 4 kwartały (kolumny)
# - 2 lata (trzeci wymiar)

sprzedaz <- array(
  data = c(100, 150, 200, 120, 180, 220, 110, 160, 210, 130, 170, 230,
           105, 155, 205, 125, 185, 225, 115, 165, 215, 135, 175, 235),
  dim = c(3, 4, 2),
  dimnames = list(
    Produkt = c("A", "B", "C"),
    Kwartal = c("Q1", "Q2", "Q3", "Q4"),
    Rok = c("2023", "2024")
  )
)

print(sprzedaz)
## , , Rok = 2023
## 
##        Kwartal
## Produkt  Q1  Q2  Q3  Q4
##       A 100 120 110 130
##       B 150 180 160 170
##       C 200 220 210 230
## 
## , , Rok = 2024
## 
##        Kwartal
## Produkt  Q1  Q2  Q3  Q4
##       A 105 125 115 135
##       B 155 185 165 175
##       C 205 225 215 235
# Dostęp do danych z 2024 roku
sprzedaz[, , "2024"]
##        Kwartal
## Produkt  Q1  Q2  Q3  Q4
##       A 105 125 115 135
##       B 155 185 165 175
##       C 205 225 215 235
# Sprzedaż produktu B w Q2 w 2023
sprzedaz["B", "Q2", "2023"]
## [1] 180
# Wszystkie kwartały produktu A w 2024
sprzedaz["A", , "2024"]
##  Q1  Q2  Q3  Q4 
## 105 125 115 135


“Dane klimatyczne”

# Temperatura w 12 miastach, przez 365 dni, przez 5 lat
temperatura <- array(
  data = rnorm(12 * 365 * 5, mean = 15, sd = 8), # ?rnorm
  dim = c(12, 365, 5),
  dimnames = list(
    Miasto = paste("Miasto", 1:12), # paste()
    Dzien = paste("Dzien", 1:365),
    Rok = 2020:2024
  )
)

# Średnia temperatura w Mieście 1 w 2023 roku
mean(temperatura["Miasto 1", , "2023"])
## [1] 14.27825
# Średnia temperatura w 100. dniu we wszystkich miastach i latach
mean(temperatura[, "Dzien 100", ])
## [1] 13.64846
# Temperatura w pierwszych 7 dniach 2024 we wszystkich miastach
temperatura[, 1:7, "2024"]
##            Dzien
## Miasto        Dzien 1   Dzien 2     Dzien 3    Dzien 4   Dzien 5   Dzien 6
##   Miasto 1  14.473869 -1.551787 19.79363169 14.4381710  8.918001  8.533684
##   Miasto 2  14.044740 18.297673 18.71871307 28.3521016 25.675413  5.279035
##   Miasto 3  18.791663 18.093304 25.47361742 26.2767049 10.682710 30.669667
##   Miasto 4   3.852931  7.698747 17.50966918 11.7801681 20.536229 12.262536
##   Miasto 5  37.544346 10.947059 10.44449749 15.3624909  4.577420  8.899487
##   Miasto 6  11.546584 23.632032 11.83750548 17.5142275 22.056840 24.029481
##   Miasto 7  10.662979 10.529199  7.52618951 14.9028578  4.177482 17.069458
##   Miasto 8  -2.192245  2.150525  0.06323819 24.0170482 22.147729  7.670347
##   Miasto 9  12.301732  9.559002 13.29824284 13.6136358 29.795179  7.068208
##   Miasto 10  9.290747 22.288937 11.09591696 28.2043316 15.198680  1.856036
##   Miasto 11 13.559546  1.996519  8.93664205 13.4222434 22.663044 22.815649
##   Miasto 12 27.219777  9.128440 14.69095951 -0.5082137 20.378205  3.125952
##            Dzien
## Miasto        Dzien 7
##   Miasto 1   4.221015
##   Miasto 2  24.983083
##   Miasto 3  15.410208
##   Miasto 4   9.414400
##   Miasto 5  17.284862
##   Miasto 6  12.244926
##   Miasto 7   7.813790
##   Miasto 8  17.794933
##   Miasto 9  26.596484
##   Miasto 10 29.048825
##   Miasto 11 10.020744
##   Miasto 12 19.022158

Factors

Factor to tzw. zmienna kategorialna - nominalna bądź porządkowa.

Dane kategorialne (jakościowe) to takie, które przyjmują wartości z ograniczonego zestawu kategorii.

Przykłady:

  • Płeć: “kobieta”, “mężczyzna”

  • Grupa krwi: “A”, “B”, “AB”, “0”

  • Poziom wykształcenia: “podstawowe”, “średnie”, “wyższe”

  • Ocena: “niedostateczny”, “dostateczny”, “dobry”, “bardzo dobry”, “celujący”



* Nominalna

# Zdefiniujmy 'zwykły' wektor zawierający płcie 
# (załóżmy, omijając zgrabnie współczesne dywagacje, że mamy dwie płcie)
(sex_vector <- c("Mężczyzna", "Kobieta", "Kobieta", "Mężczyzna", "Mężczyzna"))
## [1] "Mężczyzna" "Kobieta"   "Kobieta"   "Mężczyzna" "Mężczyzna"
# skonwertujmy typ zmiennej na factor, przypiszmy do nowej zmiennej i zobaczmy rezultat
(factor_sex_vector <- as.factor(sex_vector))
## [1] Mężczyzna Kobieta   Kobieta   Mężczyzna Mężczyzna
## Levels: Kobieta Mężczyzna


* Porządkowa

temperatura_vector <- c("Wysoka", "Niska", "Wysoka","Niska", "Średnia")
factor_temperatura_vector <- factor(temperatura_vector, 
                                    order = TRUE, # dookreślamy zarówno czy jest to zmienna porządkowa
                                    levels = c("Niska", "Średnia", "Wysoka")) #  oraz jak ten porządek rzeczy się układa
factor_temperatura_vector
## [1] Wysoka  Niska   Wysoka  Niska   Średnia
## Levels: Niska < Średnia < Wysoka
# Możemy sprawdzić liczbę poziomów w następujący sposób
nlevels(factor_temperatura_vector)
## [1] 3


Poziomy zmiennej

# tworzymy wektor 
vector_plci <- c("M", "F", "F", "M", "M")
# 'faktoryzujemy' go
factor_vector_plci <- as.factor(vector_plci)
# w kolejnym kroku nazywamy poziomy
levels(factor_vector_plci) <- c("Kobieta", "Mężczyzna") # nieco podobnie jak nazywaliśmy elementy wektora funkcją names()
# i otrzymujemy
factor_vector_plci
## [1] Mężczyzna Kobieta   Kobieta   Mężczyzna Mężczyzna
## Levels: Kobieta Mężczyzna
# zobaczmy różnice w podsumowaniu pozornie identycznych obiektów
summary(vector_plci)
##    Length     Class      Mode 
##         5 character character
summary(factor_vector_plci)
##   Kobieta Mężczyzna 
##         2         3


‘Subtelna’ własność kategorialnej zmiennej porządkowej

gosciu <- factor_vector_plci[1]
gosciuwa <- factor_vector_plci[2]
# czy facet jest "silniejszy"/większy od kobiety? Sprawdźmy operatorem >
gosciu > gosciuwa
## Warning in Ops.factor(gosciu, gosciuwa): '>' not meaningful for factors
## [1] NA
# ale co z temperaturą? Czy średnia jest niższa niż wysoka?
srednia <- temperatura_vector[5]
wysoka <- temperatura_vector[1]
srednia < wysoka # zaiste :P
## [1] TRUE


Table częstości - zliczanie wystąpień

# factor
(kolory <- factor(c("czerwony", "niebieski","czerwony", "czerwony", "zielony", 
                   "niebieski", "czerwony", "zielony","czerwony", "niebieski")))
##  [1] czerwony  niebieski czerwony  czerwony  zielony   niebieski czerwony 
##  [8] zielony   czerwony  niebieski
## Levels: czerwony niebieski zielony
# wygląda jak tabela częstości
summary(kolory) 
##  czerwony niebieski   zielony 
##         5         3         2
# Tabela częstości - "jawnie"
table(kolory)
## kolory
##  czerwony niebieski   zielony 
##         5         3         2
# Proporcje
prop.table(table(kolory)) # ?prop.table
## kolory
##  czerwony niebieski   zielony 
##       0.5       0.3       0.2
# Procenty
prop.table(table(kolory)) * 100
## kolory
##  czerwony niebieski   zielony 
##        50        30        20


# Dane
plec <- factor(c("K", "M", "K", "M", "K", "M", "K", "M"))
kierunek <- factor(c("Info", "Info", "Mat", "Mat", "Info", "Fiz", "Mat", "Fiz"))

# Tabela krzyżowa
table(plec, kierunek)
##     kierunek
## plec Fiz Info Mat
##    K   0    2   2
##    M   2    1   1
# Z proporcjami
prop.table(table(plec, kierunek))
##     kierunek
## plec   Fiz  Info   Mat
##    K 0.000 0.250 0.250
##    M 0.250 0.125 0.125
# Proporcje w wierszach
prop.table(table(plec, kierunek), margin = 1)
##     kierunek
## plec  Fiz Info  Mat
##    K 0.00 0.50 0.50
##    M 0.50 0.25 0.25
# Proporcje w kolumnach
prop.table(table(plec, kierunek), margin = 2)
##     kierunek
## plec       Fiz      Info       Mat
##    K 0.0000000 0.6666667 0.6666667
##    M 1.0000000 0.3333333 0.3333333


Modyfikowanie faktorów

# Tworzenie faktora
oceny <- factor(c("dobry", "dostateczny", "bardzo dobry", "dobry"))

# Dodanie nowego elementu - trzeba najpierw dodać poziom!
oceny[5] <- "celujący"  # Ostrzeżenie! Powstaje NA
## Warning in `[<-.factor`(`*tmp*`, 5, value = "celujący"): invalid factor level,
## NA generated
# Prawidłowy sposób:
levels(oceny) <- c(levels(oceny), "celujący")
oceny[5] <- "celujący"
print(oceny)
## [1] dobry        dostateczny  bardzo dobry dobry        celujący    
## Levels: bardzo dobry dobry dostateczny celujący
# Usunięcie nieużywanych poziomów
oceny <- factor(c("dobry", "dostateczny", "bardzo dobry", "dobry"))
oceny <- oceny[oceny != "bardzo dobry"]  # Usunięcie elementu
print(levels(oceny))  # "bardzo dobry" nadal jest poziomem!
## [1] "bardzo dobry" "dobry"        "dostateczny"
oceny <- droplevels(oceny)  # Usunięcie nieużywanych poziomów
print(levels(oceny))
## [1] "dobry"       "dostateczny"

Dodawanie nowych wartości

kolor <- factor(c("czerwony", "niebieski"))
kolor[3] <- "zielony"  # Ostrzeżenie! Powstaje NA
## Warning in `[<-.factor`(`*tmp*`, 3, value = "zielony"): invalid factor level,
## NA generated
kolor
## [1] czerwony  niebieski <NA>     
## Levels: czerwony niebieski
# Najpierw dodaj poziom:
levels(kolor) <- c(levels(kolor), "zielony")
kolor[3] <- "zielony"  # Teraz działa


Konwersja typów

# Factor na character
(oceny <- factor(c("dobry", "dostateczny", "bardzo dobry"))); class(oceny)
## [1] dobry        dostateczny  bardzo dobry
## Levels: bardzo dobry dobry dostateczny
## [1] "factor"
oceny_text <- as.character(oceny)
print(oceny_text)
## [1] "dobry"        "dostateczny"  "bardzo dobry"
class(oceny_text)
## [1] "character"
# Factor na numeric - UWAGA!
liczby_f <- factor(c("10", "20", "5", "15"))
as.numeric(liczby_f)  # BŁĄD! Daje 1, 3, 2, 4 (indeksy poziomów!)
## [1] 1 3 4 2
# Prawidłowa konwersja:
as.numeric(as.character(liczby_f))  # Daje 10, 20, 5, 15
## [1] 10 20  5 15
# Character na factor
tekst <- c("jabłko", "gruszka", "jabłko", "śliwka")
owoce <- as.factor(tekst)
print(owoce)
## [1] jabłko  gruszka jabłko  śliwka 
## Levels: gruszka jabłko śliwka


Zmiana kolejności poziomów

# Oryginalny factor
oceny <- factor(c("dobry", "dostateczny", "bardzo dobry", "celujący"))
print(levels(oceny))  # Alfabetycznie
## [1] "bardzo dobry" "celujący"     "dobry"        "dostateczny"
# Zmiana kolejności poziomów
oceny <- factor(oceny, 
                levels = c("niedostateczny", "dostateczny", "dopuszczający",
                          "dobry", "bardzo dobry", "celujący"))
print(levels(oceny))
## [1] "niedostateczny" "dostateczny"    "dopuszczający"  "dobry"         
## [5] "bardzo dobry"   "celujący"
# Odwrócenie kolejności
oceny_odwrocone <- factor(oceny, levels = rev(levels(oceny)))
print(levels(oceny_odwrocone))
## [1] "celujący"       "bardzo dobry"   "dobry"          "dopuszczający" 
## [5] "dostateczny"    "niedostateczny"


# R domyślnie sortuje poziomy alfabetycznie
miesiace <- factor(c("Styczeń", "Luty", "Marzec"))
print(levels(miesiace))  # Alfabetycznie!
## [1] "Luty"    "Marzec"  "Styczeń"
# Określ właściwą kolejność:
miesiace <- factor(miesiace, 
                   levels = c("Styczeń", "Luty", "Marzec", "Kwiecień", 
                             "Maj", "Czerwiec", "Lipiec", "Sierpień",
                             "Wrzesień", "Październik", "Listopad", "Grudzień"))


plec <- as.factor(c("M", "K", "M", "K", "K"))
levels(plec) <- c("Kobieta","Mężczyzna") # skąd on to wie?
summary(plec)
##   Kobieta Mężczyzna 
##         3         2
levels(plec) <- c("Mężczyzna", "Kobieta") # skąd on to wie lub nie? 
summary(plec)
## Mężczyzna   Kobieta 
##         3         2
# Najlepszy sposób - od razu określ właściwą kolejność
plec <- factor(c("M", "K", "M", "K", "K"),
               levels = c("K", "M"),
               labels = c("Kobieta", "Mężczyzna"))
summary(plec)
##   Kobieta Mężczyzna 
##         3         2


# jaka jest logika przypisania?
x <- factor(c("Ź", "A", "C"))
levels(x) <- c("pierwszy", "drugi", "trzeci")
print(x)
## [1] trzeci   pierwszy drugi   
## Levels: pierwszy drugi trzeci
# Odpowiedź:
# [1] trzeci  pierwszy drugi
# Levels: pierwszy drugi trzeci

# Dlaczego?
# Alfabetycznie: "A" < "C" < "Ź" (polskie znaki na końcu!)
# Więc: A=1, C=2, Ź=3
# Po zmianie: A="pierwszy", C="drugi", Ź="trzeci"


Data frames

Data frame w R to dwuwymiarowa struktura danych, która jest jedną z najczęściej używanych do przechowywania i manipulacji danymi. Można ją porównać do tabeli w bazie danych lub arkusza kalkulacyjnego, gdzie dane są zorganizowane w wiersze i kolumny.

Kluczowe cechy:

  • Każda kolumna może mieć inny typ danych (numeric, character, logical, factor)

  • Wszystkie kolumny muszą mieć taką samą długość

  • Wiersze i kolumny mogą mieć nazwy


Okazuje się, że mamy pod ręką kilka zbiorów danych by default. Ramką, którą zna każdy R’owiec jest m.in. zestaw mtcars Obejrzyjmy dokumentację, żeby sprawdzić co zawiera: https://www.rdocumentation.org/packages/datasets/versions/3.6.2/topics/mtcars a następnie obejrzyjmy ją w konsoli

mtcars
##                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
## Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
## Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
## Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
## Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
## Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
## Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
## Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
## Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
## Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
## Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2


Zabieg ten bywa niekiedy głupotą przy większych zbiorach. Posprawdzajmy ten zbiór “rezolutniej”, odpowiednimi funkcjami.

str(mtcars) # czy to w ogóle jest data.frame?
## 'data.frame':    32 obs. of  11 variables:
##  $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
##  $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
##  $ disp: num  160 160 108 258 360 ...
##  $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
##  $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
##  $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
##  $ qsec: num  16.5 17 18.6 19.4 17 ...
##  $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
##  $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
##  $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
##  $ carb: num  4 4 1 1 2 1 4 2 2 4 ...
# można sprawdzić jeszcze inaczej
is.data.frame(mtcars)
## [1] TRUE
head(mtcars) # może pierwsze kilka wierszy, żeby nie zaśmiecać konsoli
##                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
## Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
tail(mtcars, 2)  # ostatnie 2 wiersze
##                mpg cyl disp  hp drat   wt qsec vs am gear carb
## Maserati Bora 15.0   8  301 335 3.54 3.57 14.6  0  1    5    8
## Volvo 142E    21.4   4  121 109 4.11 2.78 18.6  1  1    4    2


Wymiary data.frame

nrow(mtcars)  # liczba wierszy
## [1] 32
ncol(mtcars)  # liczba kolumn
## [1] 11
dim(mtcars)   # wymiary: [wiersze, kolumny]
## [1] 32 11


Nazwy kolumn i wierszy

# Nazwy kolumn
colnames(mtcars)
##  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
## [11] "carb"
names(mtcars)  # to samo co colnames()
##  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
## [11] "carb"
# Nazwy wierszy
rownames(mtcars)
##  [1] "Mazda RX4"           "Mazda RX4 Wag"       "Datsun 710"         
##  [4] "Hornet 4 Drive"      "Hornet Sportabout"   "Valiant"            
##  [7] "Duster 360"          "Merc 240D"           "Merc 230"           
## [10] "Merc 280"            "Merc 280C"           "Merc 450SE"         
## [13] "Merc 450SL"          "Merc 450SLC"         "Cadillac Fleetwood" 
## [16] "Lincoln Continental" "Chrysler Imperial"   "Fiat 128"           
## [19] "Honda Civic"         "Toyota Corolla"      "Toyota Corona"      
## [22] "Dodge Challenger"    "AMC Javelin"         "Camaro Z28"         
## [25] "Pontiac Firebird"    "Fiat X1-9"           "Porsche 914-2"      
## [28] "Lotus Europa"        "Ford Pantera L"      "Ferrari Dino"       
## [31] "Maserati Bora"       "Volvo 142E"

Zmiana nazw kolumn bądź wierszy

polskie_nazwy_mtcars <- c(
  "MilePerGalon",    # mpg - Zużycie paliwa (mil na galon)
  "Cylindry",        # cyl - Liczba cylindrów
  "Pojemnosc",       # disp - Pojemność skokowa silnika
  "KonieMechaniczne",# hp - Moc (koni mechanicznych)
  "PrzelozenieOsi",  # drat - Przełożenie osi
  "Waga",            # wt - Masa własna (w tysiącach funtów)
  "Czas1/4mili",     # qsec - Czas przejazdu 1/4 mili
  "SilnikVS",        # vs - Typ silnika: (0 = w kształcie V, 1 = prosty)
  "SkrzyniaBiegow",  # am - Typ skrzyni biegów: (0 = automatyczna, 1 = manualna)
  "Biegi",           # gear - Liczba biegów (przełożeń do przodu)
  "Gazniki"          # carb - Liczba gaźników
)
colnames(mtcars) <- polskie_nazwy_mtcars

rownames(mtcars)[18] <- "Taki Maluch"


Stwórzmy własny data.frame from scratch

# Tworzenie prostego data.frame
df <- data.frame(
  imie = c("Anna", "Jan", "Maria", "Piotr"),
  wiek = c(23, 25, 22, 24),
  ocena = c(4.5, 3.5, 5.0, 4.0)
)

print(df)
##    imie wiek ocena
## 1  Anna   23   4.5
## 2   Jan   25   3.5
## 3 Maria   22   5.0
## 4 Piotr   24   4.0

Albo z istniejących wektorów.

# pojedyncze wektory, które staną się zmiennymi
name <- c("Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune")

type <- c("Terrestrial planet", "Terrestrial planet", "Terrestrial planet", "Terrestrial planet", "Gas giant", "Gas giant", "Gas giant", "Gas giant")

diameter <- c(0.382, 0.949, 1, 0.532, 11.209, 9.449, 4.007, 3.883)

rotation <- c(58.64, -243.02, 1, 1.03, 0.41, 0.43, -0.72, 0.67)

rings <- c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE)

# sklejenie wektorów w data.frame
planets_df <- data.frame(name, 
                         type, 
                         diameter,
                         rotation, 
                         rings)

rm(name, type, diameter, rotation, rings) # mogę usunę zbędne już wektory ze środowiska
str(planets_df)
## 'data.frame':    8 obs. of  5 variables:
##  $ name    : chr  "Mercury" "Venus" "Earth" "Mars" ...
##  $ type    : chr  "Terrestrial planet" "Terrestrial planet" "Terrestrial planet" "Terrestrial planet" ...
##  $ diameter: num  0.382 0.949 1 0.532 11.209 ...
##  $ rotation: num  58.64 -243.02 1 1.03 0.41 ...
##  $ rings   : logi  FALSE FALSE FALSE FALSE TRUE TRUE ...
planets_df
##      name               type diameter rotation rings
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
## 3   Earth Terrestrial planet    1.000     1.00 FALSE
## 4    Mars Terrestrial planet    0.532     1.03 FALSE
## 5 Jupiter          Gas giant   11.209     0.41  TRUE
## 6  Saturn          Gas giant    9.449     0.43  TRUE
## 7  Uranus          Gas giant    4.007    -0.72  TRUE
## 8 Neptune          Gas giant    3.883     0.67  TRUE


Wybierzmy z ramki jakieś interesujące nas informacje

# znajdźmy średnicę Ziemi
planets_df[3,3] # trzeci wiersz, trzecia kolumna
## [1] 1
# czy na Marsie da się mieszkać? Nie wiem, ale przyjrzyjmy się wszystkim informacjom na jego temat, którymi dysponujemy
planets_df[4,] # czwarty wiersz
##   name               type diameter rotation rings
## 4 Mars Terrestrial planet    0.532     1.03 FALSE
# Średnice pierwszych trzech planet
planets_df[1:3, "diameter"] 
## [1] 0.382 0.949 1.000
# Średnice pierwszych trzech planet - alternatywnie
planets_df[1:3,3] # Zapytają Państwo, która metoda jest lepsza? Odp.: to zależy...
## [1] 0.382 0.949 1.000
# Cała kolumna
planets_df[, 2]   # druga kolumna
## [1] "Terrestrial planet" "Terrestrial planet" "Terrestrial planet"
## [4] "Terrestrial planet" "Gas giant"          "Gas giant"         
## [7] "Gas giant"          "Gas giant"
# Wiele wierszy lub kolumn
planets_df[1:3, ]      # wiersze 1-3
##      name               type diameter rotation rings
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
## 3   Earth Terrestrial planet    1.000     1.00 FALSE
planets_df[, c(1:3)]  # kolumny od 1 do 3
##      name               type diameter
## 1 Mercury Terrestrial planet    0.382
## 2   Venus Terrestrial planet    0.949
## 3   Earth Terrestrial planet    1.000
## 4    Mars Terrestrial planet    0.532
## 5 Jupiter          Gas giant   11.209
## 6  Saturn          Gas giant    9.449
## 7  Uranus          Gas giant    4.007
## 8 Neptune          Gas giant    3.883
planets_df[, c(1, 3)]  # kolumny 1 i 3
##      name diameter
## 1 Mercury    0.382
## 2   Venus    0.949
## 3   Earth    1.000
## 4    Mars    0.532
## 5 Jupiter   11.209
## 6  Saturn    9.449
## 7  Uranus    4.007
## 8 Neptune    3.883
planets_df[c(1, 3), c(1, 2)]  # wybrane wiersze i kolumny
##      name               type
## 1 Mercury Terrestrial planet
## 3   Earth Terrestrial planet


Wyrzuciliśmy wcześniej z workspace’u wszystkie wektory tworzące zbiór planet. Teraz jednak potrzebujemy pod ręką niektórych. Zreplikujmy je używając naszego data.frame’u

name_vector <- planets_df$name
rings_vector <- planets_df$rings


Wybieranie wierszy z uwagi na warunek

# Pani od wuefu powiedziała, że nie da się biegać na gazowych planetach, zatem na których planetach można?
planets_df[planets_df$type == "Terrestrial planet",]
##      name               type diameter rotation rings
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
## 3   Earth Terrestrial planet    1.000     1.00 FALSE
## 4    Mars Terrestrial planet    0.532     1.03 FALSE
# Oraz, że lepiej biegać na tych bez ringu ("Bo jeszcze na łeb spadnie" - cytuję)
planets_df[rings_vector == F,]
##      name               type diameter rotation rings
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
## 3   Earth Terrestrial planet    1.000     1.00 FALSE
## 4    Mars Terrestrial planet    0.532     1.03 FALSE
# czyli na obu nie bardzo?
planets_df[planets_df$rings != T & planets_df$type != "Gas giant",] # omówmy
##      name               type diameter rotation rings
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
## 3   Earth Terrestrial planet    1.000     1.00 FALSE
## 4    Mars Terrestrial planet    0.532     1.03 FALSE
# które planety mają średnicę mniejszą niż ziemska?
subset(planets_df, subset = diameter <1)
##      name               type diameter rotation rings
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
## 4    Mars Terrestrial planet    0.532     1.03 FALSE
# nasza ramka jest ułożona w porządku występowania względem Słońca, ustawmy ją w kolejności leksykalnej
pozycja <-  order(planets_df$name)
planets_df[pozycja,]
##      name               type diameter rotation rings
## 3   Earth Terrestrial planet    1.000     1.00 FALSE
## 5 Jupiter          Gas giant   11.209     0.41  TRUE
## 4    Mars Terrestrial planet    0.532     1.03 FALSE
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 8 Neptune          Gas giant    3.883     0.67  TRUE
## 6  Saturn          Gas giant    9.449     0.43  TRUE
## 7  Uranus          Gas giant    4.007    -0.72  TRUE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
# albo względem wielkości. Która zmienna odpowiada za "gabaryty" planety?
pozycja <-  order(planets_df$diameter)
planets_df[pozycja,]
##      name               type diameter rotation rings
## 1 Mercury Terrestrial planet    0.382    58.64 FALSE
## 4    Mars Terrestrial planet    0.532     1.03 FALSE
## 2   Venus Terrestrial planet    0.949  -243.02 FALSE
## 3   Earth Terrestrial planet    1.000     1.00 FALSE
## 8 Neptune          Gas giant    3.883     0.67  TRUE
## 7  Uranus          Gas giant    4.007    -0.72  TRUE
## 6  Saturn          Gas giant    9.449     0.43  TRUE
## 5 Jupiter          Gas giant   11.209     0.41  TRUE

Dodanie nowej kolumny

planets_df$DaSieMieszkac <- c(F,F,T,T,F,F,F,F)

Dodanie nowego wiersza

nowa_planeta <- data.frame(
  name = "Maluch's Best Planet",
  type = "Terrestrial planet",
  diameter = 1.000,
  rotation = 1.00,
  rings = T,
  DaSieMieszkac = T
)

planets_df <- rbind(planets_df, nowa_planeta)

Modyfikacja wartości

planets_df[-9, "rings"] <- FALSE

Usunięcie kolumny lub wiersza

planets_df$DaSieMieszkac <- NULL

planets_df <- planets_df[-9, ]

Przydatne

# Unikalne wartości
unique(planets_df$type)
## [1] "Terrestrial planet" "Gas giant"
# Liczba unikalnych wartości
length(unique(planets_df$rings))
## [1] 1

Podsumowania i statystyki

# Podsumowanie statystyczne
summary(mtcars)
##   MilePerGalon      Cylindry       Pojemnosc     KonieMechaniczne
##  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0   
##  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5   
##  Median :19.20   Median :6.000   Median :196.3   Median :123.0   
##  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7   
##  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0   
##  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0   
##  PrzelozenieOsi       Waga        Czas1/4mili       SilnikVS     
##  Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  
##  1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  
##  Median :3.695   Median :3.325   Median :17.71   Median :0.0000  
##  Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  
##  3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  
##  Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  
##  SkrzyniaBiegow       Biegi          Gazniki     
##  Min.   :0.0000   Min.   :3.000   Min.   :1.000  
##  1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
##  Median :0.0000   Median :4.000   Median :2.000  
##  Mean   :0.4062   Mean   :3.688   Mean   :2.812  
##  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :1.0000   Max.   :5.000   Max.   :8.000
# Statystyki dla konkretnych kolumn
mean(mtcars$cyl)     # średnia liczba cylindrów
## Warning in mean.default(mtcars$cyl): argument is not numeric or logical:
## returning NA
## [1] NA
median(mtcars$hp)  # mediana liczby koni mechanicznych
## NULL
sd(mtcars$wt)       # odchylenie standardowe masy własnej
## [1] NA
min(mtcars$gear)     # minimum liczby biegów
## Warning in min(mtcars$gear): no non-missing arguments to min; returning Inf
## [1] Inf
max(mtcars$carb)      # maksimum liczny gaźników
## Warning in max(mtcars$carb): no non-missing arguments to max; returning -Inf
## [1] -Inf
# Tabele częstości
table(mtcars$cyl)
## < table of extent 0 >



Zadania przy tablicy

  1. Stwórz data.frame ksiazki z informacjami o 5 ulubionych książkach (tytuł, autor, rok wydania, ocena)
ksiazki <- data.frame(
  tytul = c(
    "Mistrz i Małgorzata",
    "1984",
    "Wiedźmin: Ostatnie życzenie",
    "Harry Potter i Kamień Filozoficzny",
    "Hobbit"
  ),
  autor = c(
    "Michaił Bułhakow",
    "George Orwell",
    "Andrzej Sapkowski",
    "J.K. Rowling",
    "J.R.R. Tolkien"
  ),
  rok_wydania = c(1967, 1949, 1993, 1997, 1937),
  ocena = c(4.8, 4.5, 4.7, 4.9, 4.6)
)
  1. Dodaj kolumnę z informacją czy książka została przeczytana (TRUE/FALSE)
ksiazki$przeczytana <- c(TRUE, TRUE, TRUE, FALSE, TRUE)
  1. Wyfiltruj książki wydane po 1995 roku
ksiazki_po_2010 <- ksiazki[ksiazki$rok_wydania > 1995, ]

ksiazki_po_2010_alt <- subset(ksiazki, rok_wydania > 1995)
  1. Posortuj książki według oceny malejąco
ksiazki_posortowane <- ksiazki[order(ksiazki$ocena, decreasing = TRUE), ]

ksiazki_posortowane_alt <- ksiazki[order(-ksiazki$ocena), ]
# Przypomnienie sort() vs order()
# Nasze oceny
ksiazki$ocena
# [1] 4.8 4.5 4.7 4.9 4.6

# sort() daje tylko wartości
sort(ksiazki$ocena, decreasing = TRUE)
# [1] 4.9 4.8 4.7 4.6 4.5

# order() daje pozycje
order(ksiazki$ocena, decreasing = TRUE)
# [1] 4 1 3 5 2
# Czyli: wiersz 4 (Harry Potter) ma najwyższą ocenę (4.9)
  1. Oblicz średnią ocenę przeczytanych książek
# Filtrujemy tylko przeczytane książki
przeczytane <- ksiazki[ksiazki$przeczytana == TRUE, ]

# Obliczamy średnią
(srednia_ocena_przeczytanych <- mean(przeczytane$ocena))
## [1] 4.65
  1. Oblicz liczbę przeczytanych książek
nrow(ksiazki[ksiazki$przeczytana == TRUE, ])
## [1] 4
  1. Który tytuł otrzymał najwyższą ocenę?
ksiazki$tytul[max(ksiazki$ocena)]
## [1] "Harry Potter i Kamień Filozoficzny"



Lists

Lista to najbardziej elastyczna struktura danych w R. W przeciwieństwie do wektorów, które mogą zawierać tylko elementy tego samego typu, lista może przechowywać:

  • różne typy danych (liczby, tekst, wartości logiczne)

  • obiekty o różnych długościach

  • inne struktury danych (wektory, macierze, ramki danych)

  • nawet inne listy!

Listy są szczególnie przydatne, gdy chcemy zgrupować różnorodne informacje w jeden obiekt.


Tworzenie list

Podstawowa składnia. Do tworzenia list używamy funkcji list():

# Prosta lista z różnymi typami danych
moja_lista <- list(
  imie = "Anna",
  wiek = 25,
  student = TRUE,
  oceny = c(4.5, 5.0, 4.0, 5.0)
)

moja_lista
## $imie
## [1] "Anna"
## 
## $wiek
## [1] 25
## 
## $student
## [1] TRUE
## 
## $oceny
## [1] 4.5 5.0 4.0 5.0


Lista bez nazw

Elementy listy nie muszą mieć nazw:

lista_bez_nazw <- list(
  "Warszawa",
  500000,
  c("Polska", "Europa")
)

lista_bez_nazw
## [[1]]
## [1] "Warszawa"
## 
## [[2]]
## [1] 5e+05
## 
## [[3]]
## [1] "Polska" "Europa"


Zagnieżdżone listy

Lista może zawierać inne listy:

osoba <- list(
  dane_osobowe = list(
    imie = "Jan",
    nazwisko = "Kowalski",
    wiek = 30
  ),
  adres = list(
    miasto = "Kraków",
    kod_pocztowy = "30-001"
  )
)

osoba
## $dane_osobowe
## $dane_osobowe$imie
## [1] "Jan"
## 
## $dane_osobowe$nazwisko
## [1] "Kowalski"
## 
## $dane_osobowe$wiek
## [1] 30
## 
## 
## $adres
## $adres$miasto
## [1] "Kraków"
## 
## $adres$kod_pocztowy
## [1] "30-001"


Dostęp do elementów listy

Istnieją trzy główne sposoby odwoływania się do elementów listy:

  1. Operator $ (dla nazwanych elementów)
# Najczęściej używany sposób
moja_lista$imie
## [1] "Anna"
moja_lista$oceny
## [1] 4.5 5.0 4.0 5.0
  1. Operator [[]] (zwraca element)
# Przez nazwę
moja_lista[["wiek"]]
## [1] 25
# Przez indeks
moja_lista[[1]]
## [1] "Anna"
  1. Operator [] (zwraca pod-listę)
# Zwraca listę z jednym elementem
moja_lista[1]
## $imie
## [1] "Anna"
# Różnica między [] a [[]]
class(moja_lista[1])    # lista
## [1] "list"
class(moja_lista[[1]])  # character
## [1] "character"


Dostęp do zagnieżdżonych elementów

# Sposób 1: łańcuchowy operator $
osoba$dane_osobowe$imie
## [1] "Jan"
# Sposób 2: wielokrotne [[]]
osoba[["adres"]][["miasto"]]
## [1] "Kraków"


Modyfikacja list

  1. Zmiana wartości
moja_lista$wiek <- 26
moja_lista$wiek
## [1] 26
  1. Dodawanie nowych elementów
moja_lista$kierunek <- "Statystyka"
moja_lista$rok_studiow <- 3

# Sprawdzamy zaktualizowaną listę
names(moja_lista)
## [1] "imie"        "wiek"        "student"     "oceny"       "kierunek"   
## [6] "rok_studiow"
  1. Usuwanie elementów
moja_lista$rok_studiow <- NULL
names(moja_lista)
## [1] "imie"     "wiek"     "student"  "oceny"    "kierunek"


Podstawowe informacje

# Nazwy elementów
names(moja_lista)
## [1] "imie"     "wiek"     "student"  "oceny"    "kierunek"
# Długość listy (liczba elementów)
length(moja_lista)
## [1] 5
# Struktura listy
str(moja_lista)
## List of 5
##  $ imie    : chr "Anna"
##  $ wiek    : num 26
##  $ student : logi TRUE
##  $ oceny   : num [1:4] 4.5 5 4 5
##  $ kierunek: chr "Statystyka"


Przekształcanie list

  1. Lista liczb na wektor
liczby_lista <- list(a = 1, b = 2, c = 3)
liczby_lista
## $a
## [1] 1
## 
## $b
## [1] 2
## 
## $c
## [1] 3
wektor <- unlist(liczby_lista)
wektor
## a b c 
## 1 2 3
  1. Lista na ramkę danych (gdy elementy mają tę samą długość)
dane_lista <- list(
  imie = c("Anna", "Jan", "Ewa"),
  wiek = c(25, 30, 28),
  student = c(TRUE, FALSE, TRUE)
)
dane_lista
## $imie
## [1] "Anna" "Jan"  "Ewa" 
## 
## $wiek
## [1] 25 30 28
## 
## $student
## [1]  TRUE FALSE  TRUE
df <- as.data.frame(dane_lista)
df
##   imie wiek student
## 1 Anna   25    TRUE
## 2  Jan   30   FALSE
## 3  Ewa   28    TRUE


Przechowywanie wyników analizy

# Symulacja danych
set.seed(123)
dane <- rnorm(100, mean = 50, sd = 10)

# Zapisanie różnych statystyk w jednej liście
analiza <- list(
  dane_surowe = dane,
  statystyki = list(
    srednia = mean(dane),
    mediana = median(dane),
    odch_std = sd(dane)
  ),
  histogram = hist(dane, plot = FALSE)
)

# Łatwy dostęp do wyników
analiza$statystyki$srednia
## [1] 50.90406


Przechowywanie modeli statystycznych

# Tworzenie danych
x <- 1:10
y <- 2 * x + rnorm(10)

# Model liniowy zwraca listę!
model <- lm(y ~ x)

# Możemy łatwo wyciągać informacje
class(model)
## [1] "lm"
names(model)
##  [1] "coefficients"  "residuals"     "effects"       "rank"         
##  [5] "fitted.values" "assign"        "qr"            "df.residual"  
##  [9] "xlevels"       "call"          "terms"         "model"
model$coefficients
## (Intercept)           x 
##  -0.4856184   2.0163219

Podsumowanie

Listy w R to potężne narzędzie, które pozwala:

  • Grupować różnorodne dane w jedną strukturę
  • Przechowywać złożone wyniki analiz
  • Organizować dane hierarchicznie
  • Pracować z wynikami funkcji statystycznych

Kluczowe operacje:

  • Tworzenie: list(nazwa1 = wartość1, nazwa2 = wartość2)
  • Dostęp: lista$nazwa, lista[[indeks]], lista[indeks]
  • Modyfikacja: lista$nowy_element <- wartość
  • Informacje: names(), length(), str(), etc.




Funkcje z pakietu base


Poniżej omówienie kilku przydatnych i popularnych funkcji dostępnych od razu po zainstalowaniu R.



Funkcja ls() pokazuje nam wszystkie obiekty w środowisku pracy (workspace)

ls()
##   [1] "analiza"                     "concat_wektorow"            
##   [3] "czestosc_orla"               "dane"                       
##   [5] "dane_lista"                  "dane_norm"                  
##   [7] "dane_std"                    "df"                         
##   [9] "dlugosci"                    "factor_sex_vector"          
##  [11] "factor_temperatura_vector"   "factor_vector_plci"         
##  [13] "filozofia"                   "gosciu"                     
##  [15] "gosciuwa"                    "id"                         
##  [17] "id2"                         "imiona"                     
##  [19] "imiona_posortowane"          "imiona_posortowane_malejaco"
##  [21] "imiona_sorted"               "imiona_sorted_desc"         
##  [23] "imiona_studentow"            "kierunek"                   
##  [25] "kolejnosc_rosnaco"           "kolor"                      
##  [27] "kolory"                      "ksiazki"                    
##  [29] "ksiazki_po_2010"             "ksiazki_po_2010_alt"        
##  [31] "ksiazki_posortowane"         "ksiazki_posortowane_alt"    
##  [33] "liczba_orlow"                "liczba_reszek"              
##  [35] "liczby"                      "liczby_1_100"               
##  [37] "liczby_f"                    "liczby_lista"               
##  [39] "lista_bez_nazw"              "losowe_liczby"              
##  [41] "macierz_przyklad"            "matematyka"                 
##  [43] "miesiace"                    "model"                      
##  [45] "moja.zmienna"                "moja_dokladna_liczba"       
##  [47] "moja_liczba"                 "moja_lista"                 
##  [49] "moja_wart_logiczna"          "moja_wart_logiczna2"        
##  [51] "moja_zmienna"                "MojaKlasa"                  
##  [53] "mojaZmienna"                 "moje_slowo"                 
##  [55] "mtcars"                      "name_vector"                
##  [57] "nazwa_przedmiotu"            "nowa_planeta"               
##  [59] "oceny"                       "oceny_matrix"               
##  [61] "oceny_matrix2"               "oceny_odwrocone"            
##  [63] "oceny_stat_R"                "oceny_text"                 
##  [65] "osoba"                       "owoce"                      
##  [67] "planets_df"                  "plec"                       
##  [69] "podzielne_przez_7"           "podzielne_przez_7_v2"       
##  [71] "polskie_nazwy_mtcars"        "pozycja"                    
##  [73] "przeczytane"                 "rings_vector"               
##  [75] "rzuty"                       "rzuty_moneta"               
##  [77] "rzuty_moneta2"               "sekwencjaNumericow"         
##  [79] "sekwencjaNumericow2"         "sekwencjaNumericow3"        
##  [81] "semestry"                    "sex_vector"                 
##  [83] "sprzedaz"                    "srednia"                    
##  [85] "srednia_ocen"                "srednia_ocena_przeczytanych"
##  [87] "srednia_semestr"             "stat_R"                     
##  [89] "suma_wektorow"               "szesc"                      
##  [91] "szpital"                     "tablica_przyklad"           
##  [93] "tekst"                       "temperatura"                
##  [95] "temperatura_vector"          "trufolsy"                   
##  [97] "vector_plci"                 "warunki"                    
##  [99] "wektor"                      "wektor_1"                   
## [101] "wektor_2"                    "wektor_z_brakami"           
## [103] "wiek"                        "wieksze_od_sredniej"        
## [105] "wych_fiz"                    "wyniki"                     
## [107] "wysoka"                      "x"                          
## [109] "y"                           "z"                          
## [111] "za_co"                       "zdali"

Funkcja rm() usuwa poszczególne obiekty ze środowiska

rm(oceny_matrix)

Kombinacja powyższych funkcji czyści cały workspace

rm(list=ls()) 

Można to również “wyklikać” w RStudio, co za moment pokażę.

Gdyby nam zależało na pozostawieniu pojedynczych obiektów?

rm(list=setdiff(ls(), "oceny_stat_R"))



Funkcja gc() w R jest używana do zarządzania pamięcią poprzez przeprowadzanie tzw. garbage collection (zbieranie śmieci). gc() oczyszcza pamięć, usuwając nieużywane obiekty, co może zwolnić zasoby systemowe. Po wykonaniu, funkcja zwraca raport o aktualnym zużyciu pamięci, co może być przydatne do monitorowania i optymalizacji kodu. Rekomenduje się używać jej po usunięciu dużych obiektów ze środowiska (czasem już ich nie widać, a zalegają nam gdzieś w RAMie).

gc()
##           used (Mb) gc trigger (Mb) max used (Mb)
## Ncells  609957 32.6    1546431 82.6  1546431 82.6
## Vcells 1181930  9.1    8388608 64.0  6601446 50.4



Wtręt. Wyłączanie notacji naukowej (scientific notation)

# przykład notacji wykładniczej
5834617 * 10^5
## [1] 583461700000
# i naukowej
5.834617 * 10^5  == 5.834617e+5 
## [1] TRUE
5.834617e+5 == 583461.7
## [1] TRUE
# jakieś random'owe liczby (Funkcja exp() w R służy do obliczania wartości wykładniczej liczby e (około 2.71828) podniesionej do potęgi podanego argumentu)

exp(-42) # super  mała, w notacji naukowej
## [1] 5.749522e-19
exp(42) # super duża, w notacji naukowej
## [1] 1.739275e+18
# Jeśli uruchomimy
options(scipen = 15) # opcja systemowa scipen (scientific penalty)

# to wówczas w konsoli pojawią nam się inne wyświetlenia
exp(-42) # super  mała, pozbawiona notacji
## [1] 0.0000000000000000005749522
exp(42) # super duża, analogicznie
## [1] 1739274941520500992



Wtręt. Zmniejszenie wyświetlania liczby znaków

Jeśli chcemy co do zasady w konsoli widzieć mniej znaków (i tym samym miejsc po przecinku), możemy osiągnąć to używając poniższej funkcji systemowe

pi # przykład obiektu, który istnieje bez naszej ingerencji, na podobnej zasadzie co np.`letters`
## [1] 3.141593
# albo nie istnieje ale stworzyliśmy
(e <- exp(1))
## [1] 2.718282
options(digits = 3) # zmieniamy opcje systemową

# wówczas
pi # znane nam "czyczternaście"
## [1] 3.14
# albo
e
## [1] 2.72



Funkcje floor(x) i ceiling(x) zaokrąglają do najbliższej wartości całkowitej odpowiednio w dół lub w górę. Poniżej przykłady.

floor(1.23456789)
## [1] 1
floor(-1.23456789)
## [1] -2
ceiling(1.23456789)
## [1] 2
ceiling(-1.23456789)
## [1] -1



Funkcja choose(n,x) w R służy do obliczania współczynnika dwumianowego, czyli liczby sposobów, w jakie można wybrać (k) elementów z (n) elementów bez uwzględniania kolejności.

# Liczba sposobów na wybranie 2 elementów z 5
choose(5, 2)
## [1] 10
# chcąc sprawdzić jakie to będą pary, można posłużyć się takim kodem
# Zbiór elementów
elements <- c(1, 2, 3, 4, 5) # pięć elementów

# Generowanie wszystkich kombinacji 2-elementowych
combinations <- combn(elements, 2)
print(combinations)
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,]    1    1    1    1    2    2    2    3    3     4
## [2,]    2    3    4    5    3    4    5    4    5     5

Funkcja factorial() w R służy do obliczania silni liczby. Silnia liczby (n) (oznaczana jako (n! )) to iloczyn wszystkich liczb całkowitych od 1 do (n).

# Obliczanie silni liczby 5
factorial(5)
## [1] 120
# tak nie, wykrzyknik służył do negacji
5!

Nie znając funkcji choose(), a znając factorial() i wzory, można np. samemu policzyć…

# choose(n = 5, k = 2)

factorial(5) / (factorial(2) * factorial(5-2))
## [1] 10



Funkcja trunc() w R służy do obcinania części dziesiętnej liczby, czyli usuwania wszystkiego, co znajduje się po przecinku, i zwracania liczby całkowitej najbliższej zeru. Na przykład, liczba dodatnia zostanie zaokrąglona w dół, a liczba ujemna w górę

# Obcinanie części dziesiętnej liczby dodatniej
trunc(pi)
## [1] 3
# Obcinanie części dziesiętnej liczby ujemnej
trunc(-e)
## [1] -2



Funkcja runif(n) generuje n losowych liczb z rozkładu jednostajnego (uniform distribution). W rozkładzie jednostajnym każda wartość w określonym przedziale ma jednakowe prawdopodobieństwo wystąpienia. Używana do generowania pseudolosowych liczb w określonym przedziale, co jest przydatne w symulacjach, testach statystycznych i innych analizach wymagających losowych danych.

Składnia: runif(n, min = 0, max = 1) * n: Liczba losowych wartości do wygenerowania. * min: Minimalna wartość przedziału (domyślnie 0). * max: Maksymalna wartość przedziału (domyślnie 1).

# Generowanie 1000 losowych liczb z przedziału 0 do 100
runif_result <- runif(10^3, min = 0, max = 100)
# rezultat
hist(runif_result)



Funkcja rnorm(n) generuje n losowych liczb z rozkładu normalnego (Gaussian distribution). Rozkład normalny jest symetryczny i ma kształt dzwonu (kapelusza, słonia w wężu), z wartościami skupionymi wokół średniej. Używana do generowania pseudolosowych liczb, które mają rozkład normalny, co jest przydatne w analizach statystycznych, modelowaniu i symulacjach, gdzie dane często przyjmują rozkład normalny.

Składnia: rnorm(n, mean = 0, sd = 1) * n: Liczba losowych wartości do wygenerowania. * mean: Średnia rozkładu (domyślnie 0). * sd: Odchylenie standardowe rozkładu (domyślnie 1).

# Generowanie 1000 losowych liczb z rozkładu normalnego o średniej 10 i odchyleniu standardowym 2
rnorm_result <- rnorm(1000, mean = 10, sd = 2)
# rezultat
hist(rnorm_result)



Do szybkiego stworzenia prostych statystyk opisowych możemy posłużyć się następującymi funkcjami:

  • max() maksimum
max(rnorm_result) # nasz pseudolosowy wektor z poprzedniego przykładu
## [1] 16.8
max(runif_result) # nasz pseudolosowy wektor z poprzedniego przykładu
## [1] 99.9
  • min() minimum
min(rnorm_result)
## [1] 4.61
min(runif_result)
## [1] 0.0465
  • sum() suma
sum(rnorm_result)
## [1] 10053
sum(runif_result)
## [1] 49524
  • mean() średnia
mean(rnorm_result)
## [1] 10.1
mean(runif_result)
## [1] 49.5
  • median() mediana
median(rnorm_result)
## [1] 10.1
median(runif_result)
## [1] 49.2
  • range() liczy zakres wartości vide minimum i maksimum.
range(rnorm_result)
## [1]  4.61 16.78
range(runif_result)
## [1]  0.0465 99.9405
  • quantile() służy do obliczania kwantyli dla danego wektora danych.

Kwantyle to wartości, które dzielą uporządkowany zbiór danych na równe części. Używane do opisu rozkładu danych, zrozumienia struktury danych, identyfikacji wartości odstających i porównywania różnych zbiorów danych.

Argumenty: * x: Wektor danych, dla którego chcemy obliczyć kwantyle. * probs: Wektor wartości prawdopodobieństwa, które określają, jakie kwantyle mają być obliczone (domyślnie kwartyle: 0, 0.25, 0.5, 0.75, 1). * na.rm: Jeśli ustawione na TRUE, pomija wartości NA w obliczeniach (domyślnie FALSE).

# domyślnie liczy kwartyle, gdy wywołujemy funkcję bez dodatkowych argumentów
quantile(rnorm_result) 
##    0%   25%   50%   75%  100% 
##  4.61  8.66 10.11 11.33 16.78
quantile(runif_result)
##      0%     25%     50%     75%    100% 
##  0.0465 25.2519 49.2430 74.7835 99.9405
# można również percentyle
quantile(rnorm_result, probs = seq(0, 1, 0.1), na.rm = TRUE) 
##    0%   10%   20%   30%   40%   50%   60%   70%   80%   90%  100% 
##  4.61  7.47  8.35  9.02  9.63 10.11 10.60 11.09 11.68 12.56 16.78
quantile(runif_result, probs = seq(0, 1, 0.1), na.rm = FALSE)
##      0%     10%     20%     30%     40%     50%     60%     70%     80%     90% 
##  0.0465  9.6167 19.3266 29.7297 39.5800 49.2430 58.7086 69.9259 79.8087 88.7452 
##    100% 
## 99.9405


  • sd() w R służy do obliczania odchylenia standardowego wektora danych.

Odchylenie standardowe mierzy, jak bardzo wartości w zbiorze danych różnią się od średniej, ale w tych samych jednostkach co oryginalne dane.

(x <- c(1, 2, 3, 4, 5)) # wektor
## [1] 1 2 3 4 5
sd(x)
## [1] 1.58
sd(rnorm_result)
## [1] 2
sd(runif_result)
## [1] 28.8
  • var() wariancja

Wariancja mierzy średnią kwadratową odległość wartości w zbiorze danych od ich średniej. Jest to miara rozproszenia danych.

(x <- c(1, 2, 3, 4, 5)) # wektor
## [1] 1 2 3 4 5
var(x)
## [1] 2.5
sd(x)^2
## [1] 2.5
var(rnorm_result)
## [1] 4
var(runif_result)
## [1] 829
  • cumsum() służy do obliczania skumulowanej sumy elementów wektora. Oznacza to, że każdy element wynikowego wektora jest sumą wszystkich poprzednich elementów oryginalnego wektora do tego punktu włącznie.
(x <- c(1, 2, 3, 4, 5)) # wektor
## [1] 1 2 3 4 5
cumsum(x) # skumulowana suma wektora
## [1]  1  3  6 10 15
  • cumprod() służy do obliczania skumulowanego iloczynu elementów wektora. Oznacza to, że każdy element wynikowego wektora jest iloczynem wszystkich poprzednich elementów oryginalnego wektora do tego punktu włącznie.
x <- c(2, 3, 4, 5) # wektor
cumprod(x) # skumulowany iloczyn wektora
## [1]   2   6  24 120
  • cummax()
(x <- c(1, 2, 3, 4, 5)) # wektor
## [1] 1 2 3 4 5
cummax(x)
## [1] 1 2 3 4 5
  • cummin()
(x <- c(1, 2, 3, 4, 5)) # wektor
## [1] 1 2 3 4 5
cummin(x)
## [1] 1 1 1 1 1

Pytanie do Państwa, co zrobiły powyższe dwie funkcje? Widzicie Państwo jakieś zastosowanie wyników?



  • cor() korelacja

Funkcja cor() oblicza współczynnik korelacji między dwoma wektorami danych. Korelacja mierzy siłę i kierunek związku między dwiema zmiennymi. Składnia: cor(x, y = NULL, use = "everything", method = c("pearson", "kendall", "spearman")) * x: Wektor numeryczny lub macierz. * y: Opcjonalnie, drugi wektor numeryczny (jeśli nie podano, używa x). * use: Metoda obsługi wartości NA. * method: Metoda korelacji (“pearson” domyślnie, “kendall”, “spearman”).

(x <- c(1, 2, 3, 4, 5)); (y <- 3*x)
## [1] 1 2 3 4 5
## [1]  3  6  9 12 15
cor(x, y)
## [1] 1
# idealnie!
cor(rnorm_result, runif_result)
## [1] 0.0385
# jakiś komentarz co do wyniku?



Dwie przydatne funkcje: sort() & order(). Przydatne np. chcąc wyświetlić posortowaną zmienną (kolumnę) w tabeli lub wymusić inne niż domyślne rysowanie elementów na osiach wykresu.

# Stwórzmy szybko przykładowy wektor trójelementowy
(a <- c(100, 10, 1000))
## [1]  100   10 1000
order(a)
## [1] 2 1 3
sort(a)
## [1]   10  100 1000
a[order(a)] # można posortować wektor indeksując go funkcją order(), da taki sam rezultat jak sort()
## [1]   10  100 1000
a # w środku dotychczasowa kolejność
## [1]  100   10 1000
# chcąc mieć na stałe posortowany wektor musimy go sobie nadpisać
(a <- a[order(a)])
## [1]   10  100 1000

Można jeszcze skorzystać z funkcji rank(). w R służy do przypisywania rang wartościom w wektorze. Jest to przydatne, gdy chcesz uporządkować dane według ich wartości, ale zamiast sortować je bezpośrednio, przypisujesz im rangi.

(a <- c(100, 10, 1000))
## [1]  100   10 1000
rank(a)
## [1] 2 1 3

Nieco bardziej skomplikowana sytuacja.

(a <- c(100, 10, 1000, 10, NA))
## [1]  100   10 1000   10   NA
rank(a)
## [1] 3.0 1.5 4.0 1.5 5.0

Wówczas warto sięgnąć po większą liczbę argumentów tej funkcji. rank(x, na.last = TRUE, ties.method = c("average", "first", "last", "random", "max", "min")) gdzie:

  • x: Wektor numeryczny, złożony, znakowy lub logiczny, którego rangi chcesz obliczyć.
  • na.last: Określa, jak traktować wartości NA. Może być TRUE (domyślnie, NA na końcu), FALSE (NA na początku), NA (usuwa NA) lub “keep” (zachowuje NA z rangą NA).
  • ties.method: Określa sposób traktowania remisów (wartości równych). Opcje to:
    • "average": Przypisuje średnią rangę dla remisów (domyślnie).
    • "first": Przypisuje rangi według pierwszego wystąpienia.
    • "last": Przypisuje rangi według ostatniego wystąpienia.
    • "random": Przypisuje rangi losowo.
    • "max": Przypisuje maksymalną rangę dla remisów.
    • "min": Przypisuje minimalną rangę dla remisów.



  • seq(a,b,c)
  • rep(x, n)



Funkcje paste() i cat() w R służą do łączenia (konkatenacji) ciągów znaków, ale mają różne zastosowania i zachowania.

Kluczowe różnice:

  • paste() zwraca wartość, cat() tylko wyświetla wynik
  • paste() domyślnie dodaje spację, cat() nie dodaje separatora, chyba że jest to określone

Poniżej kilka przykładów użycia.

tekst <- paste("Ta książka Vonnegut'a to bodaj \"Kocia kołyska\"!"); tekst
## [1] "Ta książka Vonnegut'a to bodaj \"Kocia kołyska\"!"
cat(tekst) # nie ma znaków specjalnych, wyłączających inne znaki specjalne
## Ta książka Vonnegut'a to bodaj "Kocia kołyska"!
tekst2 <- paste('Ta książka Vonnegut\'a to bodaj \"Kocia kołyska\"!'); tekst # to samo co powyżej, ale zamiana cudzysłowu na apostrof
## [1] "Ta książka Vonnegut'a to bodaj \"Kocia kołyska\"!"
cat(tekst2)
## Ta książka Vonnegut'a to bodaj "Kocia kołyska"!
tekst==tekst2
## [1] TRUE
# można ręcznie łamać tekst (\n)
tekst3 <- paste("Ta książka Vonnegut'a to bodaj \n\"Kocia kołyska\"\n!"); tekst3
## [1] "Ta książka Vonnegut'a to bodaj \n\"Kocia kołyska\"\n!"
cat(tekst3) # cat() znów printuje ładny output (prof Bralczyk by mnie odstrzelił za te makaronizmy)
## Ta książka Vonnegut'a to bodaj 
## "Kocia kołyska"
## !
# a można o tym zapomnieć i po prostu klikać Enter pisząc kod
tekst4 <- paste("Ta książka Vonnegut'a to bodaj 
\"Kocia kołyska\"
!"); tekst4
## [1] "Ta książka Vonnegut'a to bodaj \n\"Kocia kołyska\"\n!"
cat(tekst4)
## Ta książka Vonnegut'a to bodaj 
## "Kocia kołyska"
## !
tekst3 == tekst4
## [1] TRUE
# po prostu konkatenacja wytworzonych przed momentem wektorów
paste(tekst, tekst2, tekst3, tekst4)
## [1] "Ta książka Vonnegut'a to bodaj \"Kocia kołyska\"! Ta książka Vonnegut'a to bodaj \"Kocia kołyska\"! Ta książka Vonnegut'a to bodaj \n\"Kocia kołyska\"\n! Ta książka Vonnegut'a to bodaj \n\"Kocia kołyska\"\n!"
cat(tekst, tekst2, tekst3, tekst4)
## Ta książka Vonnegut'a to bodaj "Kocia kołyska"! Ta książka Vonnegut'a to bodaj "Kocia kołyska"! Ta książka Vonnegut'a to bodaj 
## "Kocia kołyska"
## ! Ta książka Vonnegut'a to bodaj 
## "Kocia kołyska"
## !

Sugerowałbym, z uwagi na bardziej eleganckie wyświetlenie napisów, używać cat() np. do wyświetlania komunikatów użytkownikowi.

Dodatkowo, istnieje funkcja paste0(), bliźniacza do paste() z nieco innymi domyślnymi argumentami. Potrafiliby Państwo powiedzieć czym się różnią? Można sprawdzić empirycznie, można użyć dokumentacji obu funkcji. Nie wpisujcie Państwo proszę frazy w Google czy prompt jakiegoś modelu językowego, zróbcie to oldschool’owo.


Bywa, że chcemy szybko podejrzeć zawartość ramki danych lub wektora. Wiemy, że jest długi i print trwałby jakiś czas i zamazał de facto obraz w konsoli. Wówczas możemy posłużyć się funkcjami head(x,n) oraz tail(x,n). Pierwsza wyświetli tyle pierwszych elementów ile podamy w argumencie n, druga tyleż samo tyle że od końca.



Funkcje można dowolnie w sobie zagnieżdżać, co już w sumie po poprzednich przykładach dało się intuicyjnie wyczuć.

# przykład 1
exp(floor(42.345)) - exp(sum(20,20)+2)
## [1] 0
# przykład 2
abs(-round(123 * exp(sqrt(log(3 ** 2 - 1 + 3 ** (2 - 1) / 3, base = 2))), -1))
## [1] 730



Skróty klawiczowe

  • Ctrl + ENTER odpalenie linijki kodu (albo wielu, jeśli to większy element tego samego polecenia/ funkcji)
  • Ctrl + L wyczyszczenie treści okna konsoli
  • Shift + Alt + strzałka góra lub dół skopiowanie linii kodu nad, bądź pod kopiowaną linią
  • Ctrl + 1 przenosi kursor do skryptu a Ctrl + 2 do konsoli
  • Ctrl + Shift + C komentarze (haszowanie linijki kodu)
  • Shift + Alt + J spis treści otwartego skryptu, markdown’a
  • Ctrl + Shift + K knitowanie pliku .rmd do wybranego formatu
  • Ctrl + Alt + I nowy chunk w markdown

Ogólnie warto zajrzeć w Tools -> Keybords Shortcuts Help (Alt + Shift + K)

“Gdzie kliknąć”
“Gdzie kliknąć”



Źródła i inspiracje