zrozumieć podstawowe typy danych w
R oraz różnicę między typem a klasą
obiektu (typeof() vs
class()),
oswoić się z automatyczną konwersją typów (koercją) oraz
wartościami specjalnymi NA, NaN,
Inf, NULL,
zrozumieć, skąd wynika problem z porównywaniem liczb zmiennoprzecinkowych i nauczyć się robić to bezpiecznie,
nauczyć się wywoływać funkcje z argumentami nazwanymi i
domyślnymi oraz oswoić się z korzystaniem z dokumentacji
(?nazwa_funkcji) R,
zacząć swobodnie pracować z wektorami – tworzyć je, indeksować i modyfikować – oraz zrozumieć wektoryzację i recykling,
poznać i rozróżniać podstawowe struktury danych w
R oraz zrozumieć, kiedy warto wykorzystać
każdą z nich,
nauczyć się obliczać podstawowe statystyki opisowe oraz zrozumieć, kiedy średnia może prowadzić do mylnych wniosków,
zacząć tworzyć proste, powtarzalne symulacje z wykorzystaniem
set.seed() i sample().
Modele językowe (typu Claude, ChatGPT ,
Gemini, Copilot i inne) mogą bardzo pomóc w nauce
R (czy innego języka programowania), ale
tylko wtedy, gdy uczą, a nie wyręczają. Kilka zasad, które warto
zinternalizować:
Proś o wyjaśnienie, nie tylko o kod. Zamiast
„napisz kod, który…” spróbuj: „wyjaśnij krok po kroku, co robi
x[x > mean(x)]”.
Każ się odpytywać. np.: „nie podawaj rozwiązania – zadaj mi pytanie naprowadzające”.
Uruchamiaj i sprawdzaj. Modele potrafią z pełnym
przekonaniem wymyślić nieistniejący argument albo całą funkcję.
Ostatecznym arbitrem jest dokumentacja (?funkcja) i wynik w
konsoli.
Najpierw spróbuj sam. Dziesięć minut walki z błędem uczy więcej niż gotowa odpowiedź po dziesięciu sekundach.
Nie wklejaj danych osobowych – np. surowych danych uczestników badań.
Zadawaj pytania generyczne – uogólniaj i upraszczaj przypadki.
📖 Strategie projektowania promptów
Najwięcej uczymy się tam, gdzie nasze przewidywanie rozjeżdża się z rzeczywistością.
— parafraza idei uczenia opartego na błędzie przewidywania (prediction/error-driven learning)
######################
TRUE + TRUE
######################
"5" == 5
######################
"10" < "9"
######################
0.1 + 0.2 == 0.3
######################
round(2.5)
######################
round(3.5)
######################
as.integer(-1.7)
######################
NA^0
######################
NA > 1
######################
length(NULL)
| Wyrażenie | Wynik | Krótkie wyjaśnienie |
|---|---|---|
TRUE + TRUE |
2 |
TRUE zamienia się na 1,
FALSE na 0 |
"5" == 5 |
TRUE |
liczba 5 zostaje zamieniona na tekst
"5" (koercja) |
"10" < "9" |
TRUE |
teksty porównywane są jak w słowniku, znak po znaku:
"1" < "9" |
0.1 + 0.2 == 0.3 |
FALSE |
komputer nie potrafi dokładnie zapisać 0.1
w systemie dwójkowym |
round(2.5) |
2 |
R zaokrągla „do
parzystej” (standard IEC 60559) |
round(3.5) |
4 |
j.w. – najbliższa parzysta to 4 |
as.integer(-1.7) |
-1 |
obcięcie części ułamkowej w stronę zera, a nie zaokrąglenie |
NA^0 |
1 |
cokolwiek do potęgi zero daje 1, nawet wartość nieznana |
NA > 1 |
NA |
nie wiemy, ile wynosi NA, więc nie wiemy,
czy jest większe od 1 |
length(NULL) |
0 |
NULL to „nic” – pusty obiekt |
Istnieją w R specjalne słowa/znaki
przeznaczone do operowania wartościami prawda
(TRUE & T) i fałsz
(FALSE & F)
class(TRUE); typeof(TRUE); class(T); typeof(T)
T == TRUE
T <- FALSE
T == TRUE
rm(T)
TRUE <- FALSE
T == TRUE
## [1] TRUE
T <- FALSE # nikt nam nie zabroni...
T == TRUE # FALSE
## [1] FALSE
rm(T) # przywracamy porządek: T znów wskazuje na wbudowane TRUE
TRUE <- FALSE # a tu R się postawi, bo TRUE to słowo zastrzeżone
## Error in `TRUE <- FALSE`:
## ! invalid (do_set) left-hand side to assignment
T i F to tylko skróty, które da się
nadpisać. TRUE i FALSE są “nietykalne”.
SUGESTIA Piszmy raczej
TRUE/FALSE w całości, skoro T i
F to modyfikowalne obiekty.
W języku R wyróżniamy kilka
podstawowych typów danych, które są kluczowe do pracy z danymi.
| Typ / klasa | Co przechowuje | Przykład | class() |
typeof() |
|---|---|---|---|---|
character |
tekst (ciąg znaków) | "Hello, World!" |
character |
character |
numeric |
liczby zmiennoprzecinkowe | 3.14 |
numeric |
double |
integer |
liczby całkowite (z literą L) |
42L |
integer |
integer |
logical |
TRUE (T) lub
FALSE (F) |
TRUE |
logical |
logical |
complex |
liczby zespolone | 1 + 2i |
complex |
complex |
factor |
zmienne kategorialne | factor(c("male", "female", "female", "male")) |
factor |
integer |
Date |
daty | as.Date("2024-09-05") |
Date |
double |
POSIXct |
daty i czasy | as.POSIXct("2024-09-05 09:25:01") |
POSIXct POSIXt |
double |
Pięć pierwszych wierszy to typy podstawowe
(atomowe). Trzy ostatnie to klasy zbudowane na typach
podstawowych – stąd różnica między kolumnami class() i
typeof().
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 R.
moja_dokladna_liczba <- 54.7
class(moja_dokladna_liczba) # numeric
## [1] "numeric"
moja_liczba <- 53
class(moja_liczba) # teoretycznie integer
## [1] "numeric"
moja_liczba <- 53L
class(moja_liczba) # integer
## [1] "integer"
WNIOSEK Jeśli chcemy, aby wektor numeryczny
przechowywał wartości całkowite (klasy 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) # nowa funkcji ?str ;jej output będzie wyglądał nieco inaczej na różnych klasach/ typach obiektów
## logi TRUE
Jeśli nie podobają nam się typy danych zastosowane
przez R’a, możemy próbować je zmienić
funkcjami as. ...()
# czasem przydaje się trzymać wartość numeryczną jako string (kiedy?)
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)
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.9, -3.8, 5.1, 3.2, 7.9, 1.4)) # a tu?
## [1] -5 -3 5 3 7 1
as.integer() obcina część ułamkową w stronę zera, a nie
zaokrągla ani nie bierze podłogi.
Dlatego:
7.9 daje 7, a nie 8, więc to
nie jest zaokrąglanie.-5.9 daje -5, a nie -6, więc
to nie jest floor(). Dla liczb ujemnych zachowuje się jak
ceiling().R robi to bez żadnego ostrzeżenia, więc
łatwo przeoczyć błąd.
x <- c(-5.9, -3.8, 5.1, 3.2, 7.9, 1.4)
| Funkcja | Wynik |
|---|---|
| x | # -5.9 -3.8 5.1 3.2 7.9 1.4 |
trunc(x) |
# -5 -3 5 3 7 1 |
floor(x) |
# -6 -4 5 3 7 1 |
ceiling(x) |
# -5 -3 6 4 8 2 |
round(x) |
# -6 -4 5 3 8 1 |
Dla zaokrąglenia do liczby całkowitej typu integer,
można użyć as.integer(round(x)).
Ogólnie hierarchia typów wygląda następująco:
character > complex >
numeric (double) > integer
> logical.
Przy łączeniu różnych typów R zawsze
„awansuje” wszystko do typu najwyższego. Ponad nimi stoi jeszcze lista,
która może przechowywać wszystko naraz.
Klasa (class()) to „kostium”, w którym obiekt się
pokazuje. Typ (typeof()) to sposób, w jaki obiekt jest
naprawdę przechowywany w pamięci. Ta różnica wyjaśni nam później kilka
pułapek z factorami.
(x <- factor(c("niski", "wysoki", "niski")))
class(x)
typeof(x)
(d <- as.Date("2000-02-29")) # R 1.0.0 !
class(d)
typeof(d)
as.numeric(d)
(x <- factor(c("niski", "wysoki", "niski")))
## [1] niski wysoki niski
## Levels: niski wysoki
class(x) # "factor"
## [1] "factor"
typeof(x) # "integer": factor to liczby całkowite w przebraniu, z etykietami
## [1] "integer"
unclass(x) # zdejmujemy kostium
## [1] 1 2 1
## attr(,"levels")
## [1] "niski" "wysoki"
c(x, 67)
## [1] 1 2 1 67
(d <- as.Date("1985-02-14"))
## [1] "1985-02-14"
class(d) # "Date"
## [1] "Date"
typeof(d) # "double"
## [1] "double"
as.numeric(d) # liczba dni od 1970-01-01, "urodzin" Uniksa
## [1] 5523
Ile żyjesz dni?
# Ile żyjesz dni?
Sys.Date() - as.Date("1985-02-14")
## Time difference of 15224 days
Ile żyjesz lat?
# Ile żyjesz lat?
as.numeric(
Sys.Date() - as.Date("1985-02-14")
) / 365.25
## [1] 41.68104
Funkcje is.*() zadają pytanie “czy to jest…?”
is.numeric(42L) # TRUE – integer też jest liczbą
## [1] TRUE
is.character("42") # TRUE
## [1] TRUE
is.logical(NA) # TRUE – samotne NA jest typu logical
## [1] TRUE
c(1, "dwa", TRUE) # wszystko staje się tekstem
## [1] "1" "dwa" "TRUE"
c(1, TRUE, NA) # wszystko staje się liczbą
## [1] 1 1 NA
"10" < "9" # porównanie słownikowe!
## [1] TRUE
sort(c("10", "9", "100", "2")) # sortowanie "po słownikowemu"
## [1] "10" "100" "2" "9"
sort(as.numeric(c("10", "9", "100", "2"))) # sortowanie liczbowe
## [1] 2 9 10 100
# Liczenie TRUE to codzienność analizy danych
poprawne <- c(TRUE, FALSE, TRUE, TRUE, FALSE) # czy uczestnik odpowiedział poprawnie?
sum(poprawne) # ile poprawnych odpowiedzi?
## [1] 3
mean(poprawne) # jaki odsetek poprawnych odpowiedzi?
## [1] 0.6
Bez uruchamiania poniższych linijek przwidźmy klasę obiektów:
class(c(1L, 2.5))
class(c(TRUE, "FALSE"))
class(c(1L, TRUE))
| Wartość | Znaczenie | Jak powstaje? | Jak sprawdzić? | length() |
|---|---|---|---|---|
NA |
brak danych (Not Available) | brak odpowiedzi w ankiecie | is.na() |
1 |
NaN |
nie-liczba (Not a Number) | 0/0, sqrt(-1) |
is.nan() |
1 |
Inf, -Inf |
nieskończoność | 1/0, exp(1000) |
is.infinite() |
1 |
NULL |
brak obiektu, „pustka” | x <- NULL |
is.null() |
0 |
Analogia: NA to puste pole w ankiecie (osoba istnieje,
odpowiedzi brak), a NULL to ankieta, której w ogóle nie
ma.
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
A teraz kilka zachowań, które warto znać
NA + 1 # NA "zaraża" wynik
## [1] NA
NA == NA # czy dwie nieznane wartości są równe? Nie wiadomo!
## [1] NA
sqrt(-1) # NaN + ostrzeżenie
## [1] NaN
is.na(NaN) # TRUE – NaN to szczególny przypadek braku
## [1] TRUE
is.nan(NA) # FALSE
## [1] FALSE
c(1, NA, 3) # NA zajmuje miejsce w wektorze
## [1] 1 NA 3
c(1, NULL, 3) # NULL po prostu znika
## [1] 1 3
Inf - Inf # nieskończoność minus nieskończoność to... NaN
## [1] NaN
NA & FALSE # FALSE! Niezależnie od NA koniunkcja z FALSE jest fałszem
## [1] FALSE
NA | TRUE # TRUE – z tego samego powodu
## [1] TRUE
PRZYPOMNIENIE
exp(x) to funkcja wykładnicza, czyli e do
potęgi x, gdzie e ≈ 2,71828 (liczba
Eulera).
\[ \exp(x) = e^{x} \]
exp(-1) # 0.3678794
exp(0) # 1
exp(1) # 2.718282 (samo e)
exp(2) # 7.389056
# Działa wektorowo, jak większość funkcji w R
exp(c(-1, 0, 1, 2))
# [1] 0.3678794 1.0000000 2.7182818 7.3890561
PRZYPOMNIENIE
\[ \exp(\ln x) = x, \qquad \ln(e^{x}) = x \]
Jest odwrotnością logarytmu naturalnego log()
log(exp(3)) # 3
exp(log(10)) # 10
0.1 + 0.2 == 0.3
print(0.1 + 0.2, digits = 17)
print(0.3, digits = 17)
all.equal(0.1 + 0.2, 0.3)
###################
sqrt(2)^2 == 2
print(sqrt(2)^2, digits = 16)
print(sqrt(2)^2, digits = 17)
###################
seq(0, 10, by = 1) == 3
seq(0, 1, by = 0.1) == 0.3
0.1 + 0.2 == 0.3
## [1] FALSE
print(0.1 + 0.2, digits = 17) # 0.30000000000000004: hmmm...
## [1] 0.30000000000000004
print(0.3, digits = 17) # 0.29999999999999999 wtf?
## [1] 0.29999999999999999
isTRUE(all.equal(0.1 + 0.2, 0.3)) # porównanie "z tolerancją": TRUE ; ?all.equal
## [1] TRUE
###################
sqrt(2)^2 == 2 # FALSE
## [1] FALSE
print(sqrt(2)^2, digits = 16) # 2
## [1] 2
print(sqrt(2)^2, digits = 17) # 2.0000000000000004
## [1] 2.0000000000000004
###################
seq(0, 10, by = 1) == 3 # jest na czwartym indeksie
## [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
seq(0, 1, by = 0.1) == 0.3 # hmmm... gdzie się podziało 0.3?!
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
Analogia. Ułamek 1/3 w systemie dziesiętnym to 0,333…, czyli rozwinięcie nieskończone – gdzieś musimy je uciąć. Komputer liczy w systemie dwójkowym, a tam właśnie 0,1 jest takim „nieskończonym” ułamkiem. Komputer zapisuje więc liczbę bardzo bliską 0,1, ale nie dokładnie 0,1.
Wniosek praktyczny: liczb niecałkowitych nie
porównujemy przez ==, tylko przez
isTRUE(all.equal(a, b)).
all.equal() nie zwraca FALSE, gdy wartości
się różnią. Zwraca wtedy tekst opisujący różnicę. Porównuje z tolerancją
(domyślnie około 1.5e-8), co rozwiązuje problem błędów
zmiennoprzecinkowych
all.equal(0.1 + 0.2, 0.3)
# [1] TRUE
all.equal(1, 1.5)
# [1] "Mean relative difference: 0.5"
isTRUE()rozwiązuje ten problem: zwraca TRUE
tylko wtedy, gdy argument jest dokładnie pojedynczym TRUE,
a w każdym innym przypadku (tekst, NA, wektor) zwraca
FALSE.
Swoją drogą:
1.5e-8 == 1.5*10^-8
## [1] FALSE
isTRUE(all.equal(1.5e-8, 1.5*10^-8))
## [1] TRUE
x |
round(x) |
floor(x) |
ceiling(x) |
trunc(x) |
as.integer(x) |
|---|---|---|---|---|---|
| 2.7 | 3 | 2 | 3 | 2 | 2 |
| 2.5 | 2 | 2 | 3 | 2 | 2 |
| 3.5 | 4 | 3 | 4 | 3 | 3 |
| -1.7 | -2 | -2 | -1 | -1 | -1 |
| -2.5 | -2 | -3 | -2 | -2 | -2 |
round()round(x, digits = 0) zaokrągla wartości
x do podanej liczby miejsc po przecinku. Działa
wektorowo i zwraca typ double, nie
integer.
# 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) # [poprawka: było "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
Dlaczego round(2.5) daje 2?
R stosuje regułę „zaokrąglaj połówki do
parzystej” (tzw. zaokrąglanie bankierskie). Przy sumowaniu wielu
zaokrągleń błędy w górę i w dół się znoszą, zamiast systematycznie
zawyżać wynik.
round(c(0.5, 1.5, 2.5, 3.5, 4.5)) # 0 2 2 4 4
## [1] 0 2 2 4 4
floor(x)Funkcje floor(x) i
ceiling(x) zaokrąglają do najbliższej
wartości całkowitej odpowiednio w dół lub w górę.
floor(1.23456789)
## [1] 1
floor(-1.23456789)
## [1] -2
floor(c(0.5, 1.5, 2.5, 3.5, 4.5))
## [1] 0 1 2 3 4
ceiling(x)ceiling(1.23456789)
## [1] 2
ceiling(-1.23456789)
## [1] -1
ceiling(c(0.5, 1.5, 2.5, 3.5, 4.5))
## [1] 1 2 3 4 5
trunc()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(-exp(1)) # == trunc(-e)
## [1] -2
2 albo 3 cyfry znaczące.
signif(123456, 2) # ?signif
## [1] 120000
round(0.000123456, 3) # 0
## [1] 0
signif(0.000123456, 3) # 0.000123
## [1] 0.000123
signif(x, digits = 6)zaokrągla liczbę do podanej liczby
cyfr znaczących, czyli cyfr liczonych od pierwszej niezerowej cyfry,
niezależnie od położenia przecinka.
Przykład notacji wykładniczej
5834617 * 10^5
## [1] 583461700000
Przykład notacji naukowej
5.834617e+5
## [1] 583461.7
Ile cyfr R pokazuje? Domyślnie 7 cyfr
znaczących (options(digits = 7)), dlatego wyszło co
powyżej
To samo?
5.834617 * 10^5 == 5.834617e+5
## [1] TRUE
5.834617e+5 == 583461.7
## [1] TRUE
exp(-67) # super mała, w notacji naukowej
## [1] 7.984904e-30
exp(67) # super duża, w notacji naukowej
## [1] 1.252363e+29
Wyłączanie notacji naukowej (scientific notation)
Jeśli uruchomimy options(scipen = 50), to wówczas w
konsoli pojawią nam się inne wyświetlenia
options(scipen = 50) # opcja systemowa scipen (scientific penalty)
exp(-67) # super mała, pozbawiona notacji
## [1] 0.000000000000000000000000000007984904
exp(67) # super duża, analogicznie
## [1] 125236317084221369338226600842
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
Przywracamy ustawienia domyślne – inaczej obowiązywałyby do końca dokumentu
options(scipen = 0, digits = 7)
Ogólny schemat wywołania funkcji:
nazwa_funkcji(argument1 = wartość, argument2 = wartość).
Listę argumentów i ich wartości domyślnych podejrzymy przez
?nazwa_funkcji albo args(nazwa_funkcji).
PRZYPOMNIENIE
Pamiętamy czym jest logarytm?
Logarytm to odwrotność potęgowania
\[\log_a(x) = y \iff a^y = x\]
Czyli: „do jakiej potęgi trzeba podnieść a, żeby
otrzymać x?”
\(a > 0,\ a \neq 1\) — podstawa logarytmu
\(x > 0\) — liczba logarytmowana
\(y\) — wynik
W R do logarytmowania służy funkcja
log()
Koniec Przypomnienia
Wracając do używania argumentów funkcji w
R
# ?log()
log(100)
log(x = 100, base = 10)
log(100, base = 10)
log(base = 10, 100)
log(100, 10)
log(10, 100)
# ?round()
round(3.14159, 2)
round(digits = 2, x = 3.14159)
# ?seq
seq(1, 10, length.out = 4)
seq(1, 10, len = 4)
log(100) # domyślnie logarytm naturalny o podstawie e (exp(1))
## [1] 4.60517
log(x = 100, base = 10) # 2
## [1] 2
log(100, base = 10) # 2
## [1] 2
log(base = 10, 100) # 2: nazwane argumenty mogą być w dowolnej kolejności
## [1] 2
log(100, 10) # 2: pozycyjnie
## [1] 2
log(10, 100) # 0.5! pozycja ma znaczenie, gdy nie nazywamy
## [1] 0.5
####
round(3.14159, 2)
## [1] 3.14
round(digits = 2, x = 3.14159)
## [1] 3.14
####
seq(1, 10, length.out = 4)
## [1] 1 4 7 10
seq(1, 10, len = 4) # działa, bo R dopasowuje częściowo nazwy (length.out). Ciekawostka, nie nawyk!
## [1] 1 4 7 10
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)) - 730
## [1] 0
Długie zagnieżdżenia czyta się od środka. Rozpiszmy przykład 2 krok po kroku – tak samo szuka się błędów w zawiłym kodzie:
krok1 <- 3 ** 2 - 1 + 3 ** (2 - 1) / 3 # 9 - 1 + 1 = 9
krok2 <- log(krok1, base = 2) # ok. 3.17
krok3 <- sqrt(krok2) # ok. 1.8
krok4 <- 123 * exp(krok3) # ok. 729.7
krok5 <- round(krok4, -1) # do dziesiątek; = 730
krok6 <- abs(-krok5) # wartość absolutna czyli 730
krok7 <- krok6 - 730 # odejmowanie 730 - 730 = 0
print(krok7)
## [1] 0
= vs <-if (exists("x")) rm(x)
mean(x = 1:10)
exists("x") # jaka jest wartość x ?
mean(x <- 1:10)
exists("x") # jaka jest wartość x ?
if (exists("x")) rm(x) # dla wyczyszczenia środowiska, na wypadek, gdyby obiekt o nazwie x już istniał
mean(x = 1:10)
## [1] 5.5
exists("x") # FALSE: x jest tylko nazwą argumentu funkcji do liczenia średniej
## [1] FALSE
mean(x <- 1:10)
## [1] 5.5
exists("x") # TRUE: przy okazji powstał obiekt w środowisku
## [1] TRUE
# A jaka jest wartość x
# [1] 1 2 3 4 5 6 7 8 9 10
# czy
# [1] 5.5
# ?
Stąd konwencja: <- służy do przypisania,
= do argumentów funkcji
PRZYPOMNIENIE i uzupełnienie poprzednich zajęć
| Operator | Znaczenie | Przykład | Wynik |
|---|---|---|---|
==, != |
równe, różne | 3 == 3 |
TRUE |
<, >,
<=, >= |
porównania | 2 >= 3 |
FALSE |
! |
negacja (NIE) | !TRUE |
FALSE |
& |
koniunkcja (I), element po elemencie | c(TRUE, FALSE) & c(TRUE, TRUE) |
TRUE FALSE |
| |
alternatywa (LUB), element po elemencie | c(TRUE, FALSE) | c(FALSE, FALSE) |
TRUE FALSE |
&&, || |
I / LUB dla pojedynczych wartości (np. w
if) |
TRUE && FALSE |
FALSE |
%in% |
czy element należy do zbioru | 3 %in% 1:5 |
TRUE |
any(), all() |
czy którykolwiek / czy wszystkie | any(c(FALSE, TRUE)) |
TRUE |
xor() |
alternatywa wykluczająca (ALBO) | xor(TRUE, TRUE) |
FALSE |
| x | y | xor(x, y) |
|---|---|---|
| TRUE | TRUE | FALSE |
| FALSE | FALSE | FALSE |
| FALSE | TRUE | TRUE |
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 # OR (lub)
## [1] TRUE FALSE TRUE
x||y # OR (lub)
## Error in `x || y`:
## ! 'length = 3' in coercion to 'logical(1)'
!x # negacja
## [1] FALSE TRUE TRUE
x&y # AND (i)
## [1] TRUE FALSE FALSE
x&&y # AND (i)
## Error in `x && y`:
## ! 'length = 3' in coercion to 'logical(1)'
TRUE && FALSE
## [1] FALSE
c(TRUE, FALSE) && TRUE # błąd od wersji R 4.3
## Error in `c(TRUE, FALSE) && TRUE`:
## ! 'length = 2' in coercion to 'logical(1)'
T %in% x
## [1] TRUE
c("kot", "pies", "chomik") %in% c("pies", "kot")
## [1] TRUE TRUE FALSE
xor(x,y)
## [1] FALSE FALSE TRUE
xor(c(TRUE, TRUE, FALSE), c(TRUE, FALSE, FALSE))
## [1] FALSE TRUE FALSE
any(x) # Given a set of logical vectors, is at least one of the values true?
## [1] TRUE
any(c(3, 8, 12) > 10)
## [1] TRUE
all(x) # Given a set of logical vectors, are all of the values true?
## [1] FALSE
all(c(3, 8, 12) > 10)
## [1] FALSE
Zagadka: zadanie selekcyjne Peter’a Wasona (1966)
Na stole leżą cztery karty. Każda ma literę po jednej stronie i liczbę po drugiej. Widzimy: A, K, 4, 7. Reguła brzmi:
„Jeśli na karcie jest samogłoska, to na drugiej stronie jest liczba parzysta”.
Które karty trzeba odwrócić, żeby sprawdzić, czy reguła jest prawdziwa?
Poprawna odpowiedź
Trzeba odwrócić A i 7. Kartę A, bo jeśli z tyłu jest liczba nieparzysta, reguła jest złamana. Kartę 7, bo jeśli z tyłu jest samogłoska, reguła również jest złamana. Karta K jest nieistotna, bo reguła nic nie mówi o spółgłoskach. Karta 4 też nic nie rozstrzyga, bo reguła nie twierdzi, że liczba parzysta występuje tylko przy samogłoskach.
W kategoriach logiki chodzi o sprawdzenie poprzednika implikacji (p) i zaprzeczenia następnika (nie-q).
Jak odpowiadają ludzie
Poprawnie odpowiada zwykle tylko około 10% badanych. Najczęstszy błąd to wybór A i 4. Ludzie szukają przypadków, które potwierdzają regułę, zamiast takich, które mogłyby ją obalić.
Efekt treści
Najciekawsze jest to, że gdy to samo zadanie ma treść społeczną, wyniki dramatycznie się poprawiają. W wersji Leda Cosmides i J. Tooby reguła brzmi: „Jeśli ktoś pije piwo, musi mieć ukończone 18 lat”.
Karty to: „piwo”, „woda”, „25 lat” i „16 lat”. Większość ludzi od razu wskazuje „piwo” i „16 lat”, choć logicznie jest to identyczne zadanie.
Reguła to implikacja „p → q”. W R nie
ma dla niej osobnego operatora, ale jest ona równoważna
!p | q.
p <- c(TRUE, TRUE, FALSE, FALSE) # samogłoska?
q <- c(TRUE, FALSE, TRUE, FALSE) # liczba parzysta?
data.frame(p, q, implikacja = !p | q) # implikacja jest fałszywa tylko dla p = TRUE i q = FALSE
## p q implikacja
## 1 TRUE TRUE TRUE
## 2 TRUE FALSE FALSE
## 3 FALSE TRUE TRUE
## 4 FALSE FALSE TRUE
Kartę trzeba odwrócić, jeśli KTÓRAŚ możliwa druga strona łamie regułę
mozliwe <- c(TRUE, FALSE)
any(!(!TRUE | mozliwe)) # A: samogłoska, parzystość nieznana -> TRUE, odwracamy
## [1] TRUE
any(!(!FALSE | mozliwe)) # K: spółgłoska, parzystość nieznana -> FALSE
## [1] FALSE
any(!(!mozliwe | TRUE)) # 4: parzysta, litera nieznana -> FALSE
## [1] FALSE
any(!(!mozliwe | FALSE)) # 7: nieparzysta, litera nieznana -> TRUE, odwracamy
## [1] TRUE
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!
x <- 5 # To jest skalar, ale też wektor długości 1
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
# 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
Swoją drogą, czy da się nakreślić jakiś “sznyt charakterologiczny” osoby z takimi ocenami?
# funkcja c() umożliwia nadanie nazw kolejnym elementom w konwencji nazwa = wartość
id <- c(pierwszy = 1, drugi = 2, trzeci = 3)
Strukturę obiektu można sprawdzić choćby funkcją
str()
str(oceny_stat_R)
## Named num [1:5] 4 5 2 2 3
## - attr(*, "names")= chr [1:5] "klasowka" "aktywnosc" "sprawdzian" "sprawdzian" ...
wektor_1 <- c(2,4,7,3)
wektor_2 <- c(3,8,9,2)
print(suma_wektorow <- wektor_1 + wektor_2)
## [1] 5 12 16 5
(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
(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
Zauważmy ostatnia wartość nie jest wartością wskazaną jako kończąca sekwencję, czemu?
5.1:8.2 #
## [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
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
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"
# to samo co wyzej, ale bardziej uniwersalnie
owoce[-c(1, length(owoce))] # żeby nie robić "hard-coding'u"
## [1] "banan" "gruszka" "pomarańcza"
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
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
(wektor <- seq(10,50, by = 10))
## [1] 10 20 30 40 50
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
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.
(liczby <- c(23, 5, 17, 8, 42, 11))
## [1] 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
Czy operacje się zapisują samoistnie?
(a <- c(100, 10, 1000)) # Stwórzmy szybko przykładowy wektor trójelementowy
order(a)
sort(a)
a[order(a)] # można posortować wektor indeksując go funkcją order(), da taki sam rezultat jak sort()
a # w środku dotychczasowa kolejność
a <- a[order(a)] # chcąc mieć na stałe posortowany wektor musimy go sobie nadpisać
a
Można jeszcze skorzystać z funkcji rank(). w
R służy do przypisywania rang wartościom w
wektorze. Jest to przydatne, gdy chcemy 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
📖 Samodzielna lektura, szczegóły argumentów funkcji rank()
jak zwykle dostępne w dokumentacji funkcji ?rank()
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.
(wektor <- c(3, 7, 2, 7, 9, 2, 5, 7))
## [1] 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
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.
# ?head
head(letters, 3) # [+] pierwsze 3 elementy
## [1] "a" "b" "c"
tail(letters, 3) # [+] ostatnie 3 elementy
## [1] "x" "y" "z"
head(letters, -23) # [+] ciekawostka: ujemne n = "wszystko oprócz ostatnich 23"
## [1] "a" "b" "c"
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"
Ile wynosił najlepszy wynik?
max(wyniki)
## [1] 96
Kto ma najlepszy wynik?
imiona_studentow[which.max(wyniki)]
## [1] "Iza"
“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)
print(id2)
## czwarty.drugi piąty.trzeci szósty
## 4 5 6
PRZYPOMNIENIE
Normalizacja przeskalowuje wartości zmiennej do ustalonego zakresu, najczęściej \([0, 1]\). Najmniejsza wartość przyjmuje 0, a największa 1.
\[ x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}} \]
Przeskalowanie do dowolnego zakresu \([a, b]\):
\[ x' = a + \frac{(x - x_{\min})(b - a)}{x_{\max} - x_{\min}} \]
Kiedy stosować: gdy potrzebujemy wartości w ograniczonym przedziale, np. w sieciach neuronowych, metodach opartych na odległościach (k-NN, k-średnich) lub przy porównywaniu zmiennych mierzonych w różnych jednostkach.
Uwaga: metoda jest wrażliwa na wartości odstające. Jedna skrajna obserwacja „ściska” pozostałe wartości w wąskim przedziale.
# 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
# Inaczej
as.vector(scale(x = dane,
center = min(dane),
scale = max(dane) - min(dane)))
## [1] 0.1666667 0.5833333 0.0000000 1.0000000 0.3333333
# Jeszcze inaczej
scales::rescale(dane)
## [1] 0.1666667 0.5833333 0.0000000 1.0000000 0.3333333
# Takie same wyniki?
all.equal(dane_norm, scales::rescale(dane))
## [1] TRUE
PRZYPOMNIENIE
Standaryzacja przekształca zmienną tak, aby miała średnią równą 0 i odchylenie standardowe równe 1. Wynik mówi, o ile odchyleń standardowych dana obserwacja leży od średniej.
\[ z = \frac{x - \bar{x}}{s} \]
gdzie:
\[ \bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i, \qquad s = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_i - \bar{x})^2} \]
Kiedy stosować: w PCA, regresji, analizie skupień, a także gdy chcemy porównać obserwacje z różnych rozkładów.
Uwaga: wartości nie są ograniczone do żadnego przedziału. Standaryzacja nie zmienia kształtu rozkładu, więc nie czyni danych normalnymi.
# Dane surowe
dane <- c(10, 15, 8, 20, 12)
# 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
sd(dane_std) # Sprawdzenie (równe 1)
## [1] 1
# To samo za pomocą wbudowanej funkcji
as.vector(scale(dane))
## [1] -0.6396021 0.4264014 -1.0660036 1.4924050 -0.2132007
sd() w R liczy odchylenie
z próby, czyli z mianownikiem \(n-1\)
?sd() —> Details: Like var this uses
denominator n−1
scale() zwraca macierz z atrybutami, stąd w przykładzie
as.vector()
| Cecha | Normalizacja (min-max) | Standaryzacja (z-score) |
|---|---|---|
| Wzór | \(\frac{x - x_{\min}}{x_{\max} - x_{\min}}\) | \(\frac{x - \bar{x}}{s}\) |
| Zakres wyników | \([0, 1]\) | nieograniczony |
| Średnia po przekształceniu | zależy od danych | 0 |
| Odchylenie standardowe | zależy od danych | 1 |
| Wrażliwość na outliery | duża | mniejsza |
hist(dane)
hist(dane_norm)
hist(dane_std)
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żywamy ‘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).
Symulacja
Rzut kostką 1000 razy. Jaki to będzie rozkład? Jaką zmienną operujemy?
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
hist(rzuty)
plot(rzuty)
# 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
Rozkład jednostajny dyskretny (uniform discrete
distribution)
Prawo wielkich liczb z cumsum() na rzutach kostką
Czy częstość szóstek naprawdę zbliża się do 1/6? Sprawdźmy, jak zmienia się wraz z kolejnymi rzutami.
Funkcja cumsum() liczy sumę narastającą, przykład:
1:10 # sekwencja
## [1] 1 2 3 4 5 6 7 8 9 10
# ?cumsum()
cumsum(1:10)
## [1] 1 3 6 10 15 21 28 36 45 55
I teraz mała wizualizacja ważnego prawa.
Rzucamy kostką.
# Rzucamy ponownie kostką:
set.seed(123) # Dla powtarzalności
rzuty <- sample(1:6, # ?sample
size = 1000,
replace = TRUE)
# Sprawdźmy
(liczba_szostek <- sum(rzuty == 6)) # ile faktycznie wypadło
## [1] 164
(oczekiwana <- length(rzuty) / 6) # ile "powinno" wypaść (np. 166.67 przy 1000 rzutach)
## [1] 166.6667
liczba_szostek - oczekiwana # odchylenie w sztukach, szóstej jest mniej niż oczekiwano, o ile?
## [1] -2.666667
mean(rzuty == 6) # odsetek szóstek
## [1] 0.164
czestosc_szostek <- cumsum( # 2. cumsum(...) liczy sumę narastającą. R traktuje TRUE jako 1, a FALSE jako 0, więc wynik mówi, ile szóstek było do danego rzutu włącznie
rzuty == 6 # 1. sprawdza każdy rzut, czy wypadła szóstka, i daje wektor logiczny: FALSE TRUE
) / seq_along(rzuty) # 3. numery rzutów, dzielenie daje odsetek szóstek po każdym kolejnym rzucie
# Częstość szóstek w kilku "newralgicznych" momentach
czestosc_szostek[c(10, 50, 100, 500, 1000)]
## [1] 0.300 0.140 0.210 0.176 0.164
Jak szybko zbliżamy się do 1/6?
#
plot(czestosc_szostek,
type = "l",
xlab = "Liczba rzutów",
ylab = "Odsetek szóstek")
abline(h = 1/6, col = "red", lty = 2)
To prawo wielkich liczb: przy małej liczbie prób częstość skacze, przy dużej się stabilizuje. Złudzenie gracza (gambler’s fallacy) polega na oczekiwaniu, że to wyrównanie nastąpi „od razu” – np. że po serii orłów reszka jest „należna”.
# 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"
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))) # macierz wierszowa (5X1)
## [1] "matrix" "array"
## [1] FALSE
================================
UTRWALENIE
c(5, 12, 8, 3, 15, 20, 7) wybierz elementy
większe od jego średniej.czestosc_szostek ustalmy, od
którego rzutu odsetek szóstek już na stałe mieści się w
przedziale 0.15–0.18. ★# 1
dojazd <- c(pon = 42, wt = 38, sr = 55, czw = 40, pt = 61)
mean(dojazd); median(dojazd); sd(dojazd)
names(dojazd)[which.max(dojazd)]
# 2
v <- c(5, 12, 8, 3, 15, 20, 7)
v[v > mean(v)]
# 3
poza_przedzialem <- which(czestosc_szostek < 0.15 | czestosc_szostek > 0.18)
max(poza_przedzialem) + 1 # pierwszy rzut, po którym już nigdy nie wychodzimy poza przedział
plot(czestosc_szostek, type = "l")
abline(h = c(0.15, 0.18), col = "blue", lty = 3)
abline(v = max(poza_przedzialem) + 1, col = "darkgreen")
Do zapamiętania
RNA wymaga specjalnej obsługi
(na.rm = TRUE)
Stwórzmy pierwszą macierz matrix()
# ?matrix
h <- help("matrix")
tools::Rd2txt(utils:::.getHelpFile(h),
options = list(underline_titles = FALSE))
## Matrices
##
## Description:
##
## 'matrix' creates a matrix from the given set of values.
##
## 'as.matrix' attempts to turn its argument into a matrix.
##
## 'is.matrix' tests if its argument is a (strict) matrix.
##
## Usage:
##
## matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
## dimnames = NULL)
##
## as.matrix(x, ...)
## ## S3 method for class 'data.frame'
## as.matrix(x, rownames.force = NA, ...)
##
## is.matrix(x)
##
## Arguments:
##
## data: an optional data vector (including a list or 'expression'
## vector). Non-atomic classed R objects are coerced by
## 'as.vector' and all attributes discarded.
##
## nrow: the desired number of rows.
##
## ncol: the desired number of columns.
##
## byrow: logical. If 'FALSE' (the default) the matrix is filled by
## columns, otherwise the matrix is filled by rows.
##
## dimnames: a 'dimnames' attribute for the matrix: 'NULL' or a 'list' of
## length 2 giving the row and column names respectively. An
## empty list is treated as 'NULL', and a list of length one as
## row names. The list can be named, and the list names will be
## used as names for the dimensions.
##
## x: an R object.
##
## ...: additional arguments to be passed to or from methods.
##
## rownames.force: logical indicating if the resulting matrix should have
## character (rather than 'NULL') 'rownames'. The default,
## 'NA', uses 'NULL' rownames if the data frame has 'automatic'
## row.names or for a zero-row data frame.
##
## Details:
##
## If one of 'nrow' or 'ncol' is not given, an attempt is made to
## infer it from the length of 'data' and the other parameter. If
## neither is given, a one-column matrix is returned.
##
## If there are too few elements in 'data' to fill the matrix, then
## the elements in 'data' are recycled. If 'data' has length zero,
## 'NA' of an appropriate type is used for atomic vectors ('0' for
## raw vectors) and 'NULL' for lists.
##
## 'is.matrix' returns 'TRUE' if 'x' is a vector and has a '"dim"'
## attribute of length 2 and 'FALSE' otherwise. Note that a
## 'data.frame' is *not* a matrix by this test. The function is
## generic: you can write methods to handle specific classes of
## objects, see InternalMethods.
##
## 'as.matrix' is a generic function. The method for data frames
## will return a character matrix if there is only atomic columns and
## any non-(numeric/logical/complex) column, applying 'as.vector' to
## factors and 'format' to other non-character columns. Otherwise,
## the usual coercion hierarchy (logical < integer < double <
## complex) will be used, e.g., all-logical data frames will be
## coerced to a logical matrix, mixed logical-integer will give a
## integer matrix, etc.
##
## The default method for 'as.matrix' calls 'as.vector(x)', and hence
## e.g. coerces factors to character vectors.
##
## When coercing a vector, it produces a one-column matrix, and
## promotes the names (if any) of the vector to the rownames of the
## matrix.
##
## 'is.matrix' is a primitive function.
##
## The 'print' method for a matrix gives a rectangular layout with
## dimnames or indices. For a list matrix, the entries of length not
## one are printed in the form 'integer,7' indicating the type and
## length.
##
## Note:
##
## If you just want to convert a vector to a matrix, something like
##
## dim(x) <- c(nx, ny)
## dimnames(x) <- list(row_names, col_names)
##
## will avoid duplicating 'x' _and_ preserve 'class(x)' which may be
## useful, e.g., for 'Date' objects.
##
## References:
##
## Becker RA, Chambers JM, Wilks AR (1988). _The New S Language_.
## Chapman and Hall/CRC, London. ISBN 053409192X.
##
## See Also:
##
## 'data.matrix', which attempts to convert to a numeric matrix.
##
## A matrix is the special case of a two-dimensional 'array'.
## 'inherits(m, "array")' is true for a 'matrix' 'm'.
##
## Examples:
##
## is.matrix(as.matrix(1:10))
## !is.matrix(warpbreaks) # data.frame, NOT matrix!
## warpbreaks[1:10,]
## as.matrix(warpbreaks[1:10,]) # using as.matrix.data.frame(.) method
##
## ## Example of setting row and column names
## mdat <- matrix(c(1,2,3, 11,12,13), nrow = 2, ncol = 3, byrow = TRUE,
## dimnames = list(c("row1", "row2"),
## c("C.1", "C.2", "C.3")))
## mdat
##
# 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 R markdown'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ż 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[,3], 2) # [poprawka: było round(oceny_matrix2[,1], 2)]
oceny_matrix2 # much better
## Semestr 1 Semestr 2 Średnia ocen z semestru
## Statystyka w R 2.00 3 2.50
## Filozofia analityczna 5.00 5 5.00
## Matematyka Euklidesowa 4.00 4 4.00
## srednia_semestr 3.67 4 3.83
I taka jeszcze własność macierzy
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).
📖 Poniższe przykłady tablic trójwymiarowych – do samodzielnej lektury.
“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.71933
# Średnia temperatura w 100. dniu we wszystkich miastach i latach
mean(temperatura[, "Dzien 100", ])
## [1] 13.29183
# 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 7.477119 3.713587 29.225071 18.058943 6.203891 20.6495119
## Miasto 2 30.695097 19.435071 19.370700 11.037804 10.086581 16.0537982
## Miasto 3 22.202342 6.356843 14.189831 18.532499 2.014956 4.0519486
## Miasto 4 6.160996 26.635796 10.939643 11.178563 22.917492 25.4904154
## Miasto 5 11.621271 29.959395 7.268408 22.996425 10.880762 24.2685480
## Miasto 6 22.335050 14.009295 22.959724 15.492688 4.215175 12.7449977
## Miasto 7 19.537517 23.920289 15.160943 4.977832 5.282400 11.7093016
## Miasto 8 1.916444 25.572578 7.943781 29.849256 18.175985 8.9591161
## Miasto 9 10.159352 23.848407 17.606966 1.925264 9.418478 24.5315721
## Miasto 10 20.811871 18.800776 7.600795 10.947246 12.540863 -0.3562451
## Miasto 11 11.418740 25.395391 5.554431 9.370389 22.475856 6.0891554
## Miasto 12 29.717829 13.747817 16.787108 25.590283 3.909891 20.1860768
## Dzien
## Miasto Dzien 7
## Miasto 1 33.017186
## Miasto 2 11.050696
## Miasto 3 24.807897
## Miasto 4 14.071722
## Miasto 5 9.294620
## Miasto 6 14.403130
## Miasto 7 9.612360
## Miasto 8 17.964550
## Miasto 9 14.360051
## Miasto 10 9.595822
## Miasto 11 5.575756
## Miasto 12 27.045535
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 N.unique N.blank Min.nchar Max.nchar
## 5 2 0 1 1
summary(factor_vector_plci)
## Kobieta Mężczyzna
## 2 3
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 # [+] NA + ostrzeżenie: factor nominalny nie ma porządku – i bardzo dobrze :)
## [1] NA
# ale co z temperaturą? Czy średnia jest niższa niż wysoka?
srednia <- factor_temperatura_vector[5] # [poprawka: było temperatura_vector[5], czyli zwykły tekst]
wysoka <- factor_temperatura_vector[1] # [poprawka: było temperatura_vector[1]]
srednia < wysoka # zaiste :P
## [1] TRUE
Tabele 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
# 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
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)
# 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"
Factor w praktyce: skala Likerta
W kwestionariuszach psychologicznych odpowiedzi często mają postać skali Likerta.
To modelowy przykład zmiennej porządkowej.
poziomy <- c("zdecydowanie nie",
"raczej nie",
"trudno powiedzieć",
"raczej tak",
"zdecydowanie tak")
odp <- c("raczej tak",
"zdecydowanie tak",
"trudno powiedzieć",
"raczej tak",
"zdecydowanie nie",
"raczej tak",
"zdecydowanie tak")
likert <- factor(odp, levels = poziomy, ordered = TRUE)
likert
## [1] raczej tak zdecydowanie tak trudno powiedzieć raczej tak
## [5] zdecydowanie nie raczej tak zdecydowanie tak
## 5 Levels: zdecydowanie nie < raczej nie < trudno powiedzieć < ... < zdecydowanie tak
table(likert) # pokazuje też poziom, którego nikt nie wybrał (0)!
## likert
## zdecydowanie nie raczej nie trudno powiedzieć raczej tak
## 1 0 1 3
## zdecydowanie tak
## 2
table(odp) # zwykły tekst: brak pustej kategorii, kolejność alfabetyczna
## odp
## raczej tak trudno powiedzieć zdecydowanie nie zdecydowanie tak
## 3 1 1 2
likert >= "raczej tak" # porównania mają sens, bo kolejność jest zadana
## [1] TRUE TRUE FALSE TRUE FALSE TRUE TRUE
mean(likert >= "raczej tak") # odsetek osób zgadzających się
## [1] 0.7142857
as.integer(likert) # tu zamiana na liczby JEST sensowna: kodowanie 1–5
## [1] 4 5 3 4 1 4 5
median(as.integer(likert))
## [1] 4
Pytanie do dyskusji
Czy wolno liczyć średnią z odpowiedzi na skali Likerta?
Czy „odległość” między raczej nie a trudno powiedzieć jest taka sama jak między raczej tak a zdecydowanie tak?
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"
rm(mtcars) # [+] usuwamy NASZĄ zmodyfikowaną kopię; oryginał z pakietu datasets wciąż jest dostępny (sprawdź names(mtcars))
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
Ściągawka: jak dostać się do fragmentów ramki danych?
| Zapis | Co zwraca | Przykład |
|---|---|---|
df[i, j] |
pojedynczy element lub fragment | planets_df[3, 3] |
df[i, ] |
wiersz(e) jako data.frame |
planets_df[4, ] |
df[, j] |
kolumnę jako wektor | planets_df[, 2] |
df[, j, drop = FALSE] |
kolumnę jako data.frame |
planets_df[, 2, drop = FALSE] |
df$nazwa |
kolumnę jako wektor | planets_df$name |
df[["nazwa"]] |
kolumnę jako wektor | planets_df[["name"]] |
df["nazwa"] |
kolumnę jako data.frame |
planets_df["name"] |
df[warunek, ] |
wiersze spełniające warunek | planets_df[planets_df$rings, ] |
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 # [poprawka: było 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] 2
Podsumowania i statystyki
# Podsumowanie statystyczne
summary(mtcars)
## mpg cyl disp hp
## 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
## drat wt qsec vs
## 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
## am gear carb
## 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
## [1] 6.1875
median(mtcars$hp) # mediana liczby koni mechanicznych
## [1] 123
sd(mtcars$wt) # odchylenie standardowe masy własnej
## [1] 0.9784574
min(mtcars$gear) # minimum liczby biegów
## [1] 3
max(mtcars$carb) # maksimum liczny gaźników
## [1] 8
# Tabele częstości
table(mtcars$cyl)
##
## 4 6 8
## 11 7 14
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:
$ (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
[[]] (zwraca element)# Przez nazwę
moja_lista[["wiek"]]
## [1] 25
# Przez indeks
moja_lista[[1]]
## [1] "Anna"
[] (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"
Analogię spopularyzował Hadley Wickham, „Advanced R”)
Pomocna analogia: lista to pociąg, a jej elementy to
wagony. lista[1] odczepia pierwszy wagon –
dostajemy krótszy pociąg (wciąż listę). lista[[1]] zagląda
do środka wagonu i wyjmuje ładunek.
| Zapis | Co zwraca | Klasa wyniku dla moja_lista |
|---|---|---|
lista[1] |
podlistę (wagon z ładunkiem) | list |
lista[[1]] |
sam element (ładunek) | character |
lista$imie |
sam element, po nazwie | character |
lista[c(1, 2)] |
podlistę z kilkoma elementami | list |
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
moja_lista$wiek <- 26
moja_lista$wiek
## [1] 26
moja_lista$kierunek <- "Statystyka"
moja_lista$rok_studiow <- 3
# Sprawdzamy zaktualizowaną listę
names(moja_lista)
## [1] "imie" "wiek" "student" "oceny" "kierunek"
## [6] "rok_studiow"
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 i przechowywanie wyników analiz – do samodzielnej lektury. Warto tylko zapamiętać, że wiele funkcji statystycznych, np. lm(), zwraca właśnie listę.
Przekształcanie list
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
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:
Kluczowe operacje:
list(nazwa1 = wartość1, nazwa2 = wartość2)lista$nazwa, lista[[indeks]],
lista[indeks]lista$nowy_element <- wartośćnames(), length(),
str(), etc.
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)
Gdzie:
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
runif_result <- runif(10^6, 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)
Gdzie:
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
rnorm_result <- rnorm(10^6, mean = 10, sd = 2)
# rezultat
hist(rnorm_result)
| Funkcja | Co liczy | Odporna na wartości odstające? |
|---|---|---|
mean() |
średnia arytmetyczna | nie |
median() |
mediana (wartość środkowa) | tak |
min(), max(),
range() |
wartości skrajne | nie |
quantile() |
kwantyle (np. kwartyle, percentyle) | środkowe – tak |
IQR() |
rozstęp międzykwartylowy | tak |
sd(), var() |
odchylenie standardowe, wariancja | nie |
summary() |
kilka miar naraz | – |
Większość z nich ma argument na.rm = TRUE, który pomija
braki danych.
Do szybkiego stworzenia prostych statystyk opisowych możemy posłużyć się następującymi funkcjami:
max() maksimummax(rnorm_result) # nasz pseudolosowy wektor z poprzedniego przykładu
## [1] 19.70153
max(runif_result) # nasz pseudolosowy wektor z poprzedniego przykładu
## [1] 99.99999
min() minimummin(rnorm_result)
## [1] 0.4016187
print(min(runif_result), digits = 22)
## [1] 5.133915692567825317383e-05
sum() sumasum(rnorm_result)
## [1] 9996060
sum(runif_result)
## [1] 49952880
mean() średniamean(rnorm_result)
## [1] 9.99606
mean(runif_result)
## [1] 49.95288
median() medianamedian(rnorm_result)
## [1] 9.998284
median(runif_result)
## [1] 49.91531
range() liczy zakres wartości
vide minimum i maksimum.range(rnorm_result)
## [1] 0.4016187 19.7015340
range(runif_result)
## [1] 5.133916e-05 9.999999e+01
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%
## 0.4016187 8.6478257 9.9982844 11.3431772 19.7015340
quantile(runif_result)
## 0% 25% 50% 75% 100%
## 5.133916e-05 2.491638e+01 4.991531e+01 7.494999e+01 9.999999e+01
# można również percentyle
quantile(rnorm_result, probs = seq(0, 1, 0.1), na.rm = TRUE)
## 0% 10% 20% 30% 40% 50% 60%
## 0.4016187 7.4359834 8.3132521 8.9478016 9.4903093 9.9982844 10.5039282
## 70% 80% 90% 100%
## 11.0446890 11.6782194 12.5546469 19.7015340
quantile(runif_result, probs = seq(0, 1, 0.1), na.rm = FALSE)
## 0% 10% 20% 30% 40% 50%
## 5.133916e-05 9.981419e+00 1.989810e+01 2.992358e+01 3.991508e+01 4.991531e+01
## 60% 70% 80% 90% 100%
## 5.994133e+01 6.996262e+01 7.999512e+01 8.999585e+01 9.999999e+01
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.581139
sd(rnorm_result)
## [1] 1.997519
sd(runif_result)
## [1] 28.8812
var() wariancjaWariancja 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] 3.99008
var(runif_result)
## [1] 834.1237
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
Zobaczmy obie funkcje na danych, które nie są uporządkowane: najlepszy (najkrótszy) czas reakcji „do tej pory” w kolejnych próbach treningowych.
proby <- c(520, 480, 495, 430, 460, 410, 445, 405) # czasy reakcji w ms
cummin(proby) # rekord życiowy po każdej próbie
## [1] 520 480 480 430 430 410 410 405
which(diff(cummin(proby)) < 0) + 1 # w których próbach padł nowy rekord?
## [1] 2 4 6 8
cummax(proby) # najgorszy czas "do tej pory"
## [1] 520 520 520 520 520 520 520 520
cor() korelacjaFunkcja 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"))
Gdzie:
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.001903933
# jakiś komentarz co do wyniku?
Ile korelacji znajdziemy w czystym szumie?
set.seed(7)
szum <- matrix(rnorm(30 * 20), ncol = 20) # 30 "osób", 20 "zmiennych" czystego szumu
(kor <- cor(szum)) # macierz korelacji 20 x 20
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1.000000000 -0.260481138 -0.1080071615 -0.006557698 -0.10355131
## [2,] -0.260481138 1.000000000 0.0638825984 0.431181149 0.28926018
## [3,] -0.108007161 0.063882598 1.0000000000 -0.077940107 -0.06806709
## [4,] -0.006557698 0.431181149 -0.0779401072 1.000000000 0.05285692
## [5,] -0.103551313 0.289260180 -0.0680670905 0.052856925 1.00000000
## [6,] 0.020287787 -0.204094050 -0.3431884119 -0.105025258 0.10091227
## [7,] 0.142805108 -0.057000277 0.1195553144 0.007184798 -0.01835438
## [8,] 0.098436456 -0.220210462 -0.2894911762 -0.416884503 -0.14746116
## [9,] -0.278301611 0.161294562 0.1818099131 0.012718782 0.23281337
## [10,] 0.009873806 0.072067012 0.5240728362 0.027333197 0.01689046
## [11,] 0.488169575 0.204913108 -0.1945506824 0.118772483 0.05492099
## [12,] -0.260193883 0.035944049 0.2932410222 -0.084025929 -0.32443882
## [13,] 0.186538552 0.011364226 -0.0439376557 0.341386418 -0.03953204
## [14,] 0.089677141 0.134942987 -0.5112365519 0.098792378 0.46588885
## [15,] -0.101539837 0.227555443 0.0009629415 -0.008048254 0.29228220
## [16,] -0.013843415 -0.020620085 -0.1072119200 -0.105749847 0.22364861
## [17,] -0.067871422 0.277345131 -0.0062925920 0.148046060 0.36597941
## [18,] -0.256506735 0.104382276 0.1538254301 -0.141704486 -0.10260952
## [19,] 0.004802103 0.126144519 0.1116610911 0.278656847 0.16978901
## [20,] -0.003699918 0.004075751 -0.1092850765 0.060630504 -0.14001185
## [,6] [,7] [,8] [,9] [,10]
## [1,] 0.020287787 0.142805108 0.09843646 -0.278301611 0.009873806
## [2,] -0.204094050 -0.057000277 -0.22021046 0.161294562 0.072067012
## [3,] -0.343188412 0.119555314 -0.28949118 0.181809913 0.524072836
## [4,] -0.105025258 0.007184798 -0.41688450 0.012718782 0.027333197
## [5,] 0.100912273 -0.018354377 -0.14746116 0.232813370 0.016890463
## [6,] 1.000000000 -0.354441415 -0.10496375 -0.009921481 -0.088601797
## [7,] -0.354441415 1.000000000 -0.01607924 0.031329654 -0.091981612
## [8,] -0.104963754 -0.016079236 1.00000000 0.135901595 -0.278434952
## [9,] -0.009921481 0.031329654 0.13590160 1.000000000 -0.162246077
## [10,] -0.088601797 -0.091981612 -0.27843495 -0.162246077 1.000000000
## [11,] 0.045725475 0.126140442 0.16656159 -0.083738366 -0.066654045
## [12,] -0.328930145 0.257856333 -0.13820619 0.150254630 0.128521201
## [13,] 0.295088654 -0.131602950 -0.11755768 -0.114872009 0.157272305
## [14,] 0.201520844 -0.162246545 0.08307674 0.202135493 -0.081892032
## [15,] 0.180129346 -0.224380233 0.06894077 0.147072327 0.128300648
## [16,] 0.528599055 -0.026561097 -0.14015556 -0.001864470 -0.319081519
## [17,] -0.049225541 0.195328943 -0.19346302 -0.103173890 0.320145783
## [18,] 0.015443663 0.279486584 -0.21951535 -0.077725739 0.044311420
## [19,] -0.015884107 0.083009184 -0.24665413 0.187106926 -0.121793519
## [20,] 0.282971054 -0.081789213 -0.17493554 0.027619681 0.045548253
## [,11] [,12] [,13] [,14] [,15]
## [1,] 0.48816957 -0.260193883 0.186538552 0.08967714 -0.1015398366
## [2,] 0.20491311 0.035944049 0.011364226 0.13494299 0.2275554432
## [3,] -0.19455068 0.293241022 -0.043937656 -0.51123655 0.0009629415
## [4,] 0.11877248 -0.084025929 0.341386418 0.09879238 -0.0080482542
## [5,] 0.05492099 -0.324438818 -0.039532037 0.46588885 0.2922821984
## [6,] 0.04572547 -0.328930145 0.295088654 0.20152084 0.1801293457
## [7,] 0.12614044 0.257856333 -0.131602950 -0.16224654 -0.2243802329
## [8,] 0.16656159 -0.138206190 -0.117557685 0.08307674 0.0689407682
## [9,] -0.08373837 0.150254630 -0.114872009 0.20213549 0.1470723274
## [10,] -0.06665404 0.128521201 0.157272305 -0.08189203 0.1283006483
## [11,] 1.00000000 -0.100678671 0.117865529 0.15396684 0.0860324871
## [12,] -0.10067867 1.000000000 0.004680883 -0.30195812 -0.1739015383
## [13,] 0.11786553 0.004680883 1.000000000 0.06624502 0.2877882187
## [14,] 0.15396684 -0.301958120 0.066245019 1.00000000 0.0641114892
## [15,] 0.08603249 -0.173901538 0.287788219 0.06411149 1.0000000000
## [16,] -0.19906332 -0.379928384 -0.086726804 0.08508370 0.1956154078
## [17,] 0.08242674 -0.323441114 -0.049588963 0.29652870 0.2956183817
## [18,] 0.02772117 0.063967740 -0.117170376 -0.26119126 -0.0256562728
## [19,] 0.04973950 -0.078001615 0.133111868 0.04732604 0.2523204698
## [20,] 0.14257428 0.063143467 0.089116551 -0.26118159 -0.0093352177
## [,16] [,17] [,18] [,19] [,20]
## [1,] -0.01384342 -0.067871422 -0.25650673 0.004802103 -0.003699918
## [2,] -0.02062008 0.277345131 0.10438228 0.126144519 0.004075751
## [3,] -0.10721192 -0.006292592 0.15382543 0.111661091 -0.109285077
## [4,] -0.10574985 0.148046060 -0.14170449 0.278656847 0.060630504
## [5,] 0.22364861 0.365979412 -0.10260952 0.169789007 -0.140011853
## [6,] 0.52859905 -0.049225541 0.01544366 -0.015884107 0.282971054
## [7,] -0.02656110 0.195328943 0.27948658 0.083009184 -0.081789213
## [8,] -0.14015556 -0.193463020 -0.21951535 -0.246654128 -0.174935539
## [9,] -0.00186447 -0.103173890 -0.07772574 0.187106926 0.027619681
## [10,] -0.31908152 0.320145783 0.04431142 -0.121793519 0.045548253
## [11,] -0.19906332 0.082426737 0.02772117 0.049739503 0.142574279
## [12,] -0.37992838 -0.323441114 0.06396774 -0.078001615 0.063143467
## [13,] -0.08672680 -0.049588963 -0.11717038 0.133111868 0.089116551
## [14,] 0.08508370 0.296528695 -0.26119126 0.047326036 -0.261181587
## [15,] 0.19561541 0.295618382 -0.02565627 0.252320470 -0.009335218
## [16,] 1.00000000 0.100767497 0.01373553 0.126416694 -0.197290469
## [17,] 0.10076750 1.000000000 0.36931421 0.162260155 -0.024033161
## [18,] 0.01373553 0.369314211 1.00000000 0.218975775 0.208214618
## [19,] 0.12641669 0.162260155 0.21897577 1.000000000 0.044153708
## [20,] -0.19729047 -0.024033161 0.20821462 0.044153708 1.000000000
diag(kor) <- NA # korelacja zmiennej z samą sobą nas nie interesuje
max(abs(kor), na.rm = TRUE) # najsilniejsza korelacja w szumie
## [1] 0.5285991
which(abs(kor) == max(abs(kor), na.rm = TRUE), arr.ind = TRUE) # która para zmiennych dała tę najsilniejszą korelację
## row col
## [1,] 16 6
## [2,] 6 16
sum(abs(kor) > 0.3, na.rm = TRUE) / 2 # ile par ma |r| > 0.3? (każda para liczona raz)
## [1] 19
Przełomowe odkrycie: zmienna 6 koreluje ze zmienną 16 (r ≈ 0,53)!
Przy 20 zmiennych mamy 190 par (20 · 19 / 2 = 190 par), więc „coś” wyjdzie zawsze.
Z zastrzeżeniem !
Działamy tu na przykładzie kontrolowanego jednak szumu generowanego pseudolosowo. W życiu może być inaczej.
W uproszczeniu !
Próg istotności statystycznej (p < 0,05) dla n = 30 to mniej więcej |r| > 0,36. Na 190 par spodziewamy się więc około 9–10 korelacji „istotnych statystycznie”, które w całości są fałszywymi alarmami. Tak właśnie działa poziom istotności 5%: 5% ze 190 to 9,5.
Jeśli w badaniu mierzymy wiele zmiennych i korelujemy „wszystko ze wszystkim”, to znalezienie kilku istotnych korelacji o niczym jeszcze nie świadczy. Szczególnie niebezpieczne jest wybranie najsilniejszej z nich i opisanie jej, jakby była hipotezą od początku. To jedna z form p-hackingu.
Pokusa zabłyśnięcia oraz konieczność zrewanżowania się za przyznany grand może prowadzić niektórych w tę stronę.
wyniki <- replicate(1000, {
k <- cor(matrix(rnorm(30 * 20), ncol = 20))
sum(abs(k[upper.tri(k)]) > 0.3)
})
mean(wyniki) # średnio ok. 20 par z |r| > 0,3, powtarzajmy wielokrotnie bez set.seed()
## [1] 20.372
To można policzyć też tak.
Przy braku prawdziwej zależności statystyka
\[ t = \frac{r\sqrt{n-2}}{\sqrt{1-r^2}} \]
ma rozkład \(t\)-Studenta z \(n-2\) stopniami swobody.
Dla \(r = 0{,}3\) i \(n = 30\):
\[ t = \frac{0{,}3 \cdot \sqrt{28}}{\sqrt{1-0{,}09}} = \frac{0{,}3 \cdot 5{,}292}{0{,}954} \approx 1{,}66 \]
Prawdopodobieństwo, że \(|t|\) przekroczy \(1{,}66\) przy 28 stopniach swobody, wynosi około \(0{,}107\).
Oznacza to, że każda pojedyncza para czystego szumu ma mniej więcej 11% szans na \(|r| > 0{,}3\). Przy 190 parach daje to średnio
\[
190 \cdot 0{,}107 \approx 20 \text{ par.}
\]
Można posprawdzać jeszcze w R
n <- 30; r <- 0.3
t <- r * sqrt(n - 2) / sqrt(1 - r^2)
p <- 2 * pt(-t, df = n - 2) # ok. 0,107
# pt(-1.664, df = 28) # ok. 0,0536 -> Funkcja pt(x, df) w R zwraca pole pod krzywą rozkładu t na lewo od x. Dla x = −1,66
# Czyli w około 5,4% przypadków t wychodzi mniejsze niż −1,66, co odpowiada korelacji r < −0,3.
# Prawy ogon. Rozkład t jest symetryczny, więc na prawo od +1,66 leży tyle samo, około 0,0536. To przypadki z r > 0,3.
# 0,0536 + 0,0536 ≈ 0,107
p * 190 # ok. 20
📖 Fragment do samodzielnej lektury – na zajęciach tylko go sygnalizujemy.
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. Symbol Newtona, czytany „n po k” (liczba
kombinacji bez powtórzeń)
# 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
Symbol Newtona wzór:
\[ \binom{n}{k} = \frac{n!}{k!\,(n-k)!}, \qquad 0 \le k \le n \]
gdzie \(n! = 1 \cdot 2 \cdot \ldots \cdot n\), a z definicji \(0! = 1\).
Przydatne własności:
\[ \binom{n}{0} = \binom{n}{n} = 1, \qquad \binom{n}{k} = \binom{n}{n-k} \]
Przykład (Lotto). Liczba sposobów wybrania 6 liczb spośród 49:
\[ \binom{49}{6} = \frac{49!}{6!\,43!} = 13\,983\,816 \]
więc prawdopodobieństwo trafienia „szóstki” jednym zakładem wynosi
\[
P = \frac{1}{\binom{49}{6}} \approx 7{,}15 \cdot 10^{-8}.
\]
Szybciutko w R
choose(49, 6)
## [1] 13983816
1 / choose(49, 6)
## [1] 7.151124e-08
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).
\[
n! = 1 \cdot 2 \cdot 3 \cdot \ldots \cdot n = \prod_{i=1}^{n} i,
\qquad 0! = 1
\]
Silnię można też zdefiniować rekurencyjnie:
\[ n! = n \cdot (n-1)!, \qquad 0! = 1 \]
Interpretacja kombinatoryczna: \(n!\) to liczba permutacji, czyli sposobów ustawienia \(n\) różnych elementów w kolejności. Na przykład 3 książki można ustawić na półce na \(3! = 6\) sposobów.
Silnia pojawia się we wzorze na symbol Newtona.
Szybciutko w R
# 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
ZADANIE
Mądrość tłumu
W 1907 roku Francis Galton opisał konkurs na jarmarku, w którym kilkaset osób zgadywało wagę wołu. Mediana odpowiedzi okazała się zaskakująco bliska prawdziwej wadze. Sprawdźmy to na sobie!
Pytanie do sali: ile metrów wysokości ma Pałac Kultury i Nauki w Warszawie (z iglicą)? Każdy podaje swoją odpowiedź, a my wpisujemy je na bieżąco do wektora.
zgadywania <- c(..., ..., ...)
zgadywania <- c(150, 300, 230, 180, 400, 250, 210, 120, 280, 260, 190, 500) # "jakieś" odpowiedzi z sali
################
prawda <- 237
mean(zgadywania); median(zgadywania)
## [1] 255.8333
## [1] 240
abs(mean(zgadywania) - prawda) # błąd "tłumu" (średnia)
## [1] 18.83333
abs(median(zgadywania) - prawda) # błąd "tłumu" (mediana)
## [1] 3
# Ilu z nas indywidualnie trafiło lepiej niż mediana grupy?
sum(abs(zgadywania - prawda) < abs(median(zgadywania) - prawda))
## [1] 0
Efekt działa, gdy błędy poszczególnych osób są niezależne i znoszą się nawzajem. Pytanie kontrolne: co by się stało, gdybym przed zbieraniem odpowiedzi powiedział głośno „pewnie ze 400 metrów”? (efekt zakotwiczenia)
PYTANIE
Usuń z wektora zgadywania największą wartość i porównaj
średnią i medianę przed i po. Która miara zmieniła się bardziej?
bez_max <- zgadywania[-which.max(zgadywania)]
mean(zgadywania) - mean(bez_max)
median(zgadywania) - median(bez_max)
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 wynikpaste() domyślnie dodaje spację, cat() nie
dodaje separatora, chyba że jest to określonePoniż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\"!'); tekst2 # 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
## 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.
Szyfr Cezara
Ponoć Juliusz Cezar szyfrował listy, przesuwając każdą literę o 3 pozycje w alfabecie.
Zróbmy to w R, korzystając z
wbudowanego wektora LETTERS, indeksowania i reszty z
dzielenia %%.
# Zdekoduj wiadomość od prowadzącego
paste(LETTERS[c(9, 18, 15, 14, 13, 1, 14)], collapse = "")
## [1] "IRONMAN"
imie <- "ania"
litery <- strsplit(imie, "")[[1]] # strsplit() zwraca listę, stąd [[1]]
pozycje <- match(litery, LETTERS) # na której pozycji alfabetu jest każda litera?
pozycje
## [1] NA NA NA NA
zaszyfrowane <- paste(LETTERS[(pozycje + 3 - 1) %% 26 + 1], collapse = "")
zaszyfrowane
## [1] "NANANANA"
# A co z polskimi znakami?
match(strsplit("łucja", "")[[1]], LETTERS) # NA – "ł" nie ma w alfabecie łacińskim!
## [1] NA NA NA NA NA
Odszyfrujmy słowo "vwdwbvwbnd", przesuwając litery o 3
pozycje wstecz.
poz <- match(strsplit("vwdwbvwbnd", "")[[1]], LETTERS)
paste(LETTERS[(poz - 3 - 1) %% 26 + 1], collapse = "")
Własna funkcja.
szyfruj <- function(tekst, k) {
poz <- match(strsplit(tekst, "")[[1]], LETTERS)
paste(LETTERS[(poz + k - 1) %% 26 + 1], collapse = "")
}
szyfruj("ania", 3) # "dqld"
## [1] "NANANANA"
szyfruj("dqld", -3) # "ania"
## [1] "NANANANA"
szyfruj("vwdwbvwbnd", -3) #
## [1] "NANANANANANANANANANA"
Funkcja ls() pokazuje nam wszystkie
obiekty w środowisku pracy (workspace)
ls()
## [1] "a" "analiza"
## [3] "bez_max" "combinations"
## [5] "concat_wektorow" "czestosc_szostek"
## [7] "d" "dane"
## [9] "dane_lista" "dane_norm"
## [11] "dane_std" "df"
## [13] "dojazd" "e"
## [15] "elements" "factor_sex_vector"
## [17] "factor_temperatura_vector" "factor_vector_plci"
## [19] "filozofia" "gosciu"
## [21] "gosciuwa" "h"
## [23] "id" "id2"
## [25] "imie" "imiona_studentow"
## [27] "kierunek" "kolor"
## [29] "kolory" "kor"
## [31] "krok1" "krok2"
## [33] "krok3" "krok4"
## [35] "krok5" "krok6"
## [37] "krok7" "liczba_szostek"
## [39] "liczby" "liczby_f"
## [41] "liczby_lista" "likert"
## [43] "lista_bez_nazw" "litery"
## [45] "macierz_przyklad" "matematyka"
## [47] "miesiace" "model"
## [49] "moja_dokladna_liczba" "moja_liczba"
## [51] "moja_lista" "moja_wart_logiczna"
## [53] "moja_wart_logiczna2" "moje_slowo"
## [55] "mozliwe" "n"
## [57] "name_vector" "nazwa_przedmiotu"
## [59] "nowa_planeta" "oceny"
## [61] "oceny_matrix" "oceny_matrix2"
## [63] "oceny_odwrocone" "oceny_stat_R"
## [65] "oceny_text" "oczekiwana"
## [67] "odp" "osoba"
## [69] "owoce" "p"
## [71] "planets_df" "plec"
## [73] "polskie_nazwy_mtcars" "poprawne"
## [75] "poz" "poza_przedzialem"
## [77] "poziomy" "pozycja"
## [79] "pozycje" "prawda"
## [81] "proby" "q"
## [83] "r" "rings_vector"
## [85] "rnorm_result" "runif_result"
## [87] "rzuty" "sekwencjaNumericow"
## [89] "sekwencjaNumericow2" "sekwencjaNumericow3"
## [91] "semestry" "sex_vector"
## [93] "sprzedaz" "srednia"
## [95] "srednia_ocen" "srednia_semestr"
## [97] "stat_R" "suma_wektorow"
## [99] "szesc" "szpital"
## [101] "szum" "szyfruj"
## [103] "t" "tablica_przyklad"
## [105] "tekst" "tekst2"
## [107] "tekst3" "tekst4"
## [109] "temperatura" "temperatura_vector"
## [111] "trufolsy" "v"
## [113] "vector_plci" "warunki"
## [115] "wektor" "wektor_1"
## [117] "wektor_2" "wektor_z_brakami"
## [119] "wiek" "wych_fiz"
## [121] "wyniki" "wysoka"
## [123] "x" "y"
## [125] "za_co" "zaszyfrowane"
## [127] "zdali" "zgadywania"
Funkcja rm() usuwa poszczególne obiekty
ze środowiska
rm(oceny_matrix)
Kombinacja powyższych funkcji czyści cały workspace
rm(list=ls())
Uwaga o rm(list = ls())
Uwaga: rm(list = ls()) usuwa tylko obiekty. Nie odłącza
wczytanych pakietów i nie przywraca zmienionych ustawień
(options()). Jeśli chcemy naprawdę zacząć od zera,
restartujemy sesję R: Session → Restart R lub
Ctrl + Shift + F10 (na Macu: Cmd + Shift +
F10).
Gdyby nam zależało na pozostawieniu pojedynczych obiektów?
rm(list=setdiff(ls(), "oceny_stat_R"))
📖 Fragment do samodzielnej lektury – na zajęciach tylko go sygnalizujemy.
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 849528 45.4 1362484 72.8 1362484 72.8
## Vcells 1615758 12.4 8388608 64.0 8339338 63.7
library() vs require() oraz
pakiet::funkcja()
Pakiety: library() czy
require()?
Obie funkcje wczytują zainstalowany pakiet, ale różnie reagują na jego brak.
library(pakiet) |
require(pakiet) |
|
|---|---|---|
| Gdy pakietu nie ma | błąd – skrypt się zatrzymuje | ostrzeżenie – skrypt idzie dalej |
| Co zwraca | (niewidocznie) listę wczytanych pakietów | TRUE lub FALSE |
| Kiedy używać | na początku skryptu (domyślnie) | wewnątrz warunków, gdy brak pakietu da się obsłużyć |
library(pakietKtoregoNieMa) # błąd
## Error in `library()`:
## ! there is no package called 'pakietKtoregoNieMa'
wynik <- suppressWarnings(require(pakietKtoregoNieMa))
wynik # FALSE
## [1] FALSE
# Notacja pakiet::funkcja() – użycie funkcji bez wczytywania całego pakietu
stats::sd(c(1, 2, 3, 4))
## [1] 1.290994
base::mean(c(1, 2, 3, 4))
## [1] 2.5
Notacja pakiet::funkcja() przydaje się też wtedy, gdy
dwa pakiety mają funkcję o tej samej nazwie – od razu widać, której
używamy.
| Struktura | Wymiary | Typy elementów | Tworzenie | Dostęp |
|---|---|---|---|---|
| wektor | 1 | jeden typ | c(), :, seq(),
rep() |
x[i] |
| macierz | 2 | jeden typ | matrix() |
m[i, j] |
| tablica | dowolnie wiele | jeden typ | array() |
a[i, j, k] |
| factor | 1 | kategorie (poziomy) | factor() |
f[i], levels(f) |
| ramka danych | 2 | każda kolumna może mieć inny typ | data.frame() |
df[i, j], df$kolumna |
| lista | 1 (ale zagnieżdżalna) | dowolne obiekty | list() |
l[[i]], l$nazwa |
Materiały z warsztatów Data Science w zastosowaniach biznesowych - Warsztaty z wykorzystaniem programu R Uniwersytet Warszawski, Wydział Nauk Ekonomicznych
DataCamp.com Introduction to R
Materiały dr Bartosza Maćkiewicza:
Biecek, Przemysław, “Przewodnik po pakiecie R” http://www.biecek.pl/R/ https://cran.r-project.org/doc/contrib/Biecek-R-basics.pdf
Gągolewski, Marek, “Programowanie w języku R”, “Deep R Programming”, etc. https://deepr.gagolewski.com/index.html
Crawley, Michael J. The R book. John Wiley & Sons, 2012 https://onlinelibrary.wiley.com/doi/book/10.1002/9781118448908
Kabacoff, Robert. R in action: data analysis and graphics with R. Manning Publications Co., 2015 https://www.cs.uni.edu/~jacobson/4772/week11/R_in_Action.pdf
Fox, John, Michael Friendly, and Sanford Weisberg. “Hypothesis tests for multivariate linear models using the car package.” R J 5.1 (2013) https://www.researchgate.net/publication/285736465_Hypothesis_Tests_for_Multivariate_Linear_Models_Using_the_car_Package
Hothorn, Torsten, et al. “Package ‘multcomp’.” Simultaneous inference in general parametric models. Project for Statistical Computing, Vienna, Austria (2016) https://cran.r-project.org/web/packages/multcomp/vignettes/generalsiminf.pdf
claude.ai
chatgpt.com
Bar-Hillel, Maya, and Willem A. Wagenaar. “The perception of randomness.” Advances in Applied Mathematics 12.4 (1991)
Burns, Patrick. “The R Inferno” (2011) https://www.burns-stat.com/documents/books/the-r-inferno/
Galton, Francis. “Vox populi.” Nature 75 (1907)
Goldberg, David. “What every computer scientist should know about floating-point arithmetic.” ACM Computing Surveys 23.1 (1991)
Kahneman, Daniel, and Amos Tversky. “Subjective probability: A judgment of representativeness.” Cognitive Psychology 3.3 (1972)
R FAQ 7.31 “Why doesn’t R think these numbers are equal?” https://cran.r-project.org/doc/FAQ/R-FAQ.html
Ratcliff, Roger. “Methods for dealing with reaction time outliers.” Psychological Bulletin 114.3 (1993)
Simmons, Joseph P., Leif D. Nelson, and Uri Simonsohn. “False-positive psychology.” Psychological Science 22.11 (2011)
Stevens, Stanley Smith. “On the theory of scales of measurement.” Science 103.2684 (1946)
Stroop, J. Ridley. “Studies of interference in serial verbal reactions.” Journal of Experimental Psychology 18.6 (1935)
Vigen, Tyler. “Spurious Correlations” https://www.tylervigen.com/spurious-correlations
Wason, Peter C. “Reasoning about a rule.” Quarterly Journal of Experimental Psychology 20.3 (1968)
Whelan, Robert. “Effective analysis of reaction time data.” The Psychological Record 58.3 (2008)
Wickham, Hadley. “Advanced R”, 2nd ed. (2019) https://adv-r.hadley.nz/
pomarancz
szary
zielony
czerwony
niebieski