Kernfragen dieser Lehreinheit
- Wie definiere ich Vektoren und wie kann ich mit ihnen Rechenoperationen durchführen?
- Wie definiere ich Matrizen und wie kann ich mit ihnen Rechenoperationen mit ihnen durchführen?
- Wie bestimme ich Determinante und Inverse einer Matrix?
- Wie nutze ich Matrixoperationen um Deskriptivstatistiken zu bestimmen?
Bisher haben wir gelernt, dass viele statistische Größen mithilfe von Summen, Mittelwerten, Quadraten, Abweichungen und weiteren recht einfachen Rechenoperationen bestimmt werden können. Diese können oft noch einfacher mithilfe von Matrizen und Vektoren dargestellt werden. Vielleicht sind Ihnen Matrizen aus der Schule bekannt. Falls nicht, ist das kein Problem, denn dieser Beitrag wird Vektoren und Matrizen ausführlich erklären. Matrixalgebra wird bspw. auch in Eid, et al. (2017) im Anhang B: Matrixalgebra ab Seite 1051 behandelt.
Vektoren kennen wir bereits aus den ersten Datensätzen, die wir kennengelernt haben. Bspw. enthält ein Variablenvektor einer Person einfach nur die Einträge der Variablen aus dem Datensatz dieser spezifischen Person. Matrizen haben wir im Grunde auch schon kennengelernt. Ein Datensatz ist eng mit einer Matrix verwandt. Genauso wie ein Datensatz, besteht auch eine Matrix aus Zeilen und Spalten. Der Hauptunterschied ist, dass bei einer Matrix nur numerische Inhalte, also Zahlen, erlaubt sind. Wenn wir Daten in R
verarbeiten wollen, wird der Datensatz oft in Matrizen umtransformiert (falls er vorher nicht-numerische Inhalte enthielt) und dann mit geeigneten Operationen, sogenannten Matrixoperationen, verarbeitet.
R
ist eine vektorbasierte Programmiersprache, was bedeutet, dass möglichst viel mit Vektor- oder Matrixoperationen durchgeführt werden soll, da diese besonders optimiert (und damit besonders schnell) sind. Um davon Gebrauch zu machen, müssen wir uns mit diesen Operationen vertraut machen:
Vektoren
Vektoren werden häufig (aber nicht immer, Ausnahmen bestätigen die Regel) in Kleinbuchstaben dargestellt.
Seien x
und y
zwei Vektoren, die wir mit dem Zuordnungspfeil <-
und mit der Vektorfunktion c()
erstellen:
x <- c(1, 2, 3)
y <- c(10, 8, 6)
Vektoren werden Meistens als sogenannte Spaltenvektoren (dazu später mehr) dargestellt. x
und y
sehen also so aus
Die Elemente werden mit der jeweiligen Position im Vektor nummeriert. Das Element
Wir erkennen, dass den Elementen
Wir können auf Elemente eines Vektor mit eckigen Klammern zugreifen. Bspw. erhalten wir das 2. Element von Y (also quasi
y[2]
## [1] 8
Auch mehrere Elemente lassen sich auf diese Weise ausgeben. Sind wir bspw. am 2. bis 3. Element interessiert, können wir
y[2:3]
## [1] 8 6
schreiben. Interessiert uns hingegen das 1. und 3. Element, brauchen wir erneut einen Vektor, der die Position auswählt:
y[c(1,3)]
## [1] 10 6
Die Addition von Vektoren funktioniert elementenweise. Das bedeutet, dass das 1. Element des 1. Vektors und das 1. Element des 2. Vektors miteinander addiert werden und das 2. Element des 1. Vektors mit dem 2. Element des 2. Vektors miteinander addiert werden, etc.
Elementeweise Additionen funktionieren super simpel, indem wir x
und y
einfach mit +
verknüpfen.
x + y # Addition
## [1] 11 10 9
Wenn x
und y
nicht die selbe Länge haben, ist es in R
oft so, dass die Vektoren künstlich verlängert werden, um verrechnet zu werden. Dies sollten wir immer im Hinterkopf behalten.
z <- c(1:6) # Zahlen 1 bis 6
z + y
## [1] 11 10 9 14 13 12
z
ist hier doppelt so lang wie y
, sodass in der Addition y
einfach zweimal hintereinander geschrieben wird, damit die Addition möglich ist, denn eine Addition bei Vektoren (und auch Matrizen) funktioniert nur, wenn die beiden Elemente das gleiche Format haben! Das ist eine Besonderheit von R
, was hin und wieder zu Problemen oder Fehlern führen kann.
Wenn wir einen Vektor mit einer Zahl, also einem Skalar, multiplizieren, so bewirkt dies eine elementenweise Multiplikation mit dieser Zahl. Wenn wir beispielsweise
Für
In R
sieht das so aus
3*x
## [1] 3 6 9
Genauso können wir auch jedes Element durch 2 Teilen, indem wir mit
1/2*x
## [1] 0.5 1.0 1.5
Wenn wir mit
-1*x
## [1] -1 -2 -3
Jedes Element wird also mit
Zwei Vektoren der gleichen Länge können in R
auch miteinander multipliziert werden. Achtung, dies ist eine spezielle Art der Multiplikation, die in R
durchgeführt wird, nämlich wieder die elementenweise Multiplikation.
x*y
## [1] 10 16 18
In R
können den Elementen von Vektoren auch Namen vergeben werden. Bspw. könnten x
und y
die Anzahl von Obst auf der Einkaufsliste von Xavian und Yvonne repräsentieren. In R
geht das so
names(x) <- c("Orangen", "Äpfel", "Bananen")
names(y) <- c("Orangen", "Äpfel", "Bananen")
x
## Orangen Äpfel Bananen
## 1 2 3
y
## Orangen Äpfel Bananen
## 10 8 6
Die Länge eines Vektors und damit die maximale Anzahl an Elementen erhalten wir mit
length(x)
## [1] 3
Matrizen
Ein Vektor ist eine eindimensionale Sammlung von Zahlen. Die Elemente werden einfach durchnummeriert. Eine Matrix ist ein zweidimensionales Objekt, welches aus einer Vielzahl von Vektoren gleicher Länge besteht, welche aneinander “geklebt” werden. Matrizen werden oft in Großbuchstaben beschrieben. Elemente von Matrizen hingegen in Kleinbuchstaben. Auch hier ist das nicht wirklich einheitlich geregelt.
Eine Matrix
Die 3 Zeilenvektoren sind
Wir bemerken, dass die Indizes der Elemente uns die Position in der Matrix angeben. Bspw. ist
Die gerade behandelten Vektoren können wir ganz leicht zu einer Matrix machen, indem wir den Befehl as.matrix
bspw. auf x
anwenden. Dieser Befehl erzeugt eine 3x1 Matrix - also aus mathematischer Sicht eigentlich einen Spaltenvektor.
as.matrix(x)
## [,1]
## Orangen 1
## Äpfel 2
## Bananen 3
Wir können die beiden Vektoren auch zu einer Matrix kombinieren, indem wir sie bspw. als zwei Zeilenvektoren mit dem Befehl cbind
(was für column binding steht) zusammenfügen - genauso geht dies auch mit rbind
(was für row binding steht):
A <- cbind(x, y)
A
## x y
## Orangen 1 10
## Äpfel 2 8
## Bananen 3 6
B <- rbind(x, y)
B
## Orangen Äpfel Bananen
## x 1 2 3
## y 10 8 6
Die Matrix
Wir können nun bspw. den Eintrag [1, 2]
, wobei der 1. Eintrag immer für die Zeile und der 2. für die Spalte steht:
B[1, 2]
## [1] 2
Dies entspricht der Anzahl der Äpfel, die Xavian gekauft hat.
Eine ganze Zeile oder Spalte erhalten wir, indem wir eines der Elemente in der Indizierung frei lassen:
B[1, ] # 1. Zeile
## Orangen Äpfel Bananen
## 1 2 3
B[, 2] # 2. Spalte
## x y
## 2 8
B[1,]
ist die Anzahl an Obst von Xavian und B[,2]
ist die Anzahl an Äpfel von Xavian und Yvonne.
So wie A
und B
erzeugt wurden, ist ersichtlich, dass die Spalten von A
die Zeilen von B
sind. Wir können Zeilen und Spalten auch vertauschen, indem wir die Matrix transponieren, indem wir den Befehl t()
auf die Matrix anwenden:
A
## x y
## Orangen 1 10
## Äpfel 2 8
## Bananen 3 6
t(A)
## Orangen Äpfel Bananen
## x 1 2 3
## y 10 8 6
B
## Orangen Äpfel Bananen
## x 1 2 3
## y 10 8 6
Wir erkennen, dass die Matrix B
gerade die Transponierte von A
ist! Die Matrixaddition funktioniert genauso wie die von Vektoren. Sie wird elementenweise durchgeführt. Allerdings müssen dafür die Matrizen dasselbe Format haben, also gleich viele Zeilen und Spalten besitzen. Das Format wird üblicherweise
Die beiden Matrizen A
und B
lassen sich nicht addieren, da sie nicht das richtige Format haben:
A + B
## Error in A + B : non-conformable arrays
Eine
t(A) + B
## Orangen Äpfel Bananen
## x 2 4 6
## y 20 16 12
Dies kommt zum selben Ergebnis, wie als hätten wir jeden Eintrag von
B * 2 # skalare Multiplikation
## Orangen Äpfel Bananen
## x 2 4 6
## y 20 16 12
Matrizen lassen sich in R
auch elementenweise multiplizieren. Dafür müssen sie, wie bei der Addition auch, das gleiche Format haben.
t(A) * B
## Orangen Äpfel Bananen
## x 1 4 9
## y 100 64 36
ergibt das selbe, wie als wenn wir jeden Eintrag von
B^2
## Orangen Äpfel Bananen
## x 1 4 9
## y 100 64 36
Es entsteht eine neue Matrix der Dimension
Drehen wir das Ganze um, erhalten wir
Hier entsteht eine Matrix vom Format
Der Operator in R
hierfür heißt %*%
(verwenden wir stattdessen *
, so wird eine elementenweise Multiplikation durchgeführt, was etwas komplett anderes ist!):
A %*% B # Matrixprodukt A*B
## Orangen Äpfel Bananen
## Orangen 101 82 63
## Äpfel 82 68 54
## Bananen 63 54 45
B %*% A # Matrixprodukt B*A
## x y
## x 14 44
## y 44 200
An den Ergebnissen erkennen wir auch, dass Matrixprodukte nicht kommutativ sind, also die Reihenfolge wichtig ist in der (matrix-)multipliziert wird.
So wirklich eine Bedeutung können wir diesen Matrixprodukten nicht zuordnen. Erstellen wir jedoch eine zweite Matrix
P <- matrix(c(.5, .3, .2))
rownames(P) <- c("Orange", "Äpfel", "Banane")
colnames(P) <- "Preis"
P
## Preis
## Orange 0.5
## Äpfel 0.3
## Banane 0.2
Nun führen wir Matrixmultiplikation durch:
B %*% P
## Preis
## x 1.7
## y 8.6
Wir erkennen, dass Xavian 1.7€ und Yvonne 8.6€ ausgeben muss. Angenommen es gäbe einen Konkurrenzladen, der andere Preise für das Obst angibt. Wir können dies in unsere Matrix matrix
-Befehl, um direkt eine Matrix zu erzeugen. Dieser Befehl nimmt zunächst einen Vektor mit den Elementen entgegen. Hier empfiehlt es sich durch Zeilenumbrüche Ordnung hereinzubringen. Anschließend sagen wir noch wie viele Zeilen (nrow
) und Spalten (ncol
) wir benötigen. Mit byrow = TRUE
geben wir an, dass die Matrix zeilenweise befüllt werden soll. In Laden A kosten Orangen 0.50€, Äpfel 0.30€ und Bananen 0.20€. In Laden B sind Bananen super teuer und kosten 1.00€. Hingegen sind Orangen und Äpfel extrem billig und kosten nur 0.05€. Wenn wir die Zeilen und Spalten der Preismatrix
P <- matrix(c(.5, .05,
.3, .05,
.2, 1), nrow = 3, ncol = 2,
byrow = TRUE)
rownames(P) <- c("Orangen", "Äpfel", "Bananen")
colnames(P) <- c("Laden A", "Laden B")
B %*% P
## Laden A Laden B
## x 1.7 3.15
## y 8.6 6.90
Obwohl die Bananen so teuer sind, macht es für Yvonne mehr Sinn in Laden B einzukaufen, während Xavian besser beraten ist bei Laden A zu bleiben (unter der etwas seltsamen Annahme, dass man alles in einem Laden kaufen muss).
Spezielle Matrizen
Eine quadratische Matrix ist eine Matrix mit gleich vielen Zeilen wie Spalten. Eine wichtige quadratische Matrix ist die Einheitsmatrix diag
, was eigentlich eine (quadratische) Diagonalmatrix erzeugt mit beliebigen Elementen auf der Diagonalen:
diag(3) # Einheitsmatrix 3x3
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 0 0 1
diag(1:3) # Diagonalmatrix mit Elementen 1,2,3 auf der Diagonalen
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 2 0
## [3,] 0 0 3
Wir können eine Matrix mit dem matrix
Befehl auch mit Hand füllen. Diesem übergeben wir einen Vektor und die Dimensionen der Matrix (data
werden die Daten, die wir in die Matrix schreiben wollen übergeben, nrow
und ncol
bestimmen die Anzahl der Zeilen und Spalten und mit byrow = T
zeigen wir an, dass wir die Matrix zeilenweise gefüllt bekommen möchten):
C <- matrix(data = c(1:9), nrow = 3, ncol = 3, byrow = T)
C
## [,1] [,2] [,3]
## [1,] 1 2 3
## [2,] 4 5 6
## [3,] 7 8 9
Wir können mit diag
auch wieder die Diagonalelemente einer Matrix erfahren:
diag(C)
## [1] 1 5 9
Determinanten und Invertierung
Die Inverse, also jenes Element, mit welchem wir (matrix-)multiplizieren müssen, um die Einheitsmatrix zu erhalten, lässt sich in R
mit dem solve
Befehl erhalten (dies geht nur bei quadratischen Matrizen):
solve(C)
## Error in solve.default(C) :
## system is computationally singular: reciprocal condition number = 2.59052e-18
Die Matrix C
lässt sich nicht invertieren, da sie singulär ist und damit nicht invertierbar. Dies bedeutet, dass es lineare Abhängigkeiten der Zeilen bzw. Spalten gibt. Wir können dies explizit prüfen, indem wir die Determinante bestimmen mit det
:
det(C)
## [1] 6.661338e-16
round(det(C), 14)
## [1] 0
Mit round
runden wir das Ergebnis auf die 14. Nachkommastelle. Eine Matrix ist genau dann invertierbar (also regulär im Vergleich zu singulär), wenn die Determinante dieser (quadratischen) Matrix nicht Null ist. Lineare Abhängigkeit bedeutet, dass die Zeilen oder Spalten durch Addition, Subtraktion und skalare Multiplikationen auseinander hervorgehen. Die lineare Abhängigkeit zwischen den Spalten wird ersichtlich, wenn wir von der 2. Spalte die 1. Spalte abziehen und das Ergebnis zur 3. Spalte addieren - also de facto
2*C[, 2] - C[, 1] # 2*2.Spalte - 1. Spalte rechnen ist gleich
## [1] 3 6 9
C[, 3] # 3. Spalte
## [1] 3 6 9
Hätten wir C^-1
gerechnet, so hätten wir eine elementeweise Invertierung durchgeführt:
C^-1
## [,1] [,2] [,3]
## [1,] 1.0000000 0.500 0.3333333
## [2,] 0.2500000 0.200 0.1666667
## [3,] 0.1428571 0.125 0.1111111
C^-1 %*% C # ist nicht die Einheitsmatrix
## [,1] [,2] [,3]
## [1,] 5.333333 7.166667 9.000000
## [2,] 2.216667 2.833333 3.450000
## [3,] 1.420635 1.799603 2.178571
C^-1 * C # elementeweise ergibt überall 1 - ist immer noch nicht die Einheitsmatrix!
## [,1] [,2] [,3]
## [1,] 1 1 1
## [2,] 1 1 1
## [3,] 1 1 1
Dies bedeutet, dass C^-1
in R
nicht die Invertierung betitelt sondern solve
!
Betrachten wir nun eine invertierbare Matrix D
:
D <- matrix(c(1, 0, 0,
1, 1, 1,
2, 4, 5), 3, 3, byrow = T)
det(D)
## [1] 1
Die Determinante von D
ist 1. Somit können wir D
invertieren. Das Produkt aus D
mit seiner Inversen ergibt gerade die 3x3 Einheitsmatrix:
solve(D)
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] -3 5 -1
## [3,] 2 -4 1
D %*% solve(D)
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 0 0 1
solve(D) %*% D
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 0 0 1
Das Produkt von solve
).
M <- matrix(c(2, 2, 3, 4), ncol = 2, nrow = 2, byrow = TRUE)
M
## [,1] [,2]
## [1,] 2 2
## [2,] 3 4
det(M)
## [1] 2
M[1,1]*M[2,2] - M[1,2]*M[2,1] # Determinante mit Hand
## [1] 2
K <- matrix(c(M[2,2], -M[1,2], M[2,1], M[1,1]), byrow = TRUE, ncol = 2, nrow = 2)
K # Kofaktorenmatrix
## [,1] [,2]
## [1,] 4 -2
## [2,] 3 2
1/(M[1,1]*M[2,2] - M[1,2]*M[2,1])*K # Inverse von M
## [,1] [,2]
## [1,] 2.0 -1
## [2,] 1.5 1
solve(M) # Inverse von M
## [,1] [,2]
## [1,] 2.0 -1
## [2,] -1.5 1
Wozu können wir die Matrixinvertierung benutzen? Sei
P <- matrix(c(.5, .1, 2,
.3, .05, 2.5,
.2, 1, 3), nrow = 3, ncol = 3,
byrow = TRUE)
colnames(P) <- c("Orangen", "Äpfel", "Bananen")
rownames(P) <- c("Laden A", "Laden B", "Laden C")
P
## Orangen Äpfel Bananen
## Laden A 0.5 0.10 2.0
## Laden B 0.3 0.05 2.5
## Laden C 0.2 1.00 3.0
Bananen sind nun überall teuer. In Laden A kostet das Obst bspw.:
P[1,]
## Orangen Äpfel Bananen
## 0.5 0.1 2.0
Angenommen Sie wüssten nun, dass Xavian in Laden A 15.40€, in Laden B 18.30€ und in Laden C (dem Premiumladen) 25.40€ bezahlen muss. Wie viel Obst will er kaufen in den Läden?
Es entsteht ein Gleichungssystem
b <- as.matrix(c(15.4, 18.3, 25.4))
rownames(b) <- c("Laden A", "Laden B", "Laden C")
b
## [,1]
## Laden A 15.4
## Laden B 18.3
## Laden C 25.4
Wenn wir nun
x <- solve(P) %*% b
x
## [,1]
## Orangen 2
## Äpfel 4
## Bananen 7
Xavian hat 2 Orangen, 4 Äpfel und 7 Bananen in den Läden gekauft. Hier die Probe:
P %*% x
## [,1]
## Laden A 15.4
## Laden B 18.3
## Laden C 25.4
b
## [,1]
## Laden A 15.4
## Laden B 18.3
## Laden C 25.4
Wir sehen also, dass die Inverse einer Matrix essentiell ist, um Gleichungssysteme zu lösen. Ist die Matrix invertierbar, so ist das Gleichungssystem eindeutig lösbar.
Nun aber genug von Obst, wir sind hier um Statistik zu betreiben. Im letzten Abschnitt schauen wir uns an, wie man einfache Statistiken mit Hilfe von Matrixoperationen bestimmt.
Statistiken mit Matrixoperationen bestimmen
# Daten laden
load(url('https://pandar.netlify.app/daten/fb24.rda'))
# Nominalskalierte Variablen in Faktoren verwandeln
fb24$geschl_faktor <- factor(fb24$hand,
levels = 1:2,
labels = c("links", "rechts"))
fb24$fach <- factor(fb24$fach,
levels = 1:5,
labels = c('Allgemeine', 'Biologische', 'Entwicklung', 'Klinische', 'Diag./Meth.'))
fb24$ziel <- factor(fb24$ziel,
levels = 1:4,
labels = c("Wirtschaft", "Therapie", "Forschung", "Andere"))
fb24$wohnen <- factor(fb24$wohnen,
levels = 1:4,
labels = c("WG", "bei Eltern", "alleine", "sonstiges"))
# Rekodierung invertierter Items
fb24$mdbf4_r <- -1 * (fb24$mdbf4 - 5)
fb24$mdbf11_r <- -1 * (fb24$mdbf4 - 5)
fb24$mdbf3_r <- -1 * (fb24$mdbf4 - 5)
fb24$mdbf9_r <- -1 * (fb24$mdbf4 - 5)
# Berechnung von Skalenwerten
fb24$gs_pre <- fb24[, c('mdbf1', 'mdbf4_r',
'mdbf8', 'mdbf11_r')] |> rowMeans()
Summen lassen sich sehr leicht auch durch ein Matrixprodukt darstellen. Nehmen wir beispielsweise die ersten 3 Spalten des fb24
Datensatzes her und nennen diese X
. Wenn wir einen Zeilenvektor der Dimension X
ist, dann können wir die Summe der Elemente pro Spalte von X
durch ein Matrixprodukt ausdrücken:
X <- as.matrix(fb24[, 1:3])
n <- nrow(X)
z <- t(rep(1, n)) # 1en-Zeilenvektor der Länge n
z %*% X
## mdbf1 mdbf2 mdbf3
## [1,] 575 501 394
colSums(X)
## mdbf1 mdbf2 mdbf3
## 575 501 394
Die Kovarianz zweier Variablen
Da hier prok1
prok2
x <- X[,1]
y <- X[,2]
sum(x*y)
## [1] 1545
t(x) %*% y
## [,1]
## [1,] 1545
mean(x)
## [1] 2.994792
1/n * (z %*% x)
## [,1]
## [1,] 2.994792
mean(x)*mean(y)
## [1] 7.814535
1/n * (z %*% x) %*% 1/n* (z %*% y)
## [,1]
## [1,] 7.814535
cov(x,y)
## [1] 0.2335569
1/(n-1) * (t(x) %*% y - (z %*% x) %*% 1/n* (z %*% y))
## [,1]
## [1,] 0.2335569
Das wirkt auf den ersten Blick kompliziert, das Schöne ist nun, dass diese Formel auch für Matrizen gilt.
Wollen wir nun die Kovarianzmatrix von X
bestimmen, so ginge dies bspw. durch:
1/(n-1)*(t(X) %*% X - 1/n*t(z %*% X) %*% (z %*% X))
## mdbf1 mdbf2 mdbf3
## mdbf1 0.4659413 0.2335569 -0.2143870
## mdbf2 0.2335569 0.6162467 -0.1994437
## mdbf3 -0.2143870 -0.1994437 0.8035558
cov(X)
## mdbf1 mdbf2 mdbf3
## mdbf1 0.4659413 0.2335569 -0.2143870
## mdbf2 0.2335569 0.6162467 -0.1994437
## mdbf3 -0.2143870 -0.1994437 0.8035558
Eine alternative Schreibweise wäre:
1/(n-1)*(t(X) %*% X - n*colMeans(X) %*% t(colMeans(X)))
## mdbf1 mdbf2 mdbf3
## mdbf1 0.4659413 0.2335569 -0.2143870
## mdbf2 0.2335569 0.6162467 -0.1994437
## mdbf3 -0.2143870 -0.1994437 0.8035558
In Büchern steht dann
colMeans(X)
).