Amikor homogén, atomos adatok gyűjteményeivel dolgozol (például csupa szám vagy csupa karakter), a következő adatszerkezetek állnak rendelkezésre R-ben:
vector egydimenziós adatokhoz, egyetlen indexszel érhető el.matrix kétdimenziós, téglalap alakú (nem szaggatott) adatokhoz.array az általános N-dimenziós esethez. A matrix valójában csak egy array, ahol N=2.Python-programozóknak: Ez a rész ismerősnek tűnhet.
A NumPy-val való bármilyen hasonlóság nem véletlen (az R viszont valamivel régebbi, és mindez az alapnyelvében megtalálható).
A mátrix lényegében egy vektor, kiegészítve a sorok és oszlopok számával, amelyek meghatározzák az alakját.
Létrehozásukra van egy matrix() függvény:
# data already available
matrix(1:6, nrow=2, ncol=3)
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
# empty matrix to be filled in later
matrix(nrow=2, ncol=3)
#> [,1] [,2] [,3]
#> [1,] NA NA NA
#> [2,] NA NA NA
Fontos, hogy az adatok oszlopfolytonos sorrendben tárolódnak (azaz előbb lefelé, aztán jobbra). Ez ugyanaz, mint a Fortranban, a Matlabban és a Juliában, de eltér a C/C++-tól és a NumPy-tól.
A mátrix méreteit a dim() függvénnyel kérdezheted le, ami megadja a sorok és az oszlopok számát is; egyetlen méretet a nrow() vagy a ncol() ad vissza. A length() csak a mögöttes vektor hosszát adja vissza: az elemek teljes számát (a fenti példában 6-ot).
Néhány más függvény is mátrixot ad eredményül, például a str_split_fixed().
library(stringr)
str_split_fixed(c("split some words", "and some more"), " ", 3)
#> [,1] [,2] [,3]
#> [1,] "split" "some" "words"
#> [2,] "and" "some" "more"
Ez pontosan ugyanúgy működik, mint a vektorok indexelése, csak itt meg kell adnod a sort (sorokat) és az oszlopot (oszlopokat), mégpedig ebben a sorrendben. Ha egy dimenziót üresen hagysz, a teljes sort vagy oszlopot kapod meg.
Ugyanazt a mátrixot használva, mint fent:
m <- matrix(1:6, nrow=2, ncol=3)
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m[2, 3] # row 2, col 3
#> [1] 6
m[1:2, 3] # rows 1:2, col 3
#> [1] 5 6
m[2, ] # all of row 2
#> [1] 2 4 6
Figyeld meg, hogy ha egy részhalmazban csak egyetlen sor és/vagy oszlop van, akkor az vektorként tér vissza, nem mátrixként.
class(m)
#> [1] "matrix" "array"
class(m[2,])
#> [1] "integer"
Ez a fajta dimension reduction nem mindig kívánatos, különösen egy olyan függvényláncban, amely mátrix bemeneteket vár.
Ha megadod a drop=FALSE értéket, az garantálja, hogy mátrix bemenetből mátrix legyen a visszatérési érték. Figyeld meg, hogy az egysoros vagy egyoszlopos mátrix nem ugyanolyan típusú, mint egy vektor.
m[2, , drop = FALSE]
#> [,1] [,2] [,3]
#> [1,] 2 4 6
Az as.matrix() függvény vektort alakít egyoszlopos mátrixszá.
as.matrix(1:3)
#> [,1]
#> [1,] 1
#> [2,] 2
#> [3,] 3
A vektorokhoz hasonlóan a mátrix egyes részeit is egyszerűen módosíthatod új értékek hozzárendelésével.
m[, 2] <- c(10, 20)
m
#> [,1] [,2] [,3]
#> [1,] 1 10 5
#> [2,] 2 20 6
Vektoroknál elég volt a c() függvény ahhoz, hogy adatot beszúrj vagy hozzáfűzz. A mátrixok szerkezete összetettebb, ezért körültekintőbben kell megadnod, mire van szükség.
Az rbind() és a cbind() függvény új sort vagy oszlopot fűz a mátrixhoz.
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m2 <- matrix(7:9, nrow = 1, ncol = 3)
rbind(m, m2)
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
#> [3,] 7 8 9
Egy sor vagy oszlop törléséhez negatív indexelést használsz, ahogy a vektoroknál.
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m[, -2]
#> [,1] [,2]
#> [1,] 1 5
#> [2,] 2 6
Minden vektoros függvény működik mátrixokon is, az összes elemre alkalmazva. Csakhogy elveszíthetik a mátrix szerkezetét, és vektort adnak vissza.
sqrt(m)
#> [,1] [,2] [,3]
#> [1,] 1.000000 1.732051 2.236068
#> [2,] 1.414214 2.000000 2.449490
sum(m)
#> [1] 21
m[m > 3]
#> [1] 4 5 6
Mátrixoknál ezen kívül hasznos lehet a colSums() és a rowSums() függvény.
Ebben az esetben a dimenziócsökkentés szándékos, ezért a visszatérési értékek vektorok.
# m as defined above
rowSums(m)
#> [1] 9 12
colSums(m)
#> [1] 3 7 11
Hasonlóképpen léteznek rowMeans() és colMeans() függvények az átlagok kiszámítására.
Más fogalmaknál láttuk, hogy a Tidyverse könyvtárai leegyszerűsíthetik a vektorokon és listákon (purrr) vagy adattáblákon (dplyr) végzett műveleteket.
A mátrixok és a tömbök azonban nagyrészt kívül esnek a Tidyverse bemeneteinek körén (legalábbis egyelőre).
Az apply() függvény a Base R része, és továbbra is ez a legjobb eszköz, ha finomabb irányítást szeretnél a mátrixprogramozás felett.
A paraméterek sorrendben a következők:
A rowMeans() függvény megtalálható a Base R-ben, de apply() és mean() segítségével mi magunk is utánozhatjuk.
randm <- sample(1:10, 16, replace = TRUE) |> matrix(4, 4)
randm
#> [,1] [,2] [,3] [,4]
#> [1,] 4 9 8 3
#> [2,] 6 8 5 5
#> [3,] 4 2 1 2
#> [4,] 4 2 5 4
apply(randm, 1, mean)
#> [1] 6.00 6.00 2.25 3.75
rowMeans(randm)
#> [1] 6.00 6.00 2.25 3.75
Ez a rész az Exercism hatókörén kívül eső szintű matematikai ismereteket feltételez.
Nyugodtan hagyd ki, ha semmit nem értesz belőle.
Az R viszont adattudományi nyelv, és sok felhasználó jártas az alkalmazott matematikában és a statisztikában. Ez a tömör összefoglaló néhány tanulónak érdekes lehet.
A modern R „nyilvánvalóan” adattudományi nyelv: elsősorban táblázatos adatokkal való munkára tervezték, és népszerű olyan felhasználók körében, mint a statisztikusok, az epidemiológusok és a társadalomtudósok. Ezen a területen a fő versenytárs a Python tudományos ökoszisztémája, olyan csomagokkal, mint a Pandas vagy a Polars.
A korábbi évtizedekben azonban az R-t potenciális mérnöki nyelvként is számon tartották, amely közvetlenül a Matlabbal versenyzett.
Bármilyen meglepő, a Base R a mai napig támogatja a lineáris algebrai műveletek teljes skáláját vektorokon és mátrixokon. Nem világos, hogy ezek ma mennyire jelennek meg a felhasználói kódban, de az ilyen technikák belsőleg továbbra is nagyon fontosak a statisztikai függvények megvalósításához.
Az R lineáris algebráját még mindig tanítják néhány egyetemi kurzuson, például ezen az ökonometriaórán. A matematikához (nem a programozáshoz) jó útmutatót találsz a 3Blue1Brown csatornán.
Az Exercism-ön belül a lineáris algebra legteljesebb tárgyalása a Julia tantervében található (ugyanazok a szerzők, mint az R tantervét).
Úgy gondoljuk, hogy a komoly lineáris algebrai munkához ma a Julia (vagy a Python ott, ahol a numpy.linalg modul elérhető) megfelelőbb nyelv lenne, mint az R.
Egy négyzetes (N x N)) A mátrix főátlója a bal felső saroktól (A[1,1]) a jobb alsóig (A[N,N]) tart.
A diag() függvény viselkedése attól függ, milyen argumentumot adsz át neki.
Egy v vektor esetén a diag(v) átlós mátrixot hoz létre.
diag(1:3)
#> [,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 2 0
#> [3,] 0 0 3
Egy négyzetes A mátrix esetén a diag(A) a főátlón lévő elemeket adja vissza vektorként.
A
#> [,1] [,2] [,3]
#> [1,] 10 5 2
#> [2,] 9 3 7
#> [3,] 8 10 4
diag(A)
#> [1] 10 3 4
Egyetlen k egész szám esetén a diag(k) egy k × k egységmátrixot hoz létre: talán nem erre számítottál.
Még a komoly R-tankönyvek is hajlamosak ilyen megjegyzéseket fűzni ilyenkor: „Ki érti ezt?”.
diag(3)
#> [,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 1 0
#> [3,] 0 0 1
A sorok és oszlopok felcseréléséhez a t() függvény bármely mátrixot transzponál.
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
t(m)
#> [,1] [,2]
#> [1,] 1 2
#> [2,] 3 4
#> [3,] 5 6
Komplex értékű mátrix esetén az eredmény nem a komplex konjugált. A Conj(t(A)) hívással kapod meg az A mátrix konjugált transzponáltját (adjungáltját).
Magasabb dimenziós tömböknél az aperm() permutálja a dimenziókat.
Egy négyzetes A mátrix inverzéhez a solve(A) függvényt használhatjuk. Néhányunk szerint ez a szintaxis ... meglepő.
Ha egy mátrixot megszorzunk az inverzével, az egységmátrixot kapjuk (apró kerekítési hibáktól eltekintve).
A <- matrix(sample(1:10, 9, replace = TRUE), 3, 3)
A
#> [,1] [,2] [,3]
#> [1,] 10 5 2
#> [2,] 9 3 7
#> [3,] 8 10 4
solve(A)
#> [,1] [,2] [,3]
#> [1,] 0.16666667 0.00000000 -0.08333333
#> [2,] -0.05747126 -0.06896552 0.14942529
#> [3,] -0.18965517 0.17241379 0.04310345
A %*% solve(A) |> round(digits = 10)
[,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 1 0
#> [3,] 0 0 1
A szokásos aritmetikai operátorok, mint a +, -, *, ^, elemenként működnek a mátrixokon. Ha a dimenziók nem egyeznek, az újrahasznosítás szokásos szabályai érvényesek.
m
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
m * m
#> [,1] [,2] [,3]
#> [1,] 1 9 25
#> [2,] 4 16 36
# same calculation, different syntax
m ^ 2
#> [,1] [,2] [,3]
#> [1,] 1 9 25
#> [2,] 4 16 36
A vektor- és mátrixszorzás egyéb fajtáinak bemutatása meghaladja a kereteinket, de íme egy nagyon rövid összefoglaló:
%*% a matmult() infix operátoros formája, amely a mátrixszorzatot adja.%o% az outer() infix operátoros formája, amely a külső szorzatot adja.crossprod() a keresztszorzatot adja.