Zunächst installieren wir Pakete, die wir benötigen, um die folgenden Analysen durchzuführen.

if (!require('rstudioapi')) install.packages('rstudioapi')
if (!require('Hmisc')) install.packages('Hmisc')
if (!require('MASS')) install.packages('MASS')
if (!require('caret')) install.packages('caret')
if (!require('dplyr')) install.packages('dplyr')
if (!require('CCA')) install.packages('CCA')
if (!require('mlogit')) install.packages('mlogit')
if (!require('lmtest')) install.packages('lmtest')

Jetzt laden wir die installierten Pakete. Das Paket “rstudiopai” enthält die Funktion getActiveDocumentContext(), die es uns später erleichtert, das Working Directory zu setzen. Das Paket “Hmisc” enthält die Funktion rcorr(), die wir nutzen um einfach Korrelationsmatrizen erstellen zu können. Das Paket “MASS” benötigen wir, da es eine Funktion namens lda() enthält, mit der wir Diskriminanzanalysen berechnen können. Das Paket “caret” enthält die Funktion confusionMatrix(), mit der wir einfach Konfusionsmatrizen mit begleitenden Informationen (z.B. Vorhersageakkuratheit) anfordern können. Aus dem Paket “dplyr” benötigen wir wieder die Funktion recode(), um einfach Variablen umkodieren zu können. Das Paket “CCA” benötigen wir wegen seiner Funktion cc(), mit der wir kanonische Korrelationsanalysen berechnen können. Das Paket “mlogit” enthält eine gleichnamige Funktion, die es uns erlaubt, multinomiale logistische Regressionen zu berechen. Das Paket “lmtest” benötigen wir schließlich, da es eine Funktion namens lrtest() enthält, mit der wir Likelihood-Ratio-Tests anfordern können, um Modelle zu vergleichen.

library(rstudioapi)
library(Hmisc)
library(MASS)
library(caret)
library(dplyr)
library(CCA)
library(mlogit)
library(lmtest)

Nun schalten wir die wissenschaftliche Notation ab, um möglichst viele Nachkommastellen im Output zu erhalten (das kann vor allem für p-Werte wichtig sein, die sonst schwer zu interpretieren sind).

options(scipen=999)

Hier setzen wir das Working Directory auf den Pfad, in dem sich auch dieses Notebook befindet. Dadurch findet R alle nötigen Dateien, so lange sich diese im selben Ordner wie dieses Notebook auf Ihrem Rechner befinden.

setwd(dirname(getActiveDocumentContext()$path))

Diskriminanzanalyse

Wir wollen uns nun anschauen, wie wir eine Diskriminanzanalyse berechnen können. Dazu laden wir den Datensatz “diskrim.RData” ein (erste Zeile) und lassen ihn uns ausgeben (zweite Zeile), um ihn zu inspizieren.

load('diskrim.RData')
diskrim

Wir nutzen die Funkion lda() aus dem Paket “MASS”, um eine Diskriminanzanalyse zu berechnen. Diese Funktion benötigt mindestens zwei Argumente:

Wir packen die Ergebnisse wieder in ein Objekt, dass wir hier “LDA_Ergebnisse” nennen. In der zweiten Zeile lassen wir uns diese Ergebnise ausgeben. Wie Sie sehen, brauchen wir in diesem Fall dafür nicht die Funktion summary(), sondern wir geben einfach den Namen des Objekts, das die Ergebnisse enthält, ein.

LDA_Ergebnisse <- lda(formula = sztype ~ sozatt + emo + panssneg + sansneg, data = diskrim)
LDA_Ergebnisse
## Call:
## lda(sztype ~ sozatt + emo + panssneg + sansneg, data = diskrim)
## 
## Prior probabilities of groups:
##        HN       NSK       HSK 
## 0.3409091 0.2954545 0.3636364 
## 
## Group means:
##          sozatt         emo   panssneg    sansneg
## HN   0.02688377  0.09461278  0.5317344  0.6491836
## NSK -0.67979707 -0.60525263 -0.2709220 -0.2127489
## HSK  0.52713159  0.40306827 -0.2783769 -0.4357512
## 
## Coefficients of linear discriminants:
##                  LD1         LD2
## sozatt    0.58815237 -0.58217800
## emo       0.59076594  0.02124984
## panssneg  0.01691179 -0.46933275
## sansneg  -0.60569765 -0.74121389
## 
## Proportion of trace:
##    LD1    LD2 
## 0.5413 0.4587

Wie wir sehen, hat unsere Ausgabe wieder mehrere Unterpunkte:

Sie fragen sich nun wahrscheinlich, was wir mit diesem Output anfangen sollen. Denn wir wissen nicht,

Wir wollen uns nun zunächst der ersten Fragestellung widmen. Dazu plotten wir einmal die Lage unserer Patienten im zweidimensionalen Raum, wobei wir die 1. kanonische Diskriminanzfunktion auf die x-Achse legen und die 2. kanonische Diskriminanzfunktion auf die y-Achse legen. Dies geht sehr einfach, indem wir das Objekt mit den Ergebnissen der Diskriminanzanalyse einfach der Funktion plot() übergeben.

plot(LDA_Ergebnisse)

Wir können sehen, dass sich NSK-Patienten eher im niedrigen Bereich der ersten kanonischen Diskriminanzfunktion befinden und HSK-Patienten eher im hohen Bereich. Insgesamt gibt es jedoch sehr starke Überlappungen zwischen unseren drei Gruppen (es würde uns also sehr schwer fallen, Grenzen zwischen den einzelnen Gruppen einzuzeichnen).

Wie viele Patienten würden also auf Basis unserer Diskriminanzfunktionen korrekt und wie viele falsch klassifiziert werden? Wir können uns die vorhergesagten Klassifikationen ausgeben lassen, indem wir erneut die Funktion lda() aufrufen. Wir übergeben ihr aber zusätzlich das Argument “CV = TRUE”. CV steht für cross-validation. Genau genommen führt R hier im Hintergrund eine sogenannte leave-one-out cross-validation durch. Bei diesem Verfahren wird die Diskriminanzanalyse nicht nur einmal, sondern N-mal im Hintergrund ausgeführt (N entspricht der Anzahl unserer Versuchspersonen). Bei jedem Durchlauf wird die Diskriminanzanalyse dabei jedoch mit einer ausgeschlossenen Versuchsperson durchgeführt und dann die Klasssenzugehörigkeit der ausgeschlossenen Versuchsperson vorhergesagt.

Wir packen die Ergebnisse dieser cross-validation in ein Objekt namens “Vorhersagen” und lassen es uns ausgeben.

Vorhersagen <- lda(formula = sztype ~ sozatt + emo + panssneg + sansneg, data = diskrim, CV = TRUE)
Vorhersagen
## $class
##  [1] HN  HN  HN  NSK NSK HN  HN  NSK HN  HSK NSK HN  HSK HN  HN  HSK HN  NSK HSK
## [20] HSK NSK HSK HSK NSK HN  NSK NSK HN  HSK HSK HN  HSK HSK NSK NSK HSK HSK HN 
## [39] HSK HSK HN  HSK HN  HSK
## Levels: HN NSK HSK
## 
## $posterior
##            HN        NSK        HSK
## 1  0.90009268 0.03139612 0.06851120
## 2  0.81093263 0.02156649 0.16750088
## 3  0.50760267 0.13683299 0.35556434
## 4  0.18960104 0.46663525 0.34376371
## 5  0.10636580 0.79941907 0.09421513
## 6  0.53609206 0.27917644 0.18473149
## 7  0.51705191 0.10061833 0.38232976
## 8  0.41005893 0.49338089 0.09656018
## 9  0.64988644 0.14654817 0.20356539
## 10 0.15124743 0.06531112 0.78344145
## 11 0.31487506 0.49561703 0.18950791
## 12 0.50724938 0.08383290 0.40891772
## 13 0.34964319 0.20569531 0.44466150
## 14 0.47479897 0.41371922 0.11148182
## 15 0.82167608 0.07708761 0.10123632
## 16 0.06191909 0.19937150 0.73870941
## 17 0.69661271 0.13874900 0.16463829
## 18 0.18414472 0.71706090 0.09879438
## 19 0.03321443 0.33643037 0.63035521
## 20 0.18832683 0.33672348 0.47494970
## 21 0.10842129 0.87249666 0.01908205
## 22 0.30716083 0.08892267 0.60391650
## 23 0.13118217 0.05690819 0.81190964
## 24 0.33153049 0.60186077 0.06660874
## 25 0.56522730 0.27685883 0.15791387
## 26 0.10973675 0.88358110 0.00668215
## 27 0.09633029 0.80017754 0.10349217
## 28 0.50710479 0.14649640 0.34639881
## 29 0.14491349 0.27169672 0.58338979
## 30 0.02063422 0.18414547 0.79522031
## 31 0.61407239 0.06511719 0.32081042
## 32 0.09164140 0.21855779 0.68980081
## 33 0.22051773 0.22450790 0.55497437
## 34 0.03615684 0.65841484 0.30542832
## 35 0.30049749 0.51149222 0.18801029
## 36 0.28483304 0.08389128 0.63127568
## 37 0.03627933 0.04464418 0.91907649
## 38 0.46300002 0.31660504 0.22039494
## 39 0.13561869 0.14992414 0.71445717
## 40 0.23281662 0.25111030 0.51607308
## 41 0.91074779 0.07247510 0.01677711
## 42 0.02899667 0.06264161 0.90836172
## 43 0.72465656 0.06611818 0.20922526
## 44 0.46261172 0.01150085 0.52588743
## 
## $terms
## sztype ~ sozatt + emo + panssneg + sansneg
## attr(,"variables")
## list(sztype, sozatt, emo, panssneg, sansneg)
## attr(,"factors")
##          sozatt emo panssneg sansneg
## sztype        0   0        0       0
## sozatt        1   0        0       0
## emo           0   1        0       0
## panssneg      0   0        1       0
## sansneg       0   0        0       1
## attr(,"term.labels")
## [1] "sozatt"   "emo"      "panssneg" "sansneg" 
## attr(,"order")
## [1] 1 1 1 1
## attr(,"intercept")
## [1] 1
## attr(,"response")
## [1] 1
## attr(,".Environment")
## <environment: R_GlobalEnv>
## attr(,"predvars")
## list(sztype, sozatt, emo, panssneg, sansneg)
## attr(,"dataClasses")
##    sztype    sozatt       emo  panssneg   sansneg 
##  "factor" "numeric" "numeric" "numeric" "numeric" 
## 
## $call
## lda(formula = sztype ~ sozatt + emo + panssneg + sansneg, data = diskrim, 
##     CV = TRUE)
## 
## $xlevels
## named list()

Uns interessieren hierbei nur die ersten beiden Bereiche der Ausgabe:

Die vorhergesagten Klassen, die wir somit erhalten haben, können wir mittels einer Kreuztabelle mit den tatsächlichen Klassen der Versuchspersonen abgleichen: Dies könnten wir einfach mit der Funktion table() machen, der wir die tatsächlichen und die vorhergesagten Klassen als Argumente übergeben (wie wir es im Kapitel zu binär-logistischer Regression getan haben). Aus Komfort-Gründen, wollen wir hier nun allerdings die Funktion confusionMatrix() aus dem Paket “caret” verwenden, da sie uns direkt die Akkuratheit der Klassifikation mit ausrechnet und einen Signifikanztest, ob die Hinzunahme unserer vier Variablen die Vorhersagegüte verbessert.

confusionMatrix(data = Vorhersagen$class, reference = diskrim$sztype)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction HN NSK HSK
##        HN   9   3   4
##        NSK  4   5   2
##        HSK  2   5  10
## 
## Overall Statistics
##                                           
##                Accuracy : 0.5455          
##                  95% CI : (0.3885, 0.6961)
##     No Information Rate : 0.3636          
##     P-Value [Acc > NIR] : 0.01049         
##                                           
##                   Kappa : 0.313           
##                                           
##  Mcnemar's Test P-Value : 0.55288         
## 
## Statistics by Class:
## 
##                      Class: HN Class: NSK Class: HSK
## Sensitivity             0.6000     0.3846     0.6250
## Specificity             0.7586     0.8065     0.7500
## Pos Pred Value          0.5625     0.4545     0.5882
## Neg Pred Value          0.7857     0.7576     0.7778
## Prevalence              0.3409     0.2955     0.3636
## Detection Rate          0.2045     0.1136     0.2273
## Detection Prevalence    0.3636     0.2500     0.3864
## Balanced Accuracy       0.6793     0.5955     0.6875

Wie wir sehen können, werden 24 Versuchspersonen richtig klassifiziert und 20 falsch. Dies entspricht einer Akkuratheit von ca. 54.5% (was eher moderat ist). Das 95% Konfidenzinterval der Akkuratheit liegt zwischen 0.3885 und 0.6961. Die No Information Rate beträgt 0.3636. Dieser Wert entspricht dem Anteil der Personen mit der häufigsten Klasse (HSK, wie wir oben gesehen haben). Da das Konfidenzintervall den Wert der No Information Rate nicht beinhaltet, verbessert sich die Klassifikation also signifikant durch die Hinzunahme unserer vier unabhängigen Variablen. Dies zeigt auch der Wert, der sich bei P-Value [Acc > NIR] findet, an.

Exkurs: Trainingsset, Testset und das Problem des Overfittings

Sie fragen sich an dieser Stelle eventuell, warum uns die Funktion lda() so einen “unbrauchbaren” Output liefert, wenn wir das Argument “CV = TRUE” weglassen und warum wir so einen Aufwand haben, bis wir endlich einen p-Wert erhalten. Dies liegt an der Forschungs- und Anwendungspraxis im Bereich von Klassifizierungsfragestellungen: In den meisten Forschungs- und Anwendungsfragen wollen wir nicht wissen, ob ein Klassifikationsmodell überhaupt signifikanten Mehrwert gegenüber einer Klassifikation auf Basis der häufigsten Klasse hat, sondern wir wollen ein möglichst gutes Vorhersagemodell aufstellen (in vielen Anwendungsfragen geht es darum, die Klassifikationsakkuratheit im Bereich von über 90% noch um ein bis zwei Prozentpunkte zu erhöhen). Deswegen interessiert der “schnöde” p-Wert in derartigen Fragestellungen oft nicht.

Der Grund, warum uns lda() standardmäßig keine vorhergesagten Werte für unsere Versuchspersonen liefert, liegt auch in der typischen Forschungs- und Anwendungspraxis: Um die Güte eines Klassifikationsmodells zu bestimmen, braucht man zwei Datensets:

  • Ein Trainingsset, anhand dessen im ersten Schritt das Klassifikationsmodell gelernt wird. Im Falle der Diskriminanzanalyse bedeutet das, dass anhand des Trainingssets die Gewichte für die Diskriminanzfunktionen gelernt werden.
  • Ein unabhängiges Testset, auf das das Klassifikationsmodell angewandt wird, um die Güte des Klassifikationsmodells abzuschätzen.

Warum kann man nicht einfach die gleichen Daten als Trainingsset und als Testset verwenden? Hierbei kommt es zum Problem des “Overfitting”. Overfitting beschreibt das Problem, dass ein Modell sich zu genau an die Trainingsdaten anpasst (z.B. auch an Outlier innerhalb des Trainingssets) und sich dann sehr schlecht eignet, um neue Daten, bei denen wir die Klassenzugehörigkeiten nicht kennen, zu klassifizieren (was meist allerdings das Ziel von Klassifikationsverfahren ist).1 Deswegen gibt uns lda() auch standardmäßig keine vorhergesagten Klassenzugehörigkeiten für unsere Versuchspersonen aus, das es davon ausgeht, dass wir noch ein separates Testset haben, für das uns dann die vorhergesagten Klassenzugehörigkeiten interessieren.

Die leave-one-out cross-validation, die wir oben verwendet haben, ist streng genommen eine “Notlösung”: Weil uns nur ein kleines Datenset (N = 44) zur Verfügung steht, wollen wir es nicht noch kleiner machen, indem wir Versuchspersonen ausschließen, um sie anschließend als Testdaten zu verwenden. Daher teilt die leave-one-out cross-validation die Daten künstlich in Trainings- und Testdaten. Wie bereits beschrieben, wird bei diesem Verfahren die Diskriminanzanalyse N-mal durchgeführt. Bei jedem Durchlauf wird eine Versuchsperson aus der Berechnung ausgeschlossen. Die Diskriminanzanalyse wird also mit N-1 Versuchspersonen gefittet (Trainingsset). Anschließend wird die Klasse für die ausgeschlossene Versuchsperson vorhergesagt (Testset).2 Mehr Informationen zum Thema Kreuzvalidierung können Sie dem entsprechenden Abschnitt im Buchkapitel entnehmen.

Zusammenhang zwischen Diskriminanzanalyse und kanonischer Korrelationsanalyse

Wir wollen nun einmal den Zusammenhang zwischen der Diskriminanzanalyse und der kanonischen Korrelationsanalyse verdeutlichen. Dazu kodieren wir die Gruppenvariable zunächst in zwei Kontrastvariablen. Zu diesem Zweck nutzen wir wieder die Funktion recode() aus dem Paket “dplyr”.

diskrim <- diskrim %>%
  mutate(k1 = recode(sztype, 'HN' = 0, 'HSK' = -1, 'NSK' = 1),
         k2 = recode(sztype, 'HN' = 1, 'HSK' = -0.5, 'NSK' = -0.5))

Um uns die Eingaben gleich etwas zu erleichtern, schauen wir mit der Funktion colnames(), welche Spaltennummern unsere unabhängigen Variablen und unsere Kontrastvariablen haben.

colnames(diskrim)
## [1] "sztype"   "sozatt"   "emo"      "panssneg" "sansneg"  "k1"       "k2"

Anhand der Spaltennummern packen wir die Kontrastvariablen nun in ein Objekt namens “set1” und die unabhängigen Variablen in ein Objekt namens “set2”.

set1 <- diskrim[,6:7]
set2 <- diskrim[,2:5]

Mit der Funktion cc() aus dem Paket “CCA” berechnen wir nun eine kanonische Korrelationsanalyse zwischen unseren beiden Variablensets (erste Zeile) und lassen uns die Gewichte der unabhängigen Variablen auf die beiden kanonischen Variaten ausgeben (zweite Zeile).

kk_ergebnisse <- cc(set1, set2)
kk_ergebnisse$ycoef
##                 [,1]        [,2]
## sozatt   -0.50595950 -0.51246777
## emo      -0.50820783  0.01870538
## panssneg -0.01454841 -0.41313465
## sansneg   0.52105287 -0.65246063

Die Gewichte der unabhängigen Variablen für die kanonischen Variaten sind zwar nicht identisch zu den Koeffizenten der Diskriminanzfunktion, aber sie entsprechen Ihnen konzeptuell. Wir können das auch ganz einfach zeigen: Wenn wir die Gewichte der unabhängigen Variablen für die kanonischen Variaten aus der kanonischen Korrelationsanalyse durch die Koeffizienten der Diskriminanzfunktion aus der Diskriminanzanalyse teilen, sehen wir, dass diese durch einen Skalierungsfaktor ineinander überführt werden können (-0.86 für die erste kanonische Variate und 0.88 für die zweite).

kk_ergebnisse$ycoef / LDA_Ergebnisse$scaling
##                [,1]      [,2]
## sozatt   -0.8602524 0.8802596
## emo      -0.8602524 0.8802596
## panssneg -0.8602524 0.8802596
## sansneg  -0.8602524 0.8802596

Die Unterschiede in der Skalierung kommen zu Stande, da bei der kanonischen Korrelationsanalyse die Normierungs-Nebenbedingung gilt, dass die Varianzen der kanonischen Variaten den Wert eins annehmen (vielleicht errinern Sie sich). Bei der Diskriminanzanalyse gilt jedoch eine andere Nebenbedingung (die wir gleich lernen werden).

Um die Unterschiede in der Skalierung zwischen den Diskriminanzfunktionen und den kanonischen Variaten klar zu machen, bilden wir diese nun einmal von Hand. Zunächst bilden wir die Diskriminanzfunktionen D1 und D2 für jeden Probanden von Hand. Dabei nutzen wir aus, dass die Gewichte der Funktionen bereits im Unterobjekt “scaling” des Objekts “LDA_Ergebnisse” gespeichert sind.

diskrim$D1 <- LDA_Ergebnisse$scaling[1,1] * diskrim$sozatt + LDA_Ergebnisse$scaling[2,1] * diskrim$emo + LDA_Ergebnisse$scaling[3,1] * diskrim$panssneg + LDA_Ergebnisse$scaling[4,1] * diskrim$sansneg
diskrim$D2 <- LDA_Ergebnisse$scaling[1,2] * diskrim$sozatt + LDA_Ergebnisse$scaling[2,2] * diskrim$emo + LDA_Ergebnisse$scaling[3,2] * diskrim$panssneg + LDA_Ergebnisse$scaling[4,2] * diskrim$sansneg

Nun bilden wir die kanonischen Variaten U1 und U2 für jede Versuchsperson. Hierbei nutzen wir aus, dass die Gewichte der kanonischen Variaten im Unterobjekt “ycoef” des Objekts “kk_ergebnisse” gespeichert sind.

diskrim$U1 <- kk_ergebnisse$ycoef[1,1] * diskrim$sozatt + kk_ergebnisse$ycoef[2,1] * diskrim$emo + kk_ergebnisse$ycoef[3,1] * diskrim$panssneg + kk_ergebnisse$ycoef[4,1] * diskrim$sansneg
diskrim$U2 <- kk_ergebnisse$ycoef[1,2] * diskrim$sozatt + kk_ergebnisse$ycoef[2,2] * diskrim$emo + kk_ergebnisse$ycoef[3,2] * diskrim$panssneg + kk_ergebnisse$ycoef[4,2] * diskrim$sansneg

Um uns die Anforderung der Korrelationsmatrix zu erleichtern, überprüfen wir wieder mit colnames(), welche Spaltennummern die neu gebildeten Variablen haben: Es sind die Spalten 8 bis 11.

colnames(diskrim)
##  [1] "sztype"   "sozatt"   "emo"      "panssneg" "sansneg"  "k1"      
##  [7] "k2"       "D1"       "D2"       "U1"       "U2"

Jetzt fordern wir mit der Funktion rcor() eine Korrelationsmatrix zwischen den Variablen D1, D2, U1 und U2 an. Wie wir bereits aus vorherigen Kapiteln wissen, erwartet die Funktion rcorr() eine Matrix, so dass wir die entsprechenden Spalten des Data-Frames “diskrim” zunächst mit der Funktion as.matrix() in eine Matrix transformieren müssen.

rcorr(as.matrix(diskrim[8:11]))
##    D1 D2 U1 U2
## D1  1  0 -1  0
## D2  0  1  0  1
## U1 -1  0  1  0
## U2  0  1  0  1
## 
## n= 44 
## 
## 
## P
##    D1 D2 U1 U2
## D1     1  0  1
## D2  1     1  0
## U1  0  1     1
## U2  1  0  1

Wir sehen anhand der Korrelationsmatrix zunächst, dass die Variablen D1 und D2 auf der einen Seite sowie die Variablen U1 und U2 auf der anderen Seite perfekt zu Null korrellieren. Dies ist nicht verwunderlich, da die Variablen per Definition so gebildet werden, dass sie jeweils zu 0 korrelieren. Interessanter ist stattdessen, dass die Variablen D1 und U1 perfekt zu -1 korrelieren und die Variablen D2 und U2 perfekt zu 1. Dies zeigt erneut, dass die D-Variablen anhand eines konstanten Skalierungsfaktors in die U-Variablen überführt werden können.

Nach welchen Regeln werden nun die Gewichte skaliert? Für die kanonische Korrelationsanalyse kennen wir die Regel bereits: Die Gewichte werden so skaliert, dass die kanonischen Variaten die Standardabweichung 1 haben. Wir können das einmal überprüfen. Dazu nutzen wir wieder die uns bereits bekannte Funktion sapply(), um die Standardabweichung für alle vier interessierenden Variablen gleichzeitig anfordern zu können.

sapply(X = diskrim[8:11], FUN = sd)
##       D1       D2       U1       U2 
## 1.162449 1.136029 1.000000 1.000000

Wie wir sehen, ist die Standardabweichung für unsere beiden kanonischen Variaten U1 und U2 tatsächlich exakt 1. Dies trifft allerdings nicht für die Variablen D1 und D2 zu. Nach welchen Regeln wurden die Gewichte also hier skaliert? Sie wurden so skaliert, dass die Kovarianzmatrix innerhalb der Gruppen isotrop ist (das bedeutet, dass sie der Sphärizitätsanforderung genügt). Genauer müssen Sie die Skalierung an dieser Stelle nicht verstehen. Es reicht hier zu wissen, dass die Skalierungsbedingungen für die kanonische Korrelationsanalyse und die Diskriminanzanalyse unterschiedlich sind.

Multinomiale logistische Regression

Wir wollen nun eine multinomiale logistische Regression rechnen, bei der die Klassenzugehörigkeit der Patienten durch unsere vier unabhängigen Variablen vorhergesagt werden soll. Wir wollen dazu die Funktion mlogit() aus dem gleichnamigen Paket nutzen. Leider verlangt diese Funktion es, dass wir unsere Daten vorher in einen sogenannten dfidx Data-Frame überführen.3 Was das besondere an so einem dfidx Data-Frame ist, werden wir gleich sehen. Um unsere Daten in einen dfidx Data-Frame zu überführen, benötigen wir die Funktion dfidx(), der wir drei Argumente übergeben.

Hier transformieren wir unsere Daten in einen dfidx Data-Frame namens diskrim_dfidx (erste Zeile). Und lassen ihn uns ausgeben (zweite Zeile).

diskrim_dfidx <- dfidx(data = diskrim[,1:5], choice = 'sztype', shape = 'wide')
diskrim_dfidx
##     sztype      sozatt         emo    panssneg     sansneg    idx
## 1     TRUE  0.33589584  0.62118399  1.70286182  1.87288567   1:HN
## 2    FALSE  0.33589584  0.62118399  1.70286182  1.87288567  1:HSK
## 3    FALSE  0.33589584  0.62118399  1.70286182  1.87288567  1:NSK
## 4     TRUE  1.18038405  0.96507382  0.91772500  1.71493862   2:HN
## 5    FALSE  1.18038405  0.96507382  0.91772500  1.71493862  2:HSK
## 6    FALSE  1.18038405  0.96507382  0.91772500  1.71493862  2:NSK
## 7     TRUE  0.31489911  0.27925344  0.80896367  0.27802074   3:HN
## 8    FALSE  0.31489911  0.27925344  0.80896367  0.27802074  3:HSK
## 9    FALSE  0.31489911  0.27925344  0.80896367  0.27802074  3:NSK
## 10    TRUE -0.55639095 -0.36446116  0.30415511 -0.54085464   4:HN
## 11   FALSE -0.55639095 -0.36446116  0.30415511 -0.54085464  4:HSK
## 12   FALSE -0.55639095 -0.36446116  0.30415511 -0.54085464  4:NSK
## 13    TRUE -1.74009920 -0.46350623  0.49179046 -0.04328081   5:HN
## 14   FALSE -1.74009920 -0.46350623  0.49179046 -0.04328081  5:HSK
## 15   FALSE -1.74009920 -0.46350623  0.49179046 -0.04328081  5:NSK
## 16    TRUE -0.31478264  0.26314124 -0.13147022  1.06545781   6:HN
## 17   FALSE -0.31478264  0.26314124 -0.13147022  1.06545781  6:HSK
## 18   FALSE -0.31478264  0.26314124 -0.13147022  1.06545781  6:NSK
## 19    TRUE -0.41634888  1.59146347  1.77479950  0.91749398   7:HN
## 20   FALSE -0.41634888  1.59146347  1.77479950  0.91749398  7:HSK
## 21   FALSE -0.41634888  1.59146347  1.77479950  0.91749398  7:NSK
## 22    TRUE -1.13895300 -0.33235904  0.37599129  0.82661130   8:HN
## 23   FALSE -1.13895300 -0.33235904  0.37599129  0.82661130  8:HSK
## 24   FALSE -1.13895300 -0.33235904  0.37599129  0.82661130  8:NSK
## 25    TRUE -0.03334942  0.43539159  0.67255104  0.95089954   9:HN
## 26   FALSE -0.03334942  0.43539159  0.67255104  0.95089954  9:HSK
## 27   FALSE -0.03334942  0.43539159  0.67255104  0.95089954  9:NSK
## 28    TRUE  1.79657105  0.09481423 -0.97031666 -0.04569045  10:HN
## 29   FALSE  1.79657105  0.09481423 -0.97031666 -0.04569045 10:HSK
## 30   FALSE  1.79657105  0.09481423 -0.97031666 -0.04569045 10:NSK
## 31    TRUE -0.73433521  0.07936348 -0.45775285  0.68313857  11:HN
## 32   FALSE -0.73433521  0.07936348 -0.45775285  0.68313857 11:HSK
## 33   FALSE -0.73433521  0.07936348 -0.45775285  0.68313857 11:NSK
## 34    TRUE  0.73509077  0.38364258  0.94069743  0.27532993  12:HN
## 35   FALSE  0.73509077  0.38364258  0.94069743  0.27532993 12:HSK
## 36   FALSE  0.73509077  0.38364258  0.94069743  0.27532993 12:NSK
## 37    TRUE  1.15224948 -0.93265613 -0.39398160  0.02153359  13:HN
## 38   FALSE  1.15224948 -0.93265613 -0.39398160  0.02153359 13:HSK
## 39   FALSE  1.15224948 -0.93265613 -0.39398160  0.02153359 13:NSK
## 40    TRUE -0.24798663 -1.01666807 -0.12748247  0.75131556  14:HN
## 41   FALSE -0.24798663 -1.01666807 -0.12748247  0.75131556 14:HSK
## 42   FALSE -0.24798663 -1.01666807 -0.12748247  0.75131556 14:NSK
## 43    TRUE  0.07041208 -0.18448548  2.06748471  1.00995457  15:HN
## 44   FALSE  0.07041208 -0.18448548  2.06748471  1.00995457 15:HSK
## 45   FALSE  0.07041208 -0.18448548  2.06748471  1.00995457 15:NSK
## 46   FALSE -0.24903753 -0.76156346  0.21400671 -2.00800314  16:HN
## 47   FALSE -0.24903753 -0.76156346  0.21400671 -2.00800314 16:HSK
## 48    TRUE -0.24903753 -0.76156346  0.21400671 -2.00800314 16:NSK
## 49   FALSE -0.67781574  0.89693507 -0.89235276  1.58992791  17:HN
## 50   FALSE -0.67781574  0.89693507 -0.89235276  1.58992791 17:HSK
## 51    TRUE -0.67781574  0.89693507 -0.89235276  1.58992791 17:NSK
## 52   FALSE -0.87742042 -1.66032594 -0.53381993 -0.28068378  18:HN
## 53   FALSE -0.87742042 -1.66032594 -0.53381993 -0.28068378 18:HSK
## 54    TRUE -0.87742042 -1.66032594 -0.53381993 -0.28068378 18:NSK
## 55   FALSE -0.94314046  0.67210138 -2.07314783 -0.83897257  19:HN
## 56   FALSE -0.94314046  0.67210138 -2.07314783 -0.83897257 19:HSK
## 57    TRUE -0.94314046  0.67210138 -2.07314783 -0.83897257 19:NSK
## 58   FALSE  0.01686436 -0.50777012 -0.25562538 -0.83461848  20:HN
## 59   FALSE  0.01686436 -0.50777012 -0.25562538 -0.83461848 20:HSK
## 60    TRUE  0.01686436 -0.50777012 -0.25562538 -0.83461848 20:NSK
## 61   FALSE -1.83077016 -2.79174465  0.10739932 -0.52478259  21:HN
## 62   FALSE -1.83077016 -2.79174465  0.10739932 -0.52478259 21:HSK
## 63    TRUE -1.83077016 -2.79174465  0.10739932 -0.52478259 21:NSK
## 64   FALSE  0.84438752 -0.13546580  0.19380941 -0.49245953  22:HN
## 65   FALSE  0.84438752 -0.13546580  0.19380941 -0.49245953 22:HSK
## 66    TRUE  0.84438752 -0.13546580  0.19380941 -0.49245953 22:NSK
## 67   FALSE  0.47664250  1.10431163 -0.21473283 -0.57780379  23:HN
## 68   FALSE  0.47664250  1.10431163 -0.21473283 -0.57780379 23:HSK
## 69    TRUE  0.47664250  1.10431163 -0.21473283 -0.57780379 23:NSK
## 70   FALSE -0.97378438 -1.20297922 -0.92499720  0.69996330  24:HN
## 71   FALSE -0.97378438 -1.20297922 -0.92499720  0.69996330 24:HSK
## 72    TRUE -0.97378438 -1.20297922 -0.92499720  0.69996330 24:NSK
## 73   FALSE -0.43949146 -0.56904392  0.70655740  0.45197878  25:HN
## 74   FALSE -0.43949146 -0.56904392  0.70655740  0.45197878 25:HSK
## 75    TRUE -0.43949146 -0.56904392  0.70655740  0.45197878 25:NSK
## 76   FALSE -2.72562327 -2.37027826  0.56457063  0.03242990  26:HN
## 77   FALSE -2.72562327 -2.37027826  0.56457063  0.03242990 26:HSK
## 78    TRUE -2.72562327 -2.37027826  0.56457063  0.03242990 26:NSK
## 79   FALSE -1.15023171 -1.23667570 -1.14795257 -0.40466318  27:HN
## 80   FALSE -1.15023171 -1.23667570 -1.14795257 -0.40466318 27:HSK
## 81    TRUE -1.15023171 -1.23667570 -1.14795257 -0.40466318 27:NSK
## 82   FALSE -0.30794115  0.69421487  0.73429910  0.42195159  28:HN
## 83   FALSE -0.30794115  0.69421487  0.73429910  0.42195159 28:HSK
## 84    TRUE -0.30794115  0.69421487  0.73429910  0.42195159 28:NSK
## 85   FALSE  0.04832165 -0.01195069  0.05083475 -1.09845314  29:HN
## 86    TRUE  0.04832165 -0.01195069  0.05083475 -1.09845314 29:HSK
## 87   FALSE  0.04832165 -0.01195069  0.05083475 -1.09845314 29:NSK
## 88   FALSE  1.06320501  1.06358450 -3.09383215 -1.10703867  30:HN
## 89    TRUE  1.06320501  1.06358450 -3.09383215 -1.10703867 30:HSK
## 90   FALSE  1.06320501  1.06358450 -3.09383215 -1.10703867 30:NSK
## 91   FALSE  0.73964563  0.92162469  0.72117527  0.69973578  31:HN
## 92    TRUE  0.73964563  0.92162469  0.72117527  0.69973578 31:HSK
## 93   FALSE  0.73964563  0.92162469  0.72117527  0.69973578 31:NSK
## 94   FALSE  0.95580988 -0.69738747 -0.74305466 -1.62180256  32:HN
## 95    TRUE  0.95580988 -0.69738747 -0.74305466 -1.62180256 32:HSK
## 96   FALSE  0.95580988 -0.69738747 -0.74305466 -1.62180256 32:NSK
## 97   FALSE  0.39210333 -0.09811246 -0.12415132 -0.65624257  33:HN
## 98    TRUE  0.39210333 -0.09811246 -0.12415132 -0.65624257 33:HSK
## 99   FALSE  0.39210333 -0.09811246 -0.12415132 -0.65624257 33:NSK
## 100  FALSE  0.02855979 -1.18326870 -1.60149904 -1.83925409  34:HN
## 101   TRUE  0.02855979 -1.18326870 -1.60149904 -1.83925409 34:HSK
## 102  FALSE  0.02855979 -1.18326870 -1.60149904 -1.83925409 34:NSK
## 103  FALSE -0.31902017 -0.70447256 -0.46457190 -0.03347530  35:HN
## 104   TRUE -0.31902017 -0.70447256 -0.46457190 -0.03347530 35:HSK
## 105  FALSE -0.31902017 -0.70447256 -0.46457190 -0.03347530 35:NSK
## 106  FALSE  0.49502620  1.13774129  0.77387567 -0.29700024  36:HN
## 107   TRUE  0.49502620  1.13774129  0.77387567 -0.29700024 36:HSK
## 108  FALSE  0.49502620  1.13774129  0.77387567 -0.29700024 36:NSK
## 109  FALSE  1.65908373  0.13602573 -0.51912071 -2.02439376  37:HN
## 110   TRUE  1.65908373  0.13602573 -0.51912071 -2.02439376 37:HSK
## 111  FALSE  1.65908373  0.13602573 -0.51912071 -2.02439376 37:NSK
## 112  FALSE -0.51901637 -0.09288501  1.24988155 -0.18468275  38:HN
## 113   TRUE -0.51901637 -0.09288501  1.24988155 -0.18468275 38:HSK
## 114  FALSE -0.51901637 -0.09288501  1.24988155 -0.18468275 38:NSK
## 115  FALSE -0.04031698  1.71276667 -0.19287633 -0.47027550  39:HN
## 116   TRUE -0.04031698  1.71276667 -0.19287633 -0.47027550 39:HSK
## 117  FALSE -0.04031698  1.71276667 -0.19287633 -0.47027550 39:NSK
## 118  FALSE  0.02476277  0.98387235 -0.81085596  0.06378856  40:HN
## 119   TRUE  0.02476277  0.98387235 -0.81085596  0.06378856 40:HSK
## 120  FALSE  0.02476277  0.98387235 -0.81085596  0.06378856 40:NSK
## 121  FALSE -0.33003630  0.56240561  0.79934069  2.01820761  41:HN
## 122   TRUE -0.33003630  0.56240561  0.79934069  2.01820761 41:HSK
## 123  FALSE -0.33003630  0.56240561  0.79934069  2.01820761 41:NSK
## 124  FALSE  0.78136314  1.45331343 -1.03150129 -1.42255320  42:HN
## 125   TRUE  0.78136314  1.45331343 -1.03150129 -1.42255320 42:HSK
## 126  FALSE  0.78136314  1.45331343 -1.03150129 -1.42255320 42:NSK
## 127  FALSE  0.85606308  0.36240815  0.76504839  0.77176546  43:HN
## 128   TRUE  0.85606308  0.36240815  0.76504839  0.77176546 43:HSK
## 129  FALSE  0.85606308  0.36240815  0.76504839  0.77176546 43:NSK
## 130  FALSE  2.59855104  0.90342688 -0.23272323  0.22965596  44:HN
## 131   TRUE  2.59855104  0.90342688 -0.23272323  0.22965596 44:HSK
## 132  FALSE  2.59855104  0.90342688 -0.23272323  0.22965596 44:NSK
## 
## ~~~ indexes ~~~~
##     id1 id2
## 1     1  HN
## 2     1 HSK
## 3     1 NSK
## 4     2  HN
## 5     2 HSK
## 6     2 NSK
## 7     3  HN
## 8     3 HSK
## 9     3 NSK
## 10    4  HN
## 11    4 HSK
## 12    4 NSK
## 13    5  HN
## 14    5 HSK
## 15    5 NSK
## 16    6  HN
## 17    6 HSK
## 18    6 NSK
## 19    7  HN
## 20    7 HSK
## 21    7 NSK
## 22    8  HN
## 23    8 HSK
## 24    8 NSK
## 25    9  HN
## 26    9 HSK
## 27    9 NSK
## 28   10  HN
## 29   10 HSK
## 30   10 NSK
## 31   11  HN
## 32   11 HSK
## 33   11 NSK
## 34   12  HN
## 35   12 HSK
## 36   12 NSK
## 37   13  HN
## 38   13 HSK
## 39   13 NSK
## 40   14  HN
## 41   14 HSK
## 42   14 NSK
## 43   15  HN
## 44   15 HSK
## 45   15 NSK
## 46   16  HN
## 47   16 HSK
## 48   16 NSK
## 49   17  HN
## 50   17 HSK
## 51   17 NSK
## 52   18  HN
## 53   18 HSK
## 54   18 NSK
## 55   19  HN
## 56   19 HSK
## 57   19 NSK
## 58   20  HN
## 59   20 HSK
## 60   20 NSK
## 61   21  HN
## 62   21 HSK
## 63   21 NSK
## 64   22  HN
## 65   22 HSK
## 66   22 NSK
## 67   23  HN
## 68   23 HSK
## 69   23 NSK
## 70   24  HN
## 71   24 HSK
## 72   24 NSK
## 73   25  HN
## 74   25 HSK
## 75   25 NSK
## 76   26  HN
## 77   26 HSK
## 78   26 NSK
## 79   27  HN
## 80   27 HSK
## 81   27 NSK
## 82   28  HN
## 83   28 HSK
## 84   28 NSK
## 85   29  HN
## 86   29 HSK
## 87   29 NSK
## 88   30  HN
## 89   30 HSK
## 90   30 NSK
## 91   31  HN
## 92   31 HSK
## 93   31 NSK
## 94   32  HN
## 95   32 HSK
## 96   32 NSK
## 97   33  HN
## 98   33 HSK
## 99   33 NSK
## 100  34  HN
## 101  34 HSK
## 102  34 NSK
## 103  35  HN
## 104  35 HSK
## 105  35 NSK
## 106  36  HN
## 107  36 HSK
## 108  36 NSK
## 109  37  HN
## 110  37 HSK
## 111  37 NSK
## 112  38  HN
## 113  38 HSK
## 114  38 NSK
## 115  39  HN
## 116  39 HSK
## 117  39 NSK
## 118  40  HN
## 119  40 HSK
## 120  40 NSK
## 121  41  HN
## 122  41 HSK
## 123  41 NSK
## 124  42  HN
## 125  42 HSK
## 126  42 NSK
## 127  43  HN
## 128  43 HSK
## 129  43 NSK
## 130  44  HN
## 131  44 HSK
## 132  44 NSK
## indexes:  1, 2

Wir wollen uns den dfidx Data-Frame nun einmal genauer anschauen. Leider sieht die Darstellung dieses Data-Frames unterschiedlich aus, je nachdem, ob wir ihn uns in der Ausgabe in RStudio anzeigen lassen oder ob wir uns die Ausgabe im HTML-Dokument anschauen. Wir gehen hier auf die Anzeige im HTML-Dokument ein, da sie etwas übersichtlicher ist. Wir können zunächst sehen, dass die Ausgabe zweigeteilt ist. Oben sehen wir einen Data-Frame mit 132 Zeilen. Die Zeilenanzahl hat sich also verdreifacht. Woran liegt das? Dies hängt mit der letzten Spalte namens “idx” zusammen, die neu gebildet wurde. Bei dieser Variable handelt es sich um eine Index-Variable. Wie sie sehen können, enthält jede Zeile dieser Variable zwei Werte: Eine Zahl und einen String. Wie kann das sein? Nach allem, was wir über Data-Frames in R wissen, kann jede Spalte nur Werte eines Typs (also entweder numerisch oder String) enthalten. Außerdem kann jede Zelle in einem Data-Frame normalerweise nur einen Wert enthalten. Die Lösung sehen wir im zweiten Teil der Ausgabe: Genau genommen enthält die Spalte “idx” einen “Unter-Data-Frame” mit zwei Spalten. Wie wir im unteren Teil der Ausgabe sehen können, enthält die Spalte “id1” dieses Unter-Data-Frames Zahlen, während die Spalte “id2” Strings enthält. Die Kombination dieser beiden Spalten kann uns auch erklären, warum unser Data-Frame nun dreimal mehr Zeilen hat als der urprüngliche: Jede Versuchsperson hat nun drei Zeilen. Welche drei Zeilen zu welcher Versuchsperson gehören, gibt die Zahl in “id1” an. Wir sehen auch, dass in “id2” jede Versuchsperson in ihren drei Zeilen die Werte “HN”, “HSK” und “NSK” hat. Dementsprechend wird die Gruppenzugehörigkeit einer Versuchsperson nicht mehr über einen von drei möglichen Werten in der Spalte “sztype” ausgedrückt. Statdessen nimmt die Spalte “sztype” den Wert TRUE in den Spalten an, wo der Wert in der Spalte “id2” der Gruppenzugehörigkeit der in “id1” referenzierten Versuchsperson entspricht. Die restlichen Zeilen nehmen den Wert FALSE an. Somit hat jede Versuchsperson in ihren drei Zeilen einmal den Wert TRUE und zweimal den Wert FALSE.

Nun wollen wir mittels der Funktion mlogit() eine multinomiale logistische Regression berechnen. Dazu übergeben wir der Funktion drei Argumente:

multlogreg <- mlogit(formula = sztype ~ 1 | sozatt + emo + panssneg + sansneg, data = diskrim_dfidx, reflevel = 'HN')
summary(multlogreg)
## 
## Call:
## mlogit(formula = sztype ~ 1 | sozatt + emo + panssneg + sansneg, 
##     data = diskrim_dfidx, reflevel = "HN", method = "nr")
## 
## Frequencies of alternatives:choice
##      HN     HSK     NSK 
## 0.34091 0.36364 0.29545 
## 
## nr method
## 5 iterations, 0h:0m:0s 
## g'(-H)^-1g = 0.000429 
## successive function values within tolerance limits 
## 
## Coefficients :
##                 Estimate Std. Error z-value Pr(>|z|)  
## (Intercept):HSK  0.10674    0.51357  0.2078  0.83535  
## (Intercept):NSK -0.13656    0.54010 -0.2528  0.80039  
## sozatt:HSK       0.19099    0.55617  0.3434  0.73130  
## sozatt:NSK      -1.15781    0.67141 -1.7245  0.08463 .
## emo:HSK          1.10253    0.66191  1.6657  0.09578 .
## emo:NSK          0.30606    0.64193  0.4768  0.63352  
## panssneg:HSK    -0.60929    0.61126 -0.9968  0.31887  
## panssneg:NSK    -0.88210    0.61808 -1.4271  0.15354  
## sansneg:HSK     -1.38441    0.62755 -2.2061  0.02738 *
## sansneg:NSK     -1.11671    0.64319 -1.7362  0.08253 .
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Log-Likelihood: -33.933
## McFadden R^2:  0.29567 
## Likelihood ratio test : chisq = 28.489 (p.value = 0.00038965)

Wir haben hier wieder eine mehrgeteilte Ausgabe:

Wir möchten nun aber noch einen Globaltest erhalten, welchen Beitrag ein Prädiktor für die Trennung der drei Gruppen insgesamt leistet (anstatt diesen Beitrag getrennt für zwei binomial logistische Regressionen zu erhalten). Wir machen dies hier exemplarisch für den Prädiktor “sansneg”. Wir könnten dies aber natürlich analog auch für alle anderen Prädiktoren machen.

Zunächst fitten wir zu diesem Zweck ein weiteres Modell, in das wir alle Prädiktoren, bis auf den Prädiktor “sansneg” aufnehmen. Dieses Modell nennen wir “multlogreg_nosans”

multlogreg_nosans <- mlogit(sztype~1 | sozatt + emo + panssneg, data = diskrim_dfidx, reflevel = 'HSK')

Nun können wir mit der Funktion lrtest() aus dem Paket “lmtest” einen Likelihood-Ratio Test anfordern, der diese beiden Modelle vergleicht.

lrtest(multlogreg_nosans, multlogreg)

Wir sehen, Dass die Differenz aus den -2Log-Likelihoods der beiden Modelle wieder eine \(\chi^2\)-verteilte Prüfgröße ergibt. Diese ist mit zwei Freiheitsgraden assoziiert. Dies kommt daher, dass für den Prädiktor “nosans” bei einer dreigestuften Klassenvariable zwei b-Gewichte geschätzt werden. Schließlich können wir noch den mit der Prüfgröße assoziierten p-Wert entnehmen.

Nun wollen wir uns (ähnlich wie bei der Diskriminanzanalyse) eine Konfusionsmatrix ausgeben lassen. Dazu benötigen wir zunächst die vorhergesagten Werte für unsere Versuchspersonen. Um diese zu erhalten, müssen wir einen kleinen Umweg über die a-posteriori Wahrscheinlichkeiten gehen. Diese erhalten wir mittels der Funktion predict(), die wir schon aus dem Kapitel zu binomial logistischer Regression kennen. Bei der binomial logistischen Regression konnten wir das Argument “newdata” einfach weglassen und R wusste, dass es dann die Vorhersagen für die Daten, anhand derer das Modell gefittet wurde, liefern soll. Dies funktioniert im Falle der multinomial logistischen Regression leider nicht, so dass wir der Funktion das Argument “newdata = diskrim_dfidx” übergeben müssen, damit R weiß, für welche Daten die Vorhersagen getroffen werden sollen.

a_posteriori <- predict(object = multlogreg, newdata = diskrim_dfidx)
a_posteriori
##             HN         HSK        NSK
## 1  0.924181171 0.057644649 0.01817418
## 2  0.809984279 0.174143825 0.01587190
## 3  0.524854788 0.350759688 0.12438552
## 4  0.235032012 0.276390423 0.48857757
## 5  0.190940039 0.071923718 0.73713624
## 6  0.551805562 0.191524128 0.25667031
## 7  0.575320430 0.325454889 0.09922468
## 8  0.500714667 0.078677704 0.42060763
## 9  0.659674252 0.209754343 0.13057140
## 10 0.216098158 0.723860106 0.06004174
## 11 0.333063068 0.180455961 0.48648097
## 12 0.529961198 0.398851256 0.07118755
## 13 0.540377332 0.330642917 0.12897975
## 14 0.647686788 0.085578190 0.26673502
## 15 0.905623581 0.058412082 0.03596434
## 16 0.068170343 0.441883363 0.48994629
## 17 0.410263529 0.205526703 0.38420977
## 18 0.223034297 0.068701374 0.70826433
## 19 0.013556080 0.298595254 0.68784867
## 20 0.175540766 0.415385505 0.40907373
## 21 0.163320151 0.011426086 0.82525376
## 22 0.290831064 0.575407119 0.13376182
## 23 0.076527201 0.799275599 0.12419720
## 24 0.323365665 0.052867347 0.62376699
## 25 0.631075217 0.119895444 0.24902934
## 26 0.146144580 0.004799888 0.84905553
## 27 0.086255410 0.069446865 0.84429773
## 28 0.433409816 0.348433211 0.21815697
## 29 0.116336856 0.571931033 0.31173211
## 30 0.006497645 0.872632180 0.12087017
## 31 0.503800681 0.436233391 0.05996593
## 32 0.077293536 0.710513285 0.21219318
## 33 0.195016218 0.561545420 0.24343836
## 34 0.033230702 0.341430489 0.62533881
## 35 0.306715906 0.205293039 0.48799105
## 36 0.180938350 0.730322388 0.08873926
## 37 0.023168010 0.930048775 0.04678321
## 38 0.458817058 0.251637974 0.28954497
## 39 0.050426041 0.793529580 0.15604438
## 40 0.122789499 0.609370894 0.26783961
## 41 0.868224350 0.063381497 0.06839415
## 42 0.010654568 0.917964418 0.07138101
## 43 0.667129840 0.280960778 0.05190938
## 44 0.192186197 0.797447047 0.01036676

Nun nutzen wir die Funktion max.col(), um in jeder Zeile die Nummer der Spalte zu erhalten, die den höchsten Wert (also die höchste a-posteriori Wahrscheinlichkeit) enthält. Den so kreierten Zahlenvektor nutzen wir, um daraus einen Vektor der entsprechenden Klassenlabels zu erstellen. Schließlich sagen wir R noch, dass es mit der Funktion as.factor() den Vektor mit den Klassenlabels in den Typ “factor” umwandeln soll.

pred_classes <- as.factor(colnames(a_posteriori)[max.col(a_posteriori)])
pred_classes
##  [1] HN  HN  HN  NSK NSK HN  HN  HN  HN  HSK NSK HN  HN  HN  HN  NSK HN  NSK NSK
## [20] HSK NSK HSK HSK NSK HN  NSK NSK HN  HSK HSK HN  HSK HSK NSK NSK HSK HSK HN 
## [39] HSK HSK HN  HSK HN  HSK
## Levels: HN HSK NSK

Wir nutzen wieder die Funktion confusionMatrix() aus dem Paket “caret”, um eine Konfusionsmatrix der vorhergesagten und der tatsächlichen Klassenzugehörigkeiten zu erhalten.

confusionMatrix(data = pred_classes, reference = diskrim$sztype)
## Warning in confusionMatrix.default(data = pred_classes, reference =
## diskrim$sztype): Levels are not in the same order for reference and data.
## Refactoring data to match.
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction HN NSK HSK
##        HN  11   3   4
##        NSK  3   7   2
##        HSK  1   3  10
## 
## Overall Statistics
##                                           
##                Accuracy : 0.6364          
##                  95% CI : (0.4777, 0.7759)
##     No Information Rate : 0.3636          
##     P-Value [Acc > NIR] : 0.0002162       
##                                           
##                   Kappa : 0.4526          
##                                           
##  Mcnemar's Test P-Value : 0.5724067       
## 
## Statistics by Class:
## 
##                      Class: HN Class: NSK Class: HSK
## Sensitivity             0.7333     0.5385     0.6250
## Specificity             0.7586     0.8387     0.8571
## Pos Pred Value          0.6111     0.5833     0.7143
## Neg Pred Value          0.8462     0.8125     0.8000
## Prevalence              0.3409     0.2955     0.3636
## Detection Rate          0.2500     0.1591     0.2273
## Detection Prevalence    0.4091     0.2727     0.3182
## Balanced Accuracy       0.7460     0.6886     0.7411

Uns wird hier zunächst eine Warnmeldung ausgegeben, die besagt, dass die Faktorstufen im Falle der vorhergesagten und der tatsächlichen Klassenzugehörigkeiten eine andere Reihenfolge hatten. Dieses Problem hat die Funktion confusionMatrix() aber im Hintergrund selbst behoben.

Als nächstes können wir sehen, dass die Akkuratheit der Vorhersage insgesamt 63.6% beträgt. Damit ist die Vorhersage der multinomialen logistischen Regression deutlich besser als die der Diskriminanzanalyse. Man muss allerdings hinzufügen, dass die Diskriminanzanalyse einen unfairen Nachteil hatte: Wie wir bereits ausgeführt haben, hat die Diskriminanzanalyse nämlich die leave-one-out cross-validation verwendet. Wie wir im Exkurs zu Trainings- und Testdaten dargestellt haben, führt diese Methode in der Regel dazu, dass die Vorhersagequalität im Bezug auf das Trainingsdatenset verringert wird, dass dafür aber das Problem des Overfittings vermieden wird. Es könnte also sein, dass die Schätzung der Vorhersagequalität für die multinomiale logistische Regression überschätzt wird, da hier Trainings- und Testset die gleichen Daten beinhaltet haben. Dem Buchkapitel können Sie weitere Informationen dazu entnehmen, welches der beiden Verfahren bei Klassifikationsproblemen vorzuziehen ist.


  1. So gibt es andere Klassifikationsalgorithmen neben der Diskriminanzanalyse, die standardmäßig eine Akkuratheit von 100% bei der Vorhersage der Klassen der Trainingsdaten erreichen (z.B. sogenannte decision trees). Derartige Modelle werden aber meist vereinfacht, so dass sie zwar keine Akkuratheit von 100% mehr bei der Klassifikation des Trainingsdatensets haben, aber wesentlich besser auf neue Daten generalisieren.↩︎

  2. Die leave-one-out cross-validation ist eine Spezialform der k-fold cross validation. Dabei wird ein vorhandenes Datenset in k gleich große Teile geteilt. Danach wird der Klassifikationsalgorithmus k mal ausgeführt, wobei immer ein anderer der k gebildeten Subsets aus den Trainingsdaten ausgeschlossen wird und als Testset verwendet wird. Ein in der Praxis überlicher Wert ist \(k = 10\). Bei der leave-one-out cross-validtion gilt \(k = N\).↩︎

  3. Es gäbe zwar auch eine Funktion, die mit unseren Daten im vorliegenden Format arbeiten könnte, nämlich multinom() aus dem Paket “nnet”. Die Ausgabe dieser Funktion ist allerdings so sparsam, dass man sich am Ende fast alle relevanten Tests selbst von Hand ausrechnen muss. Somit bereitet die Funktion mlogit() für unsere Zwecke insgesamt weniger Aufwand als die Funktion multinom().↩︎