Mehrdimensionale Skalierung – MDS

_Explorative Verfahren

Prof. Dr. Armin Eichinger

TH Deggendorf

28.11.2024

Einleitung

Allgemeines

  • Explorative statistische Technik
  • Ziel: Objekte so im Wahrnehmungsraum positionieren, dass ähnliche Objekte nahe beieinander und unähnliche weit voneinander entfernt sind; „kognitive Landkarten“
  • Un-/Ähnlichkeiten werden in räumliche Distanzen umgerechnet

Abgrenzung zur Faktorenanalyse

  • Entsprechung: Faktorwerte

  • Unterschied: MDS beruht auf subjektiv empfunden Un-/Ähnlichkeiten; Faktorenanalyse beruht auf Ausprägungen der Objekte auf ausgewiesenen Attributen

  • Vorteile MDS:

    • Attribute können unbekannt sein
    • keine Beeinflussung durch Verbalisierung von Attributen
  • Nachteile MDS:

    • Interpretation der Ergebnisse ist schwieriger
    • Bezug zwischen den Dimensionen des Wahrnehmungsraums und Eigenschaften der Elemente/Merkmalsträger ist nicht so offensichtlich

Schritte einer MDS

  • Erheben der Ähnlichkeiten
  • (Wahl des Distanzmodells; meist euklidischer Abstand)
  • Ermittlung der Konfiguration
  • Zahl/Ermittlung der Dimensionen
  • Aggregation über Personen

Konfiguration

  • Objekte werden räumlich repräsentiert und haben dadurch Distanzen zueinander
  • Distanzen sollen groß sein, wenn die Unähnlichkeit zwischen Objekten groß ist
  • Wenn die Unähnlichkeit zwischen einem Paar von Objekten kleiner ist als zwischen einem anderen Paar, dann soll auch die entsprechende Distanz kleiner sein
  • Güte der Konfiguration: STRESS = STandardized REsidual Sum of Squares

STRESS – Formel nach Kruskal (Stress-1)

STRESS-Maß misst, wie gut die Distanzen die Ähnlichkeiten repräsentieren

Formel:

\(\text{Stress} = \sqrt{\frac{\sum_{i < j} \left( d_{ij} - \hat{d}_{ij} \right)^2}{\sum_{i < j} d_{ij}^2}}\)

Dabei ist:

  • \(d_{ij}\): Die Distanzen zwischen den Objekten \(i\) und \(j\) im ursprünglichen, hochdimensionalen Raum.
  • \(\hat{d}_{ij}\): Die entsprechenden Distanzen zwischen den Objekten \(i\) und \(j\) im niedrigdimensionalen MDS-Raum.

Erklärung der Formel

  1. Zähler \(\sum_{i < j} \left( d_{ij} - \hat{d}_{ij} \right)^2\): Der Zähler summiert die quadrierten Unterschiede zwischen den ursprünglichen Distanzen \(d_{ij}\) und den Distanzen \(\hat{d}_{ij}\) im MDS-Raum. Je größer diese Unterschiede sind, desto größer wird der Stress-Wert, was auf eine schlechtere Anpassung der MDS-Lösung hinweist.

  2. Nenner \(\sum_{i < j} d_{ij}^2\): Der Nenner ist die Summe der quadrierten ursprünglichen Distanzen. Dieser dient zur Normalisierung, sodass der Stress-Wert unabhängig von der absoluten Skalierung der Distanzen ist.

  3. Quadratwurzel: Die Quadratwurzel wird genommen, um den Stress-Wert in denselben Einheiten wie die Distanzen auszudrücken.

Interpretation des Stress-Werts

Anmerkung: Manchmal wird auch ein Stressmaß erzeugt, das 100mal größer ist. Es kann dann als Prozent Stress interpretiert werden.

  • Stress ( < 0,05 ): Sehr gute Anpassung. Die MDS-Lösung repräsentiert die Distanzen sehr gut.
  • Stress ( 0,05 ) bis ( 0,1 ): Gute Anpassung. Die MDS-Lösung ist akzeptabel.
  • Stress ( 0,1 ) bis ( 0,2 ): Mäßige Anpassung. Die MDS-Lösung ist noch nützlich, aber die Verzerrung ist deutlicher.
  • Stress ( > 0,2 ): Schlechte Anpassung. Die MDS-Lösung sollte mit Vorsicht interpretiert werden.

Datenverdichtung

Verdichtungskoeffizient \(Q = \frac{O(O-1)/2}{O \cdot k} = \frac{Zahl \: der \: Ähnlichkeiten}{Zahl \: der \: Koordinaten}\)

mit \(O\): Zahl der Objekte; \(k\): Zahl der Dimesionen

Handreichung (aus Backhaus, 2015): Werte ab 2.0

Q-Werte für k=2 & k=3:

Anzahl Objekte Dimensionen k=2 Dimensionen k=3
7 1,50 1,00
8 1,75 1,17
9 2,00 1,33
10 2,25 1,50
11 2,50 1,67
12 2,75 1,83
13 3,00 2,00

Beispiel: Eissorten 🍦

MDS in R durchführen

Die Daten finden Sie hier.

Ausgangsdaten

# CSV-Datei einlesen
similarities <- read.csv("./data/eissorten_similarities.csv", row.names = 1, sep=";", dec = ",")
#similarities <- read.csv("https://bookdown.org/Armin_E/explorativ-multivariat-mds/data/eissorten_similarities.csv", 
#row.names = 1, sep=";", dec = ".")

similarities
              Vanille Schokolade Erdbeer Stracciatella Joghurt Kaffee Zitrone
Vanille          7.00         NA      NA            NA      NA     NA      NA
Schokolade       3.45       7.00      NA            NA      NA     NA      NA
Erdbeer          2.55       2.09    7.00            NA      NA     NA      NA
Stracciatella    4.18       4.64    2.00          7.00      NA     NA      NA
Joghurt          3.36       2.45    3.73          4.18    7.00     NA      NA
Kaffee           3.45       4.36    1.73          3.45    2.09   7.00      NA
Zitrone          2.00       1.64    2.91          2.09    3.36   1.45    7.00
Haselnuss        3.36       4.73    2.00          3.18    2.09   4.00    1.82
Himbeer          3.09       1.91    5.64          1.82    2.91   2.00    3.55
              Haselnuss Himbeer
Vanille              NA      NA
Schokolade           NA      NA
Erdbeer              NA      NA
Stracciatella        NA      NA
Joghurt              NA      NA
Kaffee               NA      NA
Zitrone              NA      NA
Haselnuss             7      NA
Himbeer               2       7

Symmetrische Ähnlichkeitsmatrix

# DataFrame in eine Matrix umwandeln
similarities_matrix <- as.matrix(similarities)

# Die Matrix symmetrisieren: untere Dreiecksmatrix nach oben kopieren
symmetrized_matrix <- similarities_matrix
symmetrized_matrix[upper.tri(symmetrized_matrix)] <- t(symmetrized_matrix)[upper.tri(symmetrized_matrix)]

# Die symmetrisierte Matrix der Ähnlichkeiten anzeigen
similarities <- symmetrized_matrix
similarities
              Vanille Schokolade Erdbeer Stracciatella Joghurt Kaffee Zitrone
Vanille          7.00       3.45    2.55          4.18    3.36   3.45    2.00
Schokolade       3.45       7.00    2.09          4.64    2.45   4.36    1.64
Erdbeer          2.55       2.09    7.00          2.00    3.73   1.73    2.91
Stracciatella    4.18       4.64    2.00          7.00    4.18   3.45    2.09
Joghurt          3.36       2.45    3.73          4.18    7.00   2.09    3.36
Kaffee           3.45       4.36    1.73          3.45    2.09   7.00    1.45
Zitrone          2.00       1.64    2.91          2.09    3.36   1.45    7.00
Haselnuss        3.36       4.73    2.00          3.18    2.09   4.00    1.82
Himbeer          3.09       1.91    5.64          1.82    2.91   2.00    3.55
              Haselnuss Himbeer
Vanille            3.36    3.09
Schokolade         4.73    1.91
Erdbeer            2.00    5.64
Stracciatella      3.18    1.82
Joghurt            2.09    2.91
Kaffee             4.00    2.00
Zitrone            1.82    3.55
Haselnuss          7.00    2.00
Himbeer            2.00    7.00

Distanzen

# Maximale Ähnlichkeit bestimmen
max_value <- max(similarities)

# Ab jetzt: Distanzen
distances <- max_value - similarities
distances
              Vanille Schokolade Erdbeer Stracciatella Joghurt Kaffee Zitrone
Vanille          0.00       3.55    4.45          2.82    3.64   3.55    5.00
Schokolade       3.55       0.00    4.91          2.36    4.55   2.64    5.36
Erdbeer          4.45       4.91    0.00          5.00    3.27   5.27    4.09
Stracciatella    2.82       2.36    5.00          0.00    2.82   3.55    4.91
Joghurt          3.64       4.55    3.27          2.82    0.00   4.91    3.64
Kaffee           3.55       2.64    5.27          3.55    4.91   0.00    5.55
Zitrone          5.00       5.36    4.09          4.91    3.64   5.55    0.00
Haselnuss        3.64       2.27    5.00          3.82    4.91   3.00    5.18
Himbeer          3.91       5.09    1.36          5.18    4.09   5.00    3.45
              Haselnuss Himbeer
Vanille            3.64    3.91
Schokolade         2.27    5.09
Erdbeer            5.00    1.36
Stracciatella      3.82    5.18
Joghurt            4.91    4.09
Kaffee             3.00    5.00
Zitrone            5.18    3.45
Haselnuss          0.00    5.00
Himbeer            5.00    0.00

MDS für k=2 und k=1

library(MASS)     #hier finden wir die Funktion isoMDS()

# Herzstück: Durchführung der MDS für k=2 und k=1
# isoMDS(): nichtmetrische MDS
mds_result <- isoMDS(distances,  k=2, trace = FALSE)
mds_result_1 <- isoMDS(distances,  k=1, trace = FALSE)

# Stress-Werte explizit anzeigen
mds_result$stress
[1] 7.034366
mds_result_1$stress
[1] 17.67899
##### unwichtig: nur Aufhübschen ###########
# Ermittlung der Ausdehnung der Punkte für k=2
xrange <- range(mds_result$points[, 1])
yrange <- range(mds_result$points[, 2])

# Erweiterung der Ränder, damit Labels nicht abgeschnitten werden
xlim <- xrange + c(-1, 1) * diff(xrange) * 0.2
ylim <- yrange + c(-1, 1) * diff(yrange) * 0.2
########################################

Plot für k=2

# Ergebnisse plotten für k=2
plot(mds_result$points, type = "p", main = "MDS Eissorten (k=2)", xlab = "Dimension 1", 
     ylab = "Dimension 2", xlim = xlim, ylim = ylim)

text(mds_result$points, labels = rownames(distances), cex = 0.8, pos = 1)

Plot für k=1

##### Plot für k=1 ###########
# Extrahieren der MDS-Koordinaten für k=1
mds_points_1 <- mds_result_1$points[, 1]

# Da k=1 nur eine Dimension hat, plottet man diese entlang der x-Achse
# rep(0, length(mds_points_1)) setzt die Y-Werte auf 0
plot(mds_points_1, rep(0, length(mds_points_1)), type = "p", 
     main = "MDS Eissorten (k=1)", xlab = "Dimension 1", ylab = "", 
     xlim = range(mds_points_1), ylim = c(-1, 1))

# Namen der Eissorten hinzufügen
text(mds_points_1, rep(0, length(mds_points_1)), labels = rownames(distances), cex = 0.8, pos = 1)

# Berechnung von R² für k=2
original_distances <- as.vector(dist(distances))
mds_distances <- as.vector(dist(mds_result$points))
R_squared <- cor(original_distances, mds_distances)^2
R_squared
[1] 0.8725402
# Berechnung der Stress-Werte für verschiedene Dimensionen
stress_values <- sapply(1:5, function(k) {
  isoMDS(distances, k = k, trace = FALSE)$stress
})
plot(1:5, stress_values, type = "b", 
     main = "Stress-Plot", 
     xlab = "Anzahl der Dimensionen", ylab = "Stress")

# Berechnung des Verdichtungskoeffizienten Q
# Anzahl der Objekte (num_obj)
num_obj <- nrow(distances)

# Anzahl der Dimensionen (k)
k1 <- 1
k2 <- 2

# Berechnung von Q für k=1

Q_k1 <- (num_obj * (num_obj - 1) / 2) / (num_obj * k1)
cat("Verdichtungskoeffizient Q für k=1: ", Q_k1, "\n")
Verdichtungskoeffizient Q für k=1:  4 
# Berechnung von Q für k=2
Q_k2 <- (num_obj * (num_obj - 1) / 2) / (num_obj * k2)
cat("Verdichtungskoeffizient Q für k=2: ", Q_k2, "\n")
Verdichtungskoeffizient Q für k=2:  2 

Ähnlichkeiten/Distanzen bestimmen

Distanz = max(Ähnlichkeit) - Ähnlichkeit

Rangreihung

  • klassisches Verfahren
  • Paare von Objekten auf Kärtchen
  • Sortierung nach Ähnlichkeit
  • Anzahl von nötigen Vergleichen: n x (n-1)/2
  • Erleichterung durch iterative Bildung von Untergruppen: „Ähnliche“ & „Unähnliche“
  • Für individuelle, nicht aggregierte Lösungen besonders geeignet

Ankerpunktmethode

  • Jedes Objekt dient einmal als Vergleichsobjekt
  • Vergleich mit allen anderen Objekten nach Ähnlichkeit
  • Ergebnis: n-1 Ränge pro Objekt; also n x (n-1) Werte
  • Die resultierende Datenmatrix ist meist unsymmetrisch
  • Abhilfe ist Symmetrisieren: \(d_{neu} = \frac{d_1 + d_2}{2}\))

Ratingverfahren

  • vgl. Beispiel: Eissorten
  • Ähnlichkeiten werden auf Rating-Skalen eingeschätzt
  • Anzahl von nötigen Bewertungen: n x (n-1)/2
  • Vorteile:
    • schnell und einfach, da die Ähnlichkeitsurteile relativ isoliert von anderen Ähnlichkeiten angegeben werden
    • praktikabel auch für viele Objekte
  • Nachteile:
    • Stabilisierung durch Vergleiche mit anderen Paaren entfällt
    • Daten ungenauer, wegen begrenzter Kategorienanzahl
      → Ties (verbundene Ränge)

Aggregation von Personen

Aggregation von Personen

  • Aggregation der Ausgangswerte: Mittwelwerte (vgl. Beispiel Eissorten), Medianbildung

  • Alternativ: Je Person eine MDS; Aggregation (Mittelwerte) der Koordinaten

  • Das meint ChatGPT: Wenn die Erhaltung individueller Unterschiede wichtig ist, scheint die zweite Methode (separate MDS pro Person und Aggregation der Koordinaten) besser geeignet. Bei eher homogenen Gruppen oder wenn eine schnellere, einfachere Berechnung bevorzugt wird, kann die Aggregation der Ausgangswerte (Mittelwert oder Median) ausreichen.

Weitere Attribute berücksichtigen: Property Fitting

Property Fitting: Idee

  • Die X- und Y-Koordinaten der Objekte gehen als Prädiktoren in eine Regressionsanalyse ein
  • Jedes einzelne Attribut ist einmal Kriterium
  • Für jedes Attribut wird also eine Analyse durchgeführt
  • Wenn die Modellierung erfolgreich ist (signifikantes Ergebnis, hohes R²), bestimmen die Gewichte einen Vektor im MDS-Raum
  • Die Objekte können auf den Vektor projiziert werden
  • Die Dimensionen können nach Lage zu den Attribut-Vektoren interpretiert werden

Property Fitting in R – Output


Call:
lm(formula = Milchanteil ~ MDS1 + MDS2, data = data)

Residuals:
     Min       1Q   Median       3Q      Max 
-15.7391  -8.0975   0.5086   6.6030  15.7917 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   40.000      3.840  10.417 4.59e-05 ***
MDS1           5.461      1.686   3.240   0.0177 *  
MDS2           3.483      3.460   1.006   0.3530    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 11.52 on 6 degrees of freedom
Multiple R-squared:  0.6938,    Adjusted R-squared:  0.5917 
F-statistic: 6.797 on 2 and 6 DF,  p-value: 0.02872

Property Fitting in R – Code

# Beispiel-Daten: Milchanteile für die Eissorten
milchanteile <- c(60, 50, 30, 50, 40, 50, 0, 50, 30)  # Schätzwerte für die genannten Sorten

# Erstellen eines Dataframes mit den MDS-Koordinaten und Milchanteilen
data <- data.frame(MDS1 = mds_result$points[, 1], MDS2 = mds_result$points[, 2], Milchanteil = milchanteile)

# Durchführung der linearen Regression
regression_model <- lm(Milchanteil ~ MDS1 + MDS2, data=data)

# Zusammenfassung des Regressionsmodells anzeigen
summary(regression_model)

# Speichern der Steigung und des Intercepts
b0 <- 0
b1 <- coef(regression_model)[3] / coef(regression_model)[2] # evtl. 2 und 3 vertauschen; ich glaube aber es stimmt so

# Plot der MDS-Koordinaten
plot(mds_result$points, main = "MDS-Lösung mit Regressionsgerade", xlab = "Dimension 1", ylab = "Dimension 2", xlim = xlim, ylim = ylim, asp=1)
text(mds_result$points, labels = rownames(data), pos = 3)

# Hinzufügen der Regressionsgerade
abline(a = b0, b = b1, col = "red", lwd = 2)

steigung <- b1

for (i in 1:nrow(data)) {
  # Koordinaten des Datenpunkts
  x0 <- data$MDS1[i]
  y0 <- data$MDS2[i]
  
  # Berechnung des Lotfußpunkts (x1, y1) auf die Linie y = steigung * x
  x1 <- (x0 + steigung * y0) / (1 + steigung^2)
  y1 <- steigung * x1
  
  # Zeichnen des Lots
  segments(x0, y0, x1, y1, col = "blue", lwd = 2)
  
  # Zeichnen des Lotfußpunkts
  points(x1, y1, col = "blue", pch = 19)
}

Schließlich …

Empfehlungen

  • Zahl der Objekte sollte nicht zu klein sein; möglichst mehr als acht
  • Ratingverfahren bei vielen Vergleichen; Rangreihen für genauere Daten
  • (Distanzmodell: häufig euklidische Distanz)
  • Zwei- o. dreidimensionale Lösung anstreben
  • Rotation der Achsen erleichtert ggf. die Interpretation
  • Interpretation ist umso leichter, je größer die Sachkenntnis