Hierarchisches Clustering

Clustering
Distanzmasse
Interpretation
R
Python
Verknüpfungsverfahren, Dendrogramm und dessen Deutung.

Kurzsteckbrief

Fragestellung Welche Gruppenstruktur zeigt sich, wenn man ähnliche Objekte schrittweise zusammenfasst, ohne die Anzahl vorher festzulegen?
Zielvariable keine, das Verfahren ist unüberwacht
Eingang eine Distanzmatrix, also beliebige Datentypen, sofern ein Distanzmass definiert ist
Was die Methode liefert Dendrogramm, Gruppenzuordnung bei gewählter Schnitthöhe, Verschmelzungshöhen
R-Funktion hclust(dist(skaliert), method = "ward.D2"), geschnitten mit cutree()
Python-Funktion linkage(skaliert, method="ward"), geschnitten mit fcluster()
Verwandte Methoden k-Means, Clustergüte, Distanzmasse

Wann diese Methode, wann nicht

Typische Fragestellungen: Wie viele Gruppen stecken überhaupt in den Daten, welche Objekte gehören am engsten zusammen, wie sieht die Struktur zwischen den Gruppen aus.

Der Unterschied zu k-Means ist grundlegend: Hier wird die Anzahl Gruppen nicht vorgegeben, sondern nachträglich durch einen Schnitt im Dendrogramm gewählt. Ausserdem arbeitet das Verfahren auf einer Distanzmatrix, also auch mit gemischten Datentypen über die Gower-Distanz.

Situation Stattdessen
Sehr viele Beobachtungen, etwa über zehntausend k-Means; die Distanzmatrix wächst quadratisch
k ist bekannt und die Gruppen sind kompakt k-Means, schneller und stabiler
Gruppen beliebiger Form, Ausreisser als eigene Kategorie DBSCAN
Die Wahl der Gruppenzahl soll begründet werden zusätzlich Clustergüte
Nur Dimensionen sollen verdichtet werden Hauptkomponentenanalyse

Grundidee und Modell

Das gebräuchliche Vorgehen ist agglomerativ, also von unten nach oben: Jede Beobachtung beginnt als eigene Gruppe, und in jedem Schritt werden die beiden Gruppen mit der kleinsten Distanz verschmolzen, bis alles in einer Gruppe ist. Aufgezeichnet wird die Höhe, bei der jede Verschmelzung geschieht, und daraus entsteht das Dendrogramm.

Der entscheidende Freiheitsgrad ist die Verknüpfungsmethode: Wie wird die Distanz zwischen zwei Gruppen aus den Distanzen ihrer Mitglieder gebildet?

Methode Distanz zwischen Gruppen Neigung
Single kleinster Abstand zwischen zwei Mitgliedern Ketten, langgezogene Gruppen; empfindlich für Brücken
Complete grösster Abstand zwischen zwei Mitgliedern kompakte, ähnlich grosse Gruppen
Average Mittelwert aller paarweisen Abstände Kompromiss zwischen beiden
Ward Zuwachs an Streuung innerhalb der Gruppen kompakte, ähnlich grosse Gruppen; das übliche Standardverfahren
Centroid Abstand der Gruppenzentren kann zu Umkehrungen im Dendrogramm führen

Ward ist mit k-Means verwandt: Beide minimieren die Streuung innerhalb der Gruppen, Ward schrittweise und ohne Startwertproblem, k-Means iterativ mit vorgegebenem k. Deshalb liefern die beiden auf kompakten Daten meist ähnliche Ergebnisse.

Ein Hinweis zur Umsetzung: Ward setzt quadrierte euklidische Distanzen voraus. In R heisst die korrekte Variante ward.D2 und erwartet gewöhnliche Distanzen; in Python erwartet linkage(..., method="ward") die Datenmatrix, nicht die Distanzmatrix.

Voraussetzungen und ihre Prüfung

Voraussetzung Was sie bedeutet Prüfung Folge bei Verletzung Vorgehen
Sinnvolles Distanzmass Das Mass passt zu den Datentypen Distanzmasse Struktur beschreibt Artefakte Gower bei gemischten Typen
Standardisierung bei metrischen Daten Keine Variable dominiert Varianzen vergleichen Gruppierung folgt der grössten Variablen scale() beziehungsweise StandardScaler
Passende Verknüpfung Die Neigung des Verfahrens passt zur erwarteten Form mehrere Methoden vergleichen Ketten statt Gruppen, oder Gruppen zerschnitten Ward oder Average als Ausgangspunkt
Handhabbare Anzahl Beobachtungen Distanzmatrix wächst quadratisch n prüfen Speicherbedarf, unlesbares Dendrogramm k-Means, oder Stichprobe ziehen
Keine extremen Ausreisser Einzelne Punkte bilden keine eigenen Äste Dendrogramm ansehen Ein Ast mit einem Objekt Ausreisser prüfen und begründen

Output lesen

R, hclust() und cutree():

Grösse Was sie sagt Faustregel oder Falle
height Verschmelzungshöhen in der Reihenfolge der Schritte ein grosser Sprung heisst: hier werden unähnliche Gruppen vereint
merge welche Gruppen in welchem Schritt verschmelzen negative Zahlen sind einzelne Objekte
order Reihenfolge der Blätter im Dendrogramm nur für die Darstellung, keine Rangfolge
cutree(baum, k =) Zuordnung bei k Gruppen schneidet waagrecht; die Nummerierung ist beliebig
cutree(baum, h =) Zuordnung bei einer Höhe nützlich, wenn die Höhe inhaltlich begründet ist
cophenetic() Distanzen, wie der Baum sie darstellt Korrelation mit den echten Distanzen misst die Treue der Darstellung

Python, scipy.cluster.hierarchy:

Zugriff Was er liefert Unterschied zu R
linkage(...) Matrix mit vier Spalten: zwei Gruppen, Höhe, Grösse in R getrennt in merge und height
fcluster(..., criterion="maxclust") Zuordnung bei k Gruppen wie cutree(k =); Nummerierung beginnt bei 1
fcluster(..., criterion="distance") Zuordnung bei einer Höhe wie cutree(h =)
dendrogram(...) Zeichnung, optional gefärbt in R plot()
cophenet(...) Korrelation und kophenetische Distanzen in R cophenetic() plus cor()
Datenmatrix bei Ward linkage(X, method="ward") erwartet die Daten in R erwartet hclust immer eine Distanzmatrix

Die kophenetische Korrelation ist ein nützliches, oft übersehenes Mass: Sie sagt, wie gut das Dendrogramm die tatsächlichen Distanzen abbildet. Werte über etwa 0.75 gelten als brauchbar; sie eignet sich zum Vergleich von Verknüpfungsmethoden, nicht zur Wahl der Gruppenzahl.

Interpretationsfallen

  • Ein Dendrogramm zeigt immer eine Hierarchie, auch wenn keine Gruppen existieren. Der Verlauf der Verschmelzungshöhen sagt, ob es welche gibt.
  • Die Reihenfolge der Blätter bedeutet nichts. Sie ist eine Darstellungsentscheidung; benachbarte Blätter müssen nicht ähnlich sein.
  • Die Höhe ist Distanz, nicht Wahrscheinlichkeit. Sie hängt am gewählten Distanzmass und der Verknüpfungsmethode.
  • Der Schnitt ist eine Entscheidung. Er gehört begründet, über den Sprung in den Höhen, ein Gütemass oder eine inhaltliche Vorgabe.
  • Die Verknüpfungsmethode formt das Ergebnis. Single erzeugt Ketten, Ward kompakte Gruppen ähnlicher Grösse. Wer die Methode wechselt, bis das Ergebnis gefällt, hat nichts gezeigt.
  • Einmal verschmolzen, immer verschmolzen. Das Verfahren korrigiert frühe Zusammenfassungen nie; ein Fehler am Anfang bleibt im Baum.
  • Gruppennummern sind beliebig, wie bei k-Means.

Ergebnis berichten

Vorlage für den Ergebnissatz:

Ein hierarchisches Clustering ([Distanzmass], [Verknüpfung], n = [n]) ergibt bei einem Schnitt auf [Höhe oder k] Gruppen der Grössen [Grössen]. Der Schnitt ist begründet durch [Sprung in den Verschmelzungshöhen, Silhouette, inhaltliche Vorgabe]. Die kophenetische Korrelation beträgt [Wert].

Genannt werden: Distanzmass und Standardisierung, Verknüpfungsmethode, n, Kriterium und Höhe des Schnitts, Gruppengrössen, Gruppenprofile in Originaleinheiten und ein Mass für die Treue der Darstellung.

Abgrenzung zu verwandten Methoden

Methode Wann diese statt des hierarchischen Verfahrens
k-Means viele Beobachtungen, k bekannt, kompakte Gruppen
Clustergüte die Wahl der Gruppenzahl soll begründet werden
DBSCAN Gruppen beliebiger Form, Ausreisser als eigene Kategorie
Gaussian-Mixture-Modelle Zuordnung mit Wahrscheinlichkeit, verschiedene Gruppenformen
Hauptkomponentenanalyse Variablen verdichten statt Beobachtungen gruppieren

Beispiele

Die Beispiele 1 bis 3 arbeiten mit denselben 270 Kunden wie die Seite zu k-Means: Besuche und Umsatz, drei eingebaute Gruppen von 120, 90 und 60 Personen, z-standardisiert.

Frage und Datenlage

Dieselbe Distanzmatrix, vier Verknüpfungsmethoden. Wie stark hängt das Ergebnis an dieser einen Entscheidung?

Rechnung

par(mfrow = c(1, 4), mar = c(2, 4, 3, 1))
for (methode in c("single", "complete", "average", "ward.D2")) {
  baum_m <- hclust(distanzen, method = methode)
  plot(baum_m, labels = FALSE, hang = -1, main = methode, xlab = "", sub = "")
}
par(mfrow = c(1, 1))

# Treue der Darstellung und Gruppengroessen je Methode
for (methode in c("single", "complete", "average", "ward.D2")) {
  baum_m <- hclust(distanzen, method = methode)
  cat(sprintf("%-9s | kophenetisch: %.3f | Groessen bei k=3: %s\n",
              methode,
              cor(distanzen, cophenetic(baum_m)),
              paste(table(cutree(baum_m, k = 3)), collapse = ", ")))
}
single    | kophenetisch: 0.827 | Groessen bei k=3: 267, 2, 1
complete  | kophenetisch: 0.768 | Groessen bei k=3: 156, 55, 59
average   | kophenetisch: 0.862 | Groessen bei k=3: 92, 119, 59
ward.D2   | kophenetisch: 0.845 | Groessen bei k=3: 92, 119, 59
Abbildung 1: Beispiel 1: dieselben Daten, vier Verknüpfungsmethoden.
abstaende = pdist(skaliert)

fig, achsen = plt.subplots(1, 4, figsize=(7.5, 3.6))
for achse, methode in zip(achsen, ["single", "complete", "average", "ward"]):
    verbund_m = (linkage(skaliert, method="ward") if methode == "ward"
                 else linkage(abstaende, method=methode))
    dendrogram(verbund_m, ax=achse, no_labels=True, color_threshold=0)
    achse.set_title(methode)
plt.tight_layout()
plt.show()
Abbildung 2: Beispiel 1: dieselben vier Methoden in Python.
for methode in ["single", "complete", "average", "ward"]:
    verbund_m = (linkage(skaliert, method="ward") if methode == "ward"
                 else linkage(abstaende, method=methode))
    groessen = np.bincount(fcluster(verbund_m, 3, "maxclust"))[1:]
    print(f"{methode:9s} | kophenetisch: {cophenet(verbund_m, abstaende)[0]:.3f} "
          f"| Grössen bei k=3: {groessen.tolist()}")
single    | kophenetisch: 0.827 | Grössen bei k=3: [2, 267, 1]
complete  | kophenetisch: 0.768 | Grössen bei k=3: [59, 55, 156]
average   | kophenetisch: 0.862 | Grössen bei k=3: [59, 92, 119]
ward      | kophenetisch: 0.845 | Grössen bei k=3: [59, 92, 119]

Output Zeile für Zeile

Was im Bild zu sehen ist Deutung
Single: ein langer Ast, an den einzelne Blätter nacheinander angehängt werden Der Kettenbildungseffekt. Zwei Gruppen gelten als nah, sobald sich irgendein Paar von Mitgliedern nahe ist. Ein einzelner Punkt zwischen zwei Wolken genügt, um sie zu verbinden.
Complete: klar getrennte Blöcke, hohe Verschmelzungen Zwei Gruppen gelten erst als nah, wenn alle Mitglieder nah sind. Das erzwingt kompakte Gruppen und ist empfindlich gegenüber Ausreissern, die eine ganze Gruppe weit weg erscheinen lassen.
Average: ähnlich wie Complete, mit tieferen Höhen Der Kompromiss aus beiden.
Ward: drei deutliche Blöcke, sehr grosser Sprung ganz oben Verschmolzen wird das Paar, das die Streuung innerhalb der Gruppen am wenigsten erhöht. Die Höhen sind deshalb keine Distanzen mehr, sondern Streuungszuwächse, sie liegen zahlenmässig höher als bei den anderen Methoden.

Die Gruppengrössen bei einem Schnitt auf k = 3 sagen dasselbe in Zahlen:

Methode Grössen bei k = 3 kophenetische Korrelation
Single 267, 2, 1 0.827
Complete 156, 55, 59 0.768
Average 119, 92, 59 0.862
Ward 119, 92, 59 0.845

Bemerkenswert ist, dass die beiden Spalten verschiedene Dinge sagen. Die Gruppengrössen entlarven Single sofort: 267 zu 2 zu 1 ist keine Aufteilung, sondern ein Ausreisserfilter. Die kophenetische Korrelation dagegen liegt bei Single mit 0.827 höher als bei Complete mit 0.768, sie misst nämlich nicht die Brauchbarkeit einer Aufteilung, sondern nur, wie treu der Baum die paarweisen Abstände wiedergibt. Ein Baum kann Abstände gut abbilden und trotzdem keine verwertbaren Gruppen liefern.

Average und Ward finden dieselben Gruppengrössen, kein Zufall bei kompakten, gut getrennten Gruppen.

Die Methode wird vorher gewählt, nach der erwarteten Form der Gruppen, nicht nachher nach dem gefälligsten Bild. Vier Bäume nebeneinander sind eine Voruntersuchung; sie zu zeigen und dann den passendsten zu berichten, ist keine.

Interpretation

Für kompakte Gruppen ist Ward der übliche Ausgangspunkt, für langgezogene oder kettenförmige Strukturen Single, im Zweifel Average. Alle folgenden Beispiele verwenden Ward.

Frage und Datenlage

Das Dendrogramm gibt keine Gruppenzahl vor, es liefert nur die Verschmelzungshöhen. Die Frage: Wo wird geschnitten, und woran erkennt man das?

Rechnung

baum <- hclust(distanzen, method = "ward.D2")
gruppen <- cutree(baum, k = 3)

table(gruppen)
gruppen
  1   2   3 
 92 119  59 
round(rev(baum$height)[1:8], 3)          # die acht letzten Verschmelzungen
[1] 23.693 18.350  5.396  4.596  4.462  3.691  3.197  3.065
round(cor(distanzen, cophenetic(baum)), 3)
[1] 0.845
par(mfrow = c(1, 2), mar = c(2, 4, 3, 1))
plot(baum, labels = FALSE, hang = -1, main = "Ward", xlab = "", sub = "")
rect.hclust(baum, k = 3, border = c("#2166ac", "#b2182b", "#1b7837"))

par(mar = c(4, 4, 3, 1))
plot(skaliert[, 1], skaliert[, 2], pch = 16, cex = 0.7,
     col = c("#2166ac", "#b2182b", "#1b7837")[gruppen],
     xlab = "Besuche (z)", ylab = "Umsatz (z)", main = "Schnitt bei k = 3")
par(mfrow = c(1, 1))

round(aggregate(kunden, by = list(gruppe = gruppen), FUN = mean), 1)
  gruppe besuche umsatz
1      1     5.9  905.1
2      2    13.9 1359.9
3      3     2.9 2169.5
table(gefunden = gruppen, eingebaut = gruppe_wahr)
        eingebaut
gefunden   A   B   C
       1   1  90   1
       2 119   0   0
       3   0   0  59
Abbildung 3: Beispiel 2: Dendrogramm mit Schnitt bei drei Gruppen, daneben die Gruppen im Streudiagramm.
verbund = linkage(skaliert, method="ward")
gruppen = fcluster(verbund, t=3, criterion="maxclust")

print("Gruppengrössen:", np.bincount(gruppen)[1:])
Gruppengrössen: [ 59  92 119]
print("letzte Höhen:", np.round(verbund[-8:, 2][::-1], 3))
letzte Höhen: [23.737 18.384  5.406  4.605  4.471  3.698  3.203  3.07 ]
print("kophenetische Korrelation:",
      round(float(cophenet(verbund, abstaende)[0]), 3))
kophenetische Korrelation: 0.845
fig, achsen = plt.subplots(1, 2, figsize=(7, 3.6))
dendrogram(verbund, ax=achsen[0], no_labels=True,
           color_threshold=verbund[-2, 2])
{'icoord': [[15.0, 15.0, 25.0, 25.0], [5.0, 5.0, 20.0, 20.0], [35.0, 35.0, 45.0, 45.0], [65.0, 65.0, 75.0, 75.0], [55.0, 55.0, 70.0, 70.0], [95.0, 95.0, 105.0, 105.0], [85.0, 85.0, 100.0, 100.0], [62.5, 62.5, 92.5, 92.5], [40.0, 40.0, 77.5, 77.5], [135.0, 135.0, 145.0, 145.0], [125.0, 125.0, 140.0, 140.0], [115.0, 115.0, 132.5, 132.5], [58.75, 58.75, 123.75, 123.75], [12.5, 12.5, 91.25, 91.25], [165.0, 165.0, 175.0, 175.0], [155.0, 155.0, 170.0, 170.0], [185.0, 185.0, 195.0, 195.0], [205.0, 205.0, 215.0, 215.0], [190.0, 190.0, 210.0, 210.0], [162.5, 162.5, 200.0, 200.0], [255.0, 255.0, 265.0, 265.0], [245.0, 245.0, 260.0, 260.0], [235.0, 235.0, 252.5, 252.5], [225.0, 225.0, 243.75, 243.75], [285.0, 285.0, 295.0, 295.0], [275.0, 275.0, 290.0, 290.0], [315.0, 315.0, 325.0, 325.0], [305.0, 305.0, 320.0, 320.0], [282.5, 282.5, 312.5, 312.5], [234.375, 234.375, 297.5, 297.5], [355.0, 355.0, 365.0, 365.0], [345.0, 345.0, 360.0, 360.0], [335.0, 335.0, 352.5, 352.5], [375.0, 375.0, 385.0, 385.0], [395.0, 395.0, 405.0, 405.0], [415.0, 415.0, 425.0, 425.0], [400.0, 400.0, 420.0, 420.0], [380.0, 380.0, 410.0, 410.0], [343.75, 343.75, 395.0, 395.0], [455.0, 455.0, 465.0, 465.0], [445.0, 445.0, 460.0, 460.0], [435.0, 435.0, 452.5, 452.5], [475.0, 475.0, 485.0, 485.0], [495.0, 495.0, 505.0, 505.0], [480.0, 480.0, 500.0, 500.0], [525.0, 525.0, 535.0, 535.0], [515.0, 515.0, 530.0, 530.0], [555.0, 555.0, 565.0, 565.0], [545.0, 545.0, 560.0, 560.0], [575.0, 575.0, 585.0, 585.0], [552.5, 552.5, 580.0, 580.0], [522.5, 522.5, 566.25, 566.25], [490.0, 490.0, 544.375, 544.375], [443.75, 443.75, 517.1875, 517.1875], [369.375, 369.375, 480.46875, 480.46875], [265.9375, 265.9375, 424.921875, 424.921875], [181.25, 181.25, 345.4296875, 345.4296875], [51.875, 51.875, 263.33984375, 263.33984375], [595.0, 595.0, 605.0, 605.0], [615.0, 615.0, 625.0, 625.0], [600.0, 600.0, 620.0, 620.0], [635.0, 635.0, 645.0, 645.0], [655.0, 655.0, 665.0, 665.0], [685.0, 685.0, 695.0, 695.0], [675.0, 675.0, 690.0, 690.0], [660.0, 660.0, 682.5, 682.5], [640.0, 640.0, 671.25, 671.25], [610.0, 610.0, 655.625, 655.625], [705.0, 705.0, 715.0, 715.0], [745.0, 745.0, 755.0, 755.0], [735.0, 735.0, 750.0, 750.0], [725.0, 725.0, 742.5, 742.5], [765.0, 765.0, 775.0, 775.0], [733.75, 733.75, 770.0, 770.0], [805.0, 805.0, 815.0, 815.0], [795.0, 795.0, 810.0, 810.0], [785.0, 785.0, 802.5, 802.5], [825.0, 825.0, 835.0, 835.0], [845.0, 845.0, 855.0, 855.0], [830.0, 830.0, 850.0, 850.0], [875.0, 875.0, 885.0, 885.0], [905.0, 905.0, 915.0, 915.0], [895.0, 895.0, 910.0, 910.0], [880.0, 880.0, 902.5, 902.5], [865.0, 865.0, 891.25, 891.25], [840.0, 840.0, 878.125, 878.125], [793.75, 793.75, 859.0625, 859.0625], [751.875, 751.875, 826.40625, 826.40625], [710.0, 710.0, 789.140625, 789.140625], [632.8125, 632.8125, 749.5703125, 749.5703125], [925.0, 925.0, 935.0, 935.0], [945.0, 945.0, 955.0, 955.0], [975.0, 975.0, 985.0, 985.0], [965.0, 965.0, 980.0, 980.0], [995.0, 995.0, 1005.0, 1005.0], [1015.0, 1015.0, 1025.0, 1025.0], [1000.0, 1000.0, 1020.0, 1020.0], [972.5, 972.5, 1010.0, 1010.0], [950.0, 950.0, 991.25, 991.25], [930.0, 930.0, 970.625, 970.625], [1055.0, 1055.0, 1065.0, 1065.0], [1045.0, 1045.0, 1060.0, 1060.0], [1035.0, 1035.0, 1052.5, 1052.5], [1085.0, 1085.0, 1095.0, 1095.0], [1115.0, 1115.0, 1125.0, 1125.0], [1105.0, 1105.0, 1120.0, 1120.0], [1090.0, 1090.0, 1112.5, 1112.5], [1075.0, 1075.0, 1101.25, 1101.25], [1135.0, 1135.0, 1145.0, 1145.0], [1165.0, 1165.0, 1175.0, 1175.0], [1155.0, 1155.0, 1170.0, 1170.0], [1140.0, 1140.0, 1162.5, 1162.5], [1088.125, 1088.125, 1151.25, 1151.25], [1043.75, 1043.75, 1119.6875, 1119.6875], [950.3125, 950.3125, 1081.71875, 1081.71875], [1185.0, 1185.0, 1195.0, 1195.0], [1205.0, 1205.0, 1215.0, 1215.0], [1235.0, 1235.0, 1245.0, 1245.0], [1225.0, 1225.0, 1240.0, 1240.0], [1210.0, 1210.0, 1232.5, 1232.5], [1190.0, 1190.0, 1221.25, 1221.25], [1255.0, 1255.0, 1265.0, 1265.0], [1295.0, 1295.0, 1305.0, 1305.0], [1285.0, 1285.0, 1300.0, 1300.0], [1275.0, 1275.0, 1292.5, 1292.5], [1315.0, 1315.0, 1325.0, 1325.0], [1283.75, 1283.75, 1320.0, 1320.0], [1260.0, 1260.0, 1301.875, 1301.875], [1205.625, 1205.625, 1280.9375, 1280.9375], [1355.0, 1355.0, 1365.0, 1365.0], [1345.0, 1345.0, 1360.0, 1360.0], [1335.0, 1335.0, 1352.5, 1352.5], [1375.0, 1375.0, 1385.0, 1385.0], [1395.0, 1395.0, 1405.0, 1405.0], [1415.0, 1415.0, 1425.0, 1425.0], [1400.0, 1400.0, 1420.0, 1420.0], [1435.0, 1435.0, 1445.0, 1445.0], [1465.0, 1465.0, 1475.0, 1475.0], [1455.0, 1455.0, 1470.0, 1470.0], [1440.0, 1440.0, 1462.5, 1462.5], [1495.0, 1495.0, 1505.0, 1505.0], [1485.0, 1485.0, 1500.0, 1500.0], [1451.25, 1451.25, 1492.5, 1492.5], [1410.0, 1410.0, 1471.875, 1471.875], [1380.0, 1380.0, 1440.9375, 1440.9375], [1343.75, 1343.75, 1410.46875, 1410.46875], [1243.28125, 1243.28125, 1377.109375, 1377.109375], [1016.015625, 1016.015625, 1310.1953125, 1310.1953125], [691.19140625, 691.19140625, 1163.10546875, 1163.10546875], [1525.0, 1525.0, 1535.0, 1535.0], [1515.0, 1515.0, 1530.0, 1530.0], [1555.0, 1555.0, 1565.0, 1565.0], [1545.0, 1545.0, 1560.0, 1560.0], [1585.0, 1585.0, 1595.0, 1595.0], [1575.0, 1575.0, 1590.0, 1590.0], [1552.5, 1552.5, 1582.5, 1582.5], [1522.5, 1522.5, 1567.5, 1567.5], [1605.0, 1605.0, 1615.0, 1615.0], [1625.0, 1625.0, 1635.0, 1635.0], [1610.0, 1610.0, 1630.0, 1630.0], [1645.0, 1645.0, 1655.0, 1655.0], [1665.0, 1665.0, 1675.0, 1675.0], [1650.0, 1650.0, 1670.0, 1670.0], [1620.0, 1620.0, 1660.0, 1660.0], [1685.0, 1685.0, 1695.0, 1695.0], [1705.0, 1705.0, 1715.0, 1715.0], [1690.0, 1690.0, 1710.0, 1710.0], [1640.0, 1640.0, 1700.0, 1700.0], [1725.0, 1725.0, 1735.0, 1735.0], [1745.0, 1745.0, 1755.0, 1755.0], [1730.0, 1730.0, 1750.0, 1750.0], [1765.0, 1765.0, 1775.0, 1775.0], [1795.0, 1795.0, 1805.0, 1805.0], [1825.0, 1825.0, 1835.0, 1835.0], [1815.0, 1815.0, 1830.0, 1830.0], [1800.0, 1800.0, 1822.5, 1822.5], [1785.0, 1785.0, 1811.25, 1811.25], [1770.0, 1770.0, 1798.125, 1798.125], [1740.0, 1740.0, 1784.0625, 1784.0625], [1670.0, 1670.0, 1762.03125, 1762.03125], [1855.0, 1855.0, 1865.0, 1865.0], [1845.0, 1845.0, 1860.0, 1860.0], [1875.0, 1875.0, 1885.0, 1885.0], [1905.0, 1905.0, 1915.0, 1915.0], [1895.0, 1895.0, 1910.0, 1910.0], [1880.0, 1880.0, 1902.5, 1902.5], [1852.5, 1852.5, 1891.25, 1891.25], [1935.0, 1935.0, 1945.0, 1945.0], [1955.0, 1955.0, 1965.0, 1965.0], [1975.0, 1975.0, 1985.0, 1985.0], [1960.0, 1960.0, 1980.0, 1980.0], [1940.0, 1940.0, 1970.0, 1970.0], [1925.0, 1925.0, 1955.0, 1955.0], [1871.875, 1871.875, 1940.0, 1940.0], [1716.015625, 1716.015625, 1905.9375, 1905.9375], [1545.0, 1545.0, 1810.9765625, 1810.9765625], [1995.0, 1995.0, 2005.0, 2005.0], [2025.0, 2025.0, 2035.0, 2035.0], [2015.0, 2015.0, 2030.0, 2030.0], [2045.0, 2045.0, 2055.0, 2055.0], [2065.0, 2065.0, 2075.0, 2075.0], [2050.0, 2050.0, 2070.0, 2070.0], [2022.5, 2022.5, 2060.0, 2060.0], [2000.0, 2000.0, 2041.25, 2041.25], [2085.0, 2085.0, 2095.0, 2095.0], [2115.0, 2115.0, 2125.0, 2125.0], [2105.0, 2105.0, 2120.0, 2120.0], [2145.0, 2145.0, 2155.0, 2155.0], [2135.0, 2135.0, 2150.0, 2150.0], [2185.0, 2185.0, 2195.0, 2195.0], [2205.0, 2205.0, 2215.0, 2215.0], [2190.0, 2190.0, 2210.0, 2210.0], [2175.0, 2175.0, 2200.0, 2200.0], [2165.0, 2165.0, 2187.5, 2187.5], [2142.5, 2142.5, 2176.25, 2176.25], [2112.5, 2112.5, 2159.375, 2159.375], [2090.0, 2090.0, 2135.9375, 2135.9375], [2020.625, 2020.625, 2112.96875, 2112.96875], [2245.0, 2245.0, 2255.0, 2255.0], [2235.0, 2235.0, 2250.0, 2250.0], [2225.0, 2225.0, 2242.5, 2242.5], [2265.0, 2265.0, 2275.0, 2275.0], [2285.0, 2285.0, 2295.0, 2295.0], [2305.0, 2305.0, 2315.0, 2315.0], [2325.0, 2325.0, 2335.0, 2335.0], [2310.0, 2310.0, 2330.0, 2330.0], [2290.0, 2290.0, 2320.0, 2320.0], [2270.0, 2270.0, 2305.0, 2305.0], [2355.0, 2355.0, 2365.0, 2365.0], [2345.0, 2345.0, 2360.0, 2360.0], [2375.0, 2375.0, 2385.0, 2385.0], [2352.5, 2352.5, 2380.0, 2380.0], [2287.5, 2287.5, 2366.25, 2366.25], [2233.75, 2233.75, 2326.875, 2326.875], [2405.0, 2405.0, 2415.0, 2415.0], [2395.0, 2395.0, 2410.0, 2410.0], [2445.0, 2445.0, 2455.0, 2455.0], [2435.0, 2435.0, 2450.0, 2450.0], [2425.0, 2425.0, 2442.5, 2442.5], [2402.5, 2402.5, 2433.75, 2433.75], [2475.0, 2475.0, 2485.0, 2485.0], [2465.0, 2465.0, 2480.0, 2480.0], [2495.0, 2495.0, 2505.0, 2505.0], [2515.0, 2515.0, 2525.0, 2525.0], [2500.0, 2500.0, 2520.0, 2520.0], [2472.5, 2472.5, 2510.0, 2510.0], [2535.0, 2535.0, 2545.0, 2545.0], [2555.0, 2555.0, 2565.0, 2565.0], [2585.0, 2585.0, 2595.0, 2595.0], [2575.0, 2575.0, 2590.0, 2590.0], [2605.0, 2605.0, 2615.0, 2615.0], [2582.5, 2582.5, 2610.0, 2610.0], [2560.0, 2560.0, 2596.25, 2596.25], [2540.0, 2540.0, 2578.125, 2578.125], [2491.25, 2491.25, 2559.0625, 2559.0625], [2418.125, 2418.125, 2525.15625, 2525.15625], [2645.0, 2645.0, 2655.0, 2655.0], [2635.0, 2635.0, 2650.0, 2650.0], [2625.0, 2625.0, 2642.5, 2642.5], [2685.0, 2685.0, 2695.0, 2695.0], [2675.0, 2675.0, 2690.0, 2690.0], [2665.0, 2665.0, 2682.5, 2682.5], [2633.75, 2633.75, 2673.75, 2673.75], [2471.640625, 2471.640625, 2653.75, 2653.75], [2280.3125, 2280.3125, 2562.6953125, 2562.6953125], [2066.796875, 2066.796875, 2421.50390625, 2421.50390625], [1677.98828125, 1677.98828125, 2244.150390625, 2244.150390625], [927.1484375, 927.1484375, 1961.0693359375, 1961.0693359375], [157.607421875, 157.607421875, 1444.10888671875, 1444.10888671875]], 'dcoord': [[0.0, np.float64(0.1870761705216816), np.float64(0.1870761705216816), 0.0], [0.0, np.float64(0.3024034387694835), np.float64(0.3024034387694835), np.float64(0.1870761705216816)], [0.0, np.float64(0.19077756103128884), np.float64(0.19077756103128884), 0.0], [0.0, np.float64(0.04578122353309661), np.float64(0.04578122353309661), 0.0], [0.0, np.float64(0.18131800047526048), np.float64(0.18131800047526048), np.float64(0.04578122353309661)], [0.0, np.float64(0.0805928158009389), np.float64(0.0805928158009389), 0.0], [0.0, np.float64(0.19512216584948583), np.float64(0.19512216584948583), np.float64(0.0805928158009389)], [np.float64(0.18131800047526048), np.float64(0.4144150420139097), np.float64(0.4144150420139097), np.float64(0.19512216584948583)], [np.float64(0.19077756103128884), np.float64(0.437894584977665), np.float64(0.437894584977665), np.float64(0.4144150420139097)], [0.0, np.float64(0.10495014214389124), np.float64(0.10495014214389124), 0.0], [0.0, np.float64(0.2719256732448424), np.float64(0.2719256732448424), np.float64(0.10495014214389124)], [0.0, np.float64(0.6313578197747096), np.float64(0.6313578197747096), np.float64(0.2719256732448424)], [np.float64(0.437894584977665), np.float64(1.2341189892587492), np.float64(1.2341189892587492), np.float64(0.6313578197747096)], [np.float64(0.3024034387694835), np.float64(1.4375478982461694), np.float64(1.4375478982461694), np.float64(1.2341189892587492)], [0.0, np.float64(0.13372406078251176), np.float64(0.13372406078251176), 0.0], [0.0, np.float64(0.20253911304427463), np.float64(0.20253911304427463), np.float64(0.13372406078251176)], [0.0, np.float64(0.12472351876914163), np.float64(0.12472351876914163), 0.0], [0.0, np.float64(0.18211876892092152), np.float64(0.18211876892092152), 0.0], [np.float64(0.12472351876914163), np.float64(0.23373208415351668), np.float64(0.23373208415351668), np.float64(0.18211876892092152)], [np.float64(0.20253911304427463), np.float64(0.6802774896711373), np.float64(0.6802774896711373), np.float64(0.23373208415351668)], [0.0, np.float64(0.041574506256380324), np.float64(0.041574506256380324), 0.0], [0.0, np.float64(0.1812219725098239), np.float64(0.1812219725098239), np.float64(0.041574506256380324)], [0.0, np.float64(0.2884314769331321), np.float64(0.2884314769331321), np.float64(0.1812219725098239)], [0.0, np.float64(0.3314840175242042), np.float64(0.3314840175242042), np.float64(0.2884314769331321)], [0.0, np.float64(0.0457812235330968), np.float64(0.0457812235330968), 0.0], [0.0, np.float64(0.09197920060000137), np.float64(0.09197920060000137), np.float64(0.0457812235330968)], [0.0, np.float64(0.1004180950291023), np.float64(0.1004180950291023), 0.0], [0.0, np.float64(0.20225919612470458), np.float64(0.20225919612470458), np.float64(0.1004180950291023)], [np.float64(0.09197920060000137), np.float64(0.41918790081314095), np.float64(0.41918790081314095), np.float64(0.20225919612470458)], [np.float64(0.3314840175242042), np.float64(1.05533492343773), np.float64(1.05533492343773), np.float64(0.41918790081314095)], [0.0, np.float64(0.08607491147185697), np.float64(0.08607491147185697), 0.0], [0.0, np.float64(0.12014708743222405), np.float64(0.12014708743222405), np.float64(0.08607491147185697)], [0.0, np.float64(0.22759932064697788), np.float64(0.22759932064697788), np.float64(0.12014708743222405)], [0.0, np.float64(0.09932608483266771), np.float64(0.09932608483266771), 0.0], [0.0, np.float64(0.060314980301964034), np.float64(0.060314980301964034), 0.0], [0.0, np.float64(0.13752591560555183), np.float64(0.13752591560555183), 0.0], [np.float64(0.060314980301964034), np.float64(0.21331299161058498), np.float64(0.21331299161058498), np.float64(0.13752591560555183)], [np.float64(0.09932608483266771), np.float64(0.3133564846245374), np.float64(0.3133564846245374), np.float64(0.21331299161058498)], [np.float64(0.22759932064697788), np.float64(0.6327220184330038), np.float64(0.6327220184330038), np.float64(0.3133564846245374)], [0.0, np.float64(0.07077998057079388), np.float64(0.07077998057079388), 0.0], [0.0, np.float64(0.10512271059626731), np.float64(0.10512271059626731), np.float64(0.07077998057079388)], [0.0, np.float64(0.18449108380402218), np.float64(0.18449108380402218), np.float64(0.10512271059626731)], [0.0, np.float64(0.05987641402810018), np.float64(0.05987641402810018), 0.0], [0.0, np.float64(0.10308854958070097), np.float64(0.10308854958070097), 0.0], [np.float64(0.05987641402810018), np.float64(0.18512981992611371), np.float64(0.18512981992611371), np.float64(0.10308854958070097)], [0.0, np.float64(0.058974491992892675), np.float64(0.058974491992892675), 0.0], [0.0, np.float64(0.08022207743695502), np.float64(0.08022207743695502), np.float64(0.058974491992892675)], [0.0, np.float64(0.03380673358737454), np.float64(0.03380673358737454), 0.0], [0.0, np.float64(0.07613170216937583), np.float64(0.07613170216937583), np.float64(0.03380673358737454)], [0.0, np.float64(0.09206372994229008), np.float64(0.09206372994229008), 0.0], [np.float64(0.07613170216937583), np.float64(0.12121272469367371), np.float64(0.12121272469367371), np.float64(0.09206372994229008)], [np.float64(0.08022207743695502), np.float64(0.36133263735185467), np.float64(0.36133263735185467), np.float64(0.12121272469367371)], [np.float64(0.18512981992611371), np.float64(0.4304570938730275), np.float64(0.4304570938730275), np.float64(0.36133263735185467)], [np.float64(0.18449108380402218), np.float64(0.7446780266154548), np.float64(0.7446780266154548), np.float64(0.4304570938730275)], [np.float64(0.6327220184330038), np.float64(1.2332146950696923), np.float64(1.2332146950696923), np.float64(0.7446780266154548)], [np.float64(1.05533492343773), np.float64(2.015185424942691), np.float64(2.015185424942691), np.float64(1.2332146950696923)], [np.float64(0.6802774896711373), np.float64(2.915467093534318), np.float64(2.915467093534318), np.float64(2.015185424942691)], [np.float64(1.4375478982461694), np.float64(4.470598837356109), np.float64(4.470598837356109), np.float64(2.915467093534318)], [0.0, np.float64(0.06592253808366533), np.float64(0.06592253808366533), 0.0], [0.0, np.float64(0.14747488464536362), np.float64(0.14747488464536362), 0.0], [np.float64(0.06592253808366533), np.float64(0.2781700106716746), np.float64(0.2781700106716746), np.float64(0.14747488464536362)], [0.0, np.float64(0.11800673878224055), np.float64(0.11800673878224055), 0.0], [0.0, np.float64(0.03708083614413328), np.float64(0.03708083614413328), 0.0], [0.0, np.float64(0.031468463675264124), np.float64(0.031468463675264124), 0.0], [0.0, np.float64(0.06324176491581478), np.float64(0.06324176491581478), np.float64(0.031468463675264124)], [np.float64(0.03708083614413328), np.float64(0.2095792804452954), np.float64(0.2095792804452954), np.float64(0.06324176491581478)], [np.float64(0.11800673878224055), np.float64(0.2908605786798144), np.float64(0.2908605786798144), np.float64(0.2095792804452954)], [np.float64(0.2781700106716746), np.float64(0.6280268324925675), np.float64(0.6280268324925675), np.float64(0.2908605786798144)], [0.0, np.float64(0.08790931748270701), np.float64(0.08790931748270701), 0.0], [0.0, np.float64(0.01961438765464374), np.float64(0.01961438765464374), 0.0], [0.0, np.float64(0.06607950432665938), np.float64(0.06607950432665938), np.float64(0.01961438765464374)], [0.0, np.float64(0.10480930177160952), np.float64(0.10480930177160952), np.float64(0.06607950432665938)], [0.0, np.float64(0.10953794999451803), np.float64(0.10953794999451803), 0.0], [np.float64(0.10480930177160952), np.float64(0.19518850136847857), np.float64(0.19518850136847857), np.float64(0.10953794999451803)], [0.0, np.float64(0.04930529735905236), np.float64(0.04930529735905236), 0.0], [0.0, np.float64(0.07476729016058468), np.float64(0.07476729016058468), np.float64(0.04930529735905236)], [0.0, np.float64(0.16150572623792578), np.float64(0.16150572623792578), np.float64(0.07476729016058468)], [0.0, np.float64(0.0), np.float64(0.0), 0.0], [0.0, np.float64(0.04226656910357046), np.float64(0.04226656910357046), 0.0], [np.float64(0.0), np.float64(0.08259679263614593), np.float64(0.08259679263614593), np.float64(0.04226656910357046)], [0.0, np.float64(0.025145382371063507), np.float64(0.025145382371063507), 0.0], [0.0, np.float64(0.01376745285792813), np.float64(0.01376745285792813), 0.0], [0.0, np.float64(0.07454305996312449), np.float64(0.07454305996312449), np.float64(0.01376745285792813)], [np.float64(0.025145382371063507), np.float64(0.09404970829299533), np.float64(0.09404970829299533), np.float64(0.07454305996312449)], [0.0, np.float64(0.23661327902844362), np.float64(0.23661327902844362), np.float64(0.09404970829299533)], [np.float64(0.08259679263614593), np.float64(0.31830341867851053), np.float64(0.31830341867851053), np.float64(0.23661327902844362)], [np.float64(0.16150572623792578), np.float64(0.37390156217227616), np.float64(0.37390156217227616), np.float64(0.31830341867851053)], [np.float64(0.19518850136847857), np.float64(0.7336928579618358), np.float64(0.7336928579618358), np.float64(0.37390156217227616)], [np.float64(0.08790931748270701), np.float64(0.9146741434206455), np.float64(0.9146741434206455), np.float64(0.7336928579618358)], [np.float64(0.6280268324925675), np.float64(1.375528655745024), np.float64(1.375528655745024), np.float64(0.9146741434206455)], [0.0, np.float64(0.0844073803034696), np.float64(0.0844073803034696), 0.0], [0.0, np.float64(0.13836785710995733), np.float64(0.13836785710995733), 0.0], [0.0, np.float64(0.03876389627557116), np.float64(0.03876389627557116), 0.0], [0.0, np.float64(0.08499297619188757), np.float64(0.08499297619188757), np.float64(0.03876389627557116)], [0.0, np.float64(0.02777671972294776), np.float64(0.02777671972294776), 0.0], [0.0, np.float64(0.033806733587374606), np.float64(0.033806733587374606), 0.0], [np.float64(0.02777671972294776), np.float64(0.1393901758772195), np.float64(0.1393901758772195), np.float64(0.033806733587374606)], [np.float64(0.08499297619188757), np.float64(0.16619076728435242), np.float64(0.16619076728435242), np.float64(0.1393901758772195)], [np.float64(0.13836785710995733), np.float64(0.30207837816348987), np.float64(0.30207837816348987), np.float64(0.16619076728435242)], [np.float64(0.0844073803034696), np.float64(0.6084695592946481), np.float64(0.6084695592946481), np.float64(0.30207837816348987)], [0.0, np.float64(0.01971274771871333), np.float64(0.01971274771871333), 0.0], [0.0, np.float64(0.10831237782476456), np.float64(0.10831237782476456), np.float64(0.01971274771871333)], [0.0, np.float64(0.21299620411232092), np.float64(0.21299620411232092), np.float64(0.10831237782476456)], [0.0, np.float64(0.020004926425424768), np.float64(0.020004926425424768), 0.0], [0.0, np.float64(0.02113328455178526), np.float64(0.02113328455178526), 0.0], [0.0, np.float64(0.07940062439541504), np.float64(0.07940062439541504), np.float64(0.02113328455178526)], [np.float64(0.020004926425424768), np.float64(0.10757331396168439), np.float64(0.10757331396168439), np.float64(0.07940062439541504)], [0.0, np.float64(0.1613312149732741), np.float64(0.1613312149732741), np.float64(0.10757331396168439)], [0.0, np.float64(0.0660214273388994), np.float64(0.0660214273388994), 0.0], [0.0, np.float64(0.0754361471131906), np.float64(0.0754361471131906), 0.0], [0.0, np.float64(0.1816493413838084), np.float64(0.1816493413838084), np.float64(0.0754361471131906)], [np.float64(0.0660214273388994), np.float64(0.3050835704601422), np.float64(0.3050835704601422), np.float64(0.1816493413838084)], [np.float64(0.1613312149732741), np.float64(0.36268757386088596), np.float64(0.36268757386088596), np.float64(0.3050835704601422)], [np.float64(0.21299620411232092), np.float64(0.8254899531772307), np.float64(0.8254899531772307), np.float64(0.36268757386088596)], [np.float64(0.6084695592946481), np.float64(1.1775132864332671), np.float64(1.1775132864332671), np.float64(0.8254899531772307)], [0.0, np.float64(0.09834795043433772), np.float64(0.09834795043433772), 0.0], [0.0, np.float64(0.05310303245200809), np.float64(0.05310303245200809), 0.0], [0.0, np.float64(0.05284127125730752), np.float64(0.05284127125730752), 0.0], [0.0, np.float64(0.0910492816149313), np.float64(0.0910492816149313), np.float64(0.05284127125730752)], [np.float64(0.05310303245200809), np.float64(0.1805604730151535), np.float64(0.1805604730151535), np.float64(0.0910492816149313)], [np.float64(0.09834795043433772), np.float64(0.24271656852598425), np.float64(0.24271656852598425), np.float64(0.1805604730151535)], [0.0, np.float64(0.06968777517364887), np.float64(0.06968777517364887), 0.0], [0.0, np.float64(0.02396386782766734), np.float64(0.02396386782766734), 0.0], [0.0, np.float64(0.04791381594429354), np.float64(0.04791381594429354), np.float64(0.02396386782766734)], [0.0, np.float64(0.10664956746914254), np.float64(0.10664956746914254), np.float64(0.04791381594429354)], [0.0, np.float64(0.11574720206599054), np.float64(0.11574720206599054), 0.0], [np.float64(0.10664956746914254), np.float64(0.21263241034593047), np.float64(0.21263241034593047), np.float64(0.11574720206599054)], [np.float64(0.06968777517364887), np.float64(0.29374519857323766), np.float64(0.29374519857323766), np.float64(0.21263241034593047)], [np.float64(0.24271656852598425), np.float64(0.7911539574596603), np.float64(0.7911539574596603), np.float64(0.29374519857323766)], [0.0, np.float64(0.08967370787600386), np.float64(0.08967370787600386), 0.0], [0.0, np.float64(0.17515129611257438), np.float64(0.17515129611257438), np.float64(0.08967370787600386)], [0.0, np.float64(0.3453537184984532), np.float64(0.3453537184984532), np.float64(0.17515129611257438)], [0.0, np.float64(0.07583790548048905), np.float64(0.07583790548048905), 0.0], [0.0, np.float64(0.10970753072849838), np.float64(0.10970753072849838), 0.0], [0.0, np.float64(0.10970753072849848), np.float64(0.10970753072849848), 0.0], [np.float64(0.10970753072849838), np.float64(0.2790715853426124), np.float64(0.2790715853426124), np.float64(0.10970753072849848)], [0.0, np.float64(0.02777671972294768), np.float64(0.02777671972294768), 0.0], [0.0, np.float64(0.021941506145699785), np.float64(0.021941506145699785), 0.0], [0.0, np.float64(0.07330434763966433), np.float64(0.07330434763966433), np.float64(0.021941506145699785)], [np.float64(0.02777671972294768), np.float64(0.1164859294222473), np.float64(0.1164859294222473), np.float64(0.07330434763966433)], [0.0, np.float64(0.07848219841862997), np.float64(0.07848219841862997), 0.0], [0.0, np.float64(0.11695859845696652), np.float64(0.11695859845696652), np.float64(0.07848219841862997)], [np.float64(0.1164859294222473), np.float64(0.3242444441049512), np.float64(0.3242444441049512), np.float64(0.11695859845696652)], [np.float64(0.2790715853426124), np.float64(0.520632406037902), np.float64(0.520632406037902), np.float64(0.3242444441049512)], [np.float64(0.07583790548048905), np.float64(0.8207927138111225), np.float64(0.8207927138111225), np.float64(0.520632406037902)], [np.float64(0.3453537184984532), np.float64(1.045332531415793), np.float64(1.045332531415793), np.float64(0.8207927138111225)], [np.float64(0.7911539574596603), np.float64(2.0779549995991315), np.float64(2.0779549995991315), np.float64(1.045332531415793)], [np.float64(1.1775132864332671), np.float64(3.2031504199346634), np.float64(3.2031504199346634), np.float64(2.0779549995991315)], [np.float64(1.375528655745024), np.float64(3.698163334022503), np.float64(3.698163334022503), np.float64(3.2031504199346634)], [0.0, np.float64(0.09501457714875802), np.float64(0.09501457714875802), 0.0], [0.0, np.float64(0.21286174700650728), np.float64(0.21286174700650728), np.float64(0.09501457714875802)], [0.0, np.float64(0.09365096725741795), np.float64(0.09365096725741795), 0.0], [0.0, np.float64(0.15859081038398234), np.float64(0.15859081038398234), np.float64(0.09365096725741795)], [0.0, np.float64(0.16003941140339836), np.float64(0.16003941140339836), 0.0], [0.0, np.float64(0.20338761449759105), np.float64(0.20338761449759105), np.float64(0.16003941140339836)], [np.float64(0.15859081038398234), np.float64(0.45262862070724175), np.float64(0.45262862070724175), np.float64(0.20338761449759105)], [np.float64(0.21286174700650728), np.float64(0.9253319903114339), np.float64(0.9253319903114339), np.float64(0.45262862070724175)], [0.0, np.float64(0.04750728857437897), np.float64(0.04750728857437897), 0.0], [0.0, np.float64(0.08001970570169943), np.float64(0.08001970570169943), 0.0], [np.float64(0.04750728857437897), np.float64(0.14148119350902358), np.float64(0.14148119350902358), np.float64(0.08001970570169943)], [0.0, np.float64(0.04930529735905241), np.float64(0.04930529735905241), 0.0], [0.0, np.float64(0.056609683435483685), np.float64(0.056609683435483685), 0.0], [np.float64(0.04930529735905241), np.float64(0.1527380268660218), np.float64(0.1527380268660218), np.float64(0.056609683435483685)], [np.float64(0.14148119350902358), np.float64(0.2638844185974317), np.float64(0.2638844185974317), np.float64(0.1527380268660218)], [0.0, np.float64(0.09932608483266785), np.float64(0.09932608483266785), 0.0], [0.0, np.float64(0.11800673878224051), np.float64(0.11800673878224051), 0.0], [np.float64(0.09932608483266785), np.float64(0.33359702209502234), np.float64(0.33359702209502234), np.float64(0.11800673878224051)], [np.float64(0.2638844185974317), np.float64(0.4384320251309928), np.float64(0.4384320251309928), np.float64(0.33359702209502234)], [0.0, np.float64(0.053103032452008245), np.float64(0.053103032452008245), 0.0], [0.0, np.float64(0.07670438020845634), np.float64(0.07670438020845634), 0.0], [np.float64(0.053103032452008245), np.float64(0.2356932825025733), np.float64(0.2356932825025733), np.float64(0.07670438020845634)], [0.0, np.float64(0.09501457714875794), np.float64(0.09501457714875794), 0.0], [0.0, np.float64(0.04044258029777168), np.float64(0.04044258029777168), 0.0], [0.0, np.float64(0.013767452857928061), np.float64(0.013767452857928061), 0.0], [0.0, np.float64(0.05564049829506174), np.float64(0.05564049829506174), np.float64(0.013767452857928061)], [np.float64(0.04044258029777168), np.float64(0.09449452786454857), np.float64(0.09449452786454857), np.float64(0.05564049829506174)], [0.0, np.float64(0.1280708498772297), np.float64(0.1280708498772297), np.float64(0.09449452786454857)], [np.float64(0.09501457714875794), np.float64(0.26978232156482174), np.float64(0.26978232156482174), np.float64(0.1280708498772297)], [np.float64(0.2356932825025733), np.float64(0.6784480676436403), np.float64(0.6784480676436403), np.float64(0.26978232156482174)], [np.float64(0.4384320251309928), np.float64(0.8469067558906795), np.float64(0.8469067558906795), np.float64(0.6784480676436403)], [0.0, np.float64(0.06490370633023229), np.float64(0.06490370633023229), 0.0], [0.0, np.float64(0.0911695760295435), np.float64(0.0911695760295435), np.float64(0.06490370633023229)], [0.0, np.float64(0.06339985365535575), np.float64(0.06339985365535575), 0.0], [0.0, np.float64(0.06415798416717675), np.float64(0.06415798416717675), 0.0], [0.0, np.float64(0.0859136372565939), np.float64(0.0859136372565939), np.float64(0.06415798416717675)], [np.float64(0.06339985365535575), np.float64(0.2376886483654185), np.float64(0.2376886483654185), np.float64(0.0859136372565939)], [np.float64(0.0911695760295435), np.float64(0.5572748729948904), np.float64(0.5572748729948904), np.float64(0.2376886483654185)], [0.0, np.float64(0.01376745285792802), np.float64(0.01376745285792802), 0.0], [0.0, np.float64(0.04157450625638077), np.float64(0.04157450625638077), 0.0], [0.0, np.float64(0.099033569232161), np.float64(0.099033569232161), 0.0], [np.float64(0.04157450625638077), np.float64(0.1741983763661494), np.float64(0.1741983763661494), np.float64(0.099033569232161)], [np.float64(0.01376745285792802), np.float64(0.20984746479654998), np.float64(0.20984746479654998), np.float64(0.1741983763661494)], [0.0, np.float64(0.8326928233967623), np.float64(0.8326928233967623), np.float64(0.20984746479654998)], [np.float64(0.5572748729948904), np.float64(1.1248793960092458), np.float64(1.1248793960092458), np.float64(0.8326928233967623)], [np.float64(0.8469067558906795), np.float64(1.8284521219016407), np.float64(1.8284521219016407), np.float64(1.1248793960092458)], [np.float64(0.9253319903114339), np.float64(2.923863823712281), np.float64(2.923863823712281), np.float64(1.8284521219016407)], [0.0, np.float64(0.2902143973510944), np.float64(0.2902143973510944), 0.0], [0.0, np.float64(0.06091045787974399), np.float64(0.06091045787974399), 0.0], [0.0, np.float64(0.1644837316839658), np.float64(0.1644837316839658), np.float64(0.06091045787974399)], [0.0, np.float64(0.11152594824642835), np.float64(0.11152594824642835), 0.0], [0.0, np.float64(0.17976862412901617), np.float64(0.17976862412901617), 0.0], [np.float64(0.11152594824642835), np.float64(0.22168527488512896), np.float64(0.22168527488512896), np.float64(0.17976862412901617)], [np.float64(0.1644837316839658), np.float64(0.4772482563541379), np.float64(0.4772482563541379), np.float64(0.22168527488512896)], [np.float64(0.2902143973510944), np.float64(0.6099155550707043), np.float64(0.6099155550707043), np.float64(0.4772482563541379)], [0.0, np.float64(0.25145382371063546), np.float64(0.25145382371063546), 0.0], [0.0, np.float64(0.1106029904471379), np.float64(0.1106029904471379), 0.0], [0.0, np.float64(0.32841464018182076), np.float64(0.32841464018182076), np.float64(0.1106029904471379)], [0.0, np.float64(0.03708083614413336), np.float64(0.03708083614413336), 0.0], [0.0, np.float64(0.06812288774544334), np.float64(0.06812288774544334), np.float64(0.03708083614413336)], [0.0, np.float64(0.021133284551785557), np.float64(0.021133284551785557), 0.0], [0.0, np.float64(0.05660968343548381), np.float64(0.05660968343548381), 0.0], [np.float64(0.021133284551785557), np.float64(0.1228684233851348), np.float64(0.1228684233851348), np.float64(0.05660968343548381)], [0.0, np.float64(0.17831629281255884), np.float64(0.17831629281255884), np.float64(0.1228684233851348)], [0.0, np.float64(0.23851445269608665), np.float64(0.23851445269608665), np.float64(0.17831629281255884)], [np.float64(0.06812288774544334), np.float64(0.4297056360374467), np.float64(0.4297056360374467), np.float64(0.23851445269608665)], [np.float64(0.32841464018182076), np.float64(0.8116252381222404), np.float64(0.8116252381222404), np.float64(0.4297056360374467)], [np.float64(0.25145382371063546), np.float64(1.6161339252349698), np.float64(1.6161339252349698), np.float64(0.8116252381222404)], [np.float64(0.6099155550707043), np.float64(1.9158358357062477), np.float64(1.9158358357062477), np.float64(1.6161339252349698)], [0.0, np.float64(0.08615369263954964), np.float64(0.08615369263954964), 0.0], [0.0, np.float64(0.2534488714752394), np.float64(0.2534488714752394), np.float64(0.08615369263954964)], [0.0, np.float64(0.3403487423419967), np.float64(0.3403487423419967), np.float64(0.2534488714752394)], [0.0, np.float64(0.10464889338206325), np.float64(0.10464889338206325), 0.0], [0.0, np.float64(0.15144198143720866), np.float64(0.15144198143720866), 0.0], [0.0, np.float64(0.07845755061857473), np.float64(0.07845755061857473), 0.0], [0.0, np.float64(0.09667512488103226), np.float64(0.09667512488103226), 0.0], [np.float64(0.07845755061857473), np.float64(0.20643885503184156), np.float64(0.20643885503184156), np.float64(0.09667512488103226)], [np.float64(0.15144198143720866), np.float64(0.31439623348615314), np.float64(0.31439623348615314), np.float64(0.20643885503184156)], [np.float64(0.10464889338206325), np.float64(0.3711179430813692), np.float64(0.3711179430813692), np.float64(0.31439623348615314)], [0.0, np.float64(0.03708083614413336), np.float64(0.03708083614413336), 0.0], [0.0, np.float64(0.15546410451371373), np.float64(0.15546410451371373), np.float64(0.03708083614413336)], [0.0, np.float64(0.2231098825711949), np.float64(0.2231098825711949), 0.0], [np.float64(0.15546410451371373), np.float64(0.4960420512099398), np.float64(0.4960420512099398), np.float64(0.2231098825711949)], [np.float64(0.3711179430813692), np.float64(0.8166519532325831), np.float64(0.8166519532325831), np.float64(0.4960420512099398)], [np.float64(0.3403487423419967), np.float64(1.103117065532596), np.float64(1.103117065532596), np.float64(0.8166519532325831)], [0.0, np.float64(0.005900336939112016), np.float64(0.005900336939112016), 0.0], [0.0, np.float64(0.05797641419656147), np.float64(0.05797641419656147), np.float64(0.005900336939112016)], [0.0, np.float64(0.0990335692321611), np.float64(0.0990335692321611), 0.0], [0.0, np.float64(0.11524529185419849), np.float64(0.11524529185419849), np.float64(0.0990335692321611)], [0.0, np.float64(0.17682521574689233), np.float64(0.17682521574689233), np.float64(0.11524529185419849)], [np.float64(0.05797641419656147), np.float64(0.34542753039156904), np.float64(0.34542753039156904), np.float64(0.17682521574689233)], [0.0, np.float64(0.0019667789797040147), np.float64(0.0019667789797040147), 0.0], [0.0, np.float64(0.09094300734427055), np.float64(0.09094300734427055), np.float64(0.0019667789797040147)], [0.0, np.float64(0.05987641402810051), np.float64(0.05987641402810051), 0.0], [0.0, np.float64(0.06922477488606389), np.float64(0.06922477488606389), 0.0], [np.float64(0.05987641402810051), np.float64(0.18314171034404586), np.float64(0.18314171034404586), np.float64(0.06922477488606389)], [np.float64(0.09094300734427055), np.float64(0.21659184127702033), np.float64(0.21659184127702033), np.float64(0.18314171034404586)], [0.0, np.float64(0.06144789346719025), np.float64(0.06144789346719025), 0.0], [0.0, np.float64(0.015734231837632062), np.float64(0.015734231837632062), 0.0], [0.0, np.float64(0.021133284551785567), np.float64(0.021133284551785567), 0.0], [0.0, np.float64(0.03589856915705526), np.float64(0.03589856915705526), np.float64(0.021133284551785567)], [0.0, np.float64(0.039228775309287256), np.float64(0.039228775309287256), 0.0], [np.float64(0.03589856915705526), np.float64(0.11245381626361316), np.float64(0.11245381626361316), np.float64(0.039228775309287256)], [np.float64(0.015734231837632062), np.float64(0.2230553038197867), np.float64(0.2230553038197867), np.float64(0.11245381626361316)], [np.float64(0.06144789346719025), np.float64(0.28923014878229436), np.float64(0.28923014878229436), np.float64(0.2230553038197867)], [np.float64(0.21659184127702033), np.float64(0.736172769361378), np.float64(0.736172769361378), np.float64(0.28923014878229436)], [np.float64(0.34542753039156904), np.float64(0.8706295509780765), np.float64(0.8706295509780765), np.float64(0.736172769361378)], [0.0, np.float64(0.06339985365535562), np.float64(0.06339985365535562), 0.0], [0.0, np.float64(0.13823121561535562), np.float64(0.13823121561535562), np.float64(0.06339985365535562)], [0.0, np.float64(0.5121211833598222), np.float64(0.5121211833598222), np.float64(0.13823121561535562)], [0.0, np.float64(0.25839357574544447), np.float64(0.25839357574544447), 0.0], [0.0, np.float64(0.3573911169470654), np.float64(0.3573911169470654), np.float64(0.25839357574544447)], [0.0, np.float64(0.9859918408899887), np.float64(0.9859918408899887), np.float64(0.3573911169470654)], [np.float64(0.5121211833598222), np.float64(1.1814553746593273), np.float64(1.1814553746593273), np.float64(0.9859918408899887)], [np.float64(0.8706295509780765), np.float64(2.0745046179256708), np.float64(2.0745046179256708), np.float64(1.1814553746593273)], [np.float64(1.103117065532596), np.float64(3.0702709034466285), np.float64(3.0702709034466285), np.float64(2.0745046179256708)], [np.float64(1.9158358357062477), np.float64(4.604771554303577), np.float64(4.604771554303577), np.float64(3.0702709034466285)], [np.float64(2.923863823712281), np.float64(5.405543147924774), np.float64(5.405543147924774), np.float64(4.604771554303577)], [np.float64(3.698163334022503), np.float64(18.38392642534597), np.float64(18.38392642534597), np.float64(5.405543147924774)], [np.float64(4.470598837356109), np.float64(23.73708787183316), np.float64(23.73708787183316), np.float64(18.38392642534597)]], 'ivl': ['254', '224', '240', '241', '264', '231', '220', '251', '218', '221', '230', '210', '234', '236', '248', '259', '213', '255', '245', '247', '239', '250', '228', '212', '262', '227', '252', '265', '211', '266', '249', '233', '242', '253', '226', '244', '268', '214', '258', '232', '237', '219', '229', '238', '222', '216', '235', '225', '261', '260', '267', '263', '215', '217', '257', '246', '256', '223', '269', '0', '129', '127', '185', '136', '192', '154', '183', '145', '148', '196', '149', '186', '168', '141', '139', '202', '132', '160', '172', '123', '167', '184', '177', '180', '133', '179', '182', '121', '205', '152', '164', '175', '147', '243', '188', '200', '165', '135', '171', '156', '166', '137', '190', '122', '163', '134', '153', '208', '130', '140', '201', '131', '195', '174', '204', '197', '155', '178', '161', '181', '157', '203', '207', '150', '159', '176', '209', '162', '187', '124', '189', '193', '194', '120', '146', '142', '158', '138', '170', '128', '143', '169', '198', '144', '191', '151', '199', '206', '173', '125', '126', '9', '21', '47', '61', '56', '83', '35', '106', '110', '24', '31', '91', '93', '39', '62', '79', '86', '58', '100', '22', '72', '81', '98', '54', '103', '29', '77', '27', '40', '92', '63', '28', '65', '45', '17', '113', '78', '85', '23', '44', '50', '99', '14', '33', '88', '119', '97', '112', '71', '76', '111', '8', '84', '12', '96', '66', '95', '26', '53', '37', '87', '109', '10', '20', '108', '64', '74', '57', '60', '4', '118', '68', '114', '67', '104', '25', '73', '3', '48', '7', '101', '69', '80', '16', '6', '107', '5', '38', '1', '13', '59', '30', '75', '11', '43', '52', '15', '70', '42', '51', '19', '82', '41', '55', '116', '117', '46', '36', '105', '2', '89', '90', '18', '32', '49', '115', '34', '94', '102'], 'leaves': [254, 224, 240, 241, 264, 231, 220, 251, 218, 221, 230, 210, 234, 236, 248, 259, 213, 255, 245, 247, 239, 250, 228, 212, 262, 227, 252, 265, 211, 266, 249, 233, 242, 253, 226, 244, 268, 214, 258, 232, 237, 219, 229, 238, 222, 216, 235, 225, 261, 260, 267, 263, 215, 217, 257, 246, 256, 223, 269, 0, 129, 127, 185, 136, 192, 154, 183, 145, 148, 196, 149, 186, 168, 141, 139, 202, 132, 160, 172, 123, 167, 184, 177, 180, 133, 179, 182, 121, 205, 152, 164, 175, 147, 243, 188, 200, 165, 135, 171, 156, 166, 137, 190, 122, 163, 134, 153, 208, 130, 140, 201, 131, 195, 174, 204, 197, 155, 178, 161, 181, 157, 203, 207, 150, 159, 176, 209, 162, 187, 124, 189, 193, 194, 120, 146, 142, 158, 138, 170, 128, 143, 169, 198, 144, 191, 151, 199, 206, 173, 125, 126, 9, 21, 47, 61, 56, 83, 35, 106, 110, 24, 31, 91, 93, 39, 62, 79, 86, 58, 100, 22, 72, 81, 98, 54, 103, 29, 77, 27, 40, 92, 63, 28, 65, 45, 17, 113, 78, 85, 23, 44, 50, 99, 14, 33, 88, 119, 97, 112, 71, 76, 111, 8, 84, 12, 96, 66, 95, 26, 53, 37, 87, 109, 10, 20, 108, 64, 74, 57, 60, 4, 118, 68, 114, 67, 104, 25, 73, 3, 48, 7, 101, 69, 80, 16, 6, 107, 5, 38, 1, 13, 59, 30, 75, 11, 43, 52, 15, 70, 42, 51, 19, 82, 41, 55, 116, 117, 46, 36, 105, 2, 89, 90, 18, 32, 49, 115, 34, 94, 102], 'color_list': ['C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C0', 'C0'], 'leaves_color_list': ['C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C1', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C2', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'C3']}
achsen[0].set_title("Ward")
for nummer, farbe in zip([1, 2, 3], ["#2166ac", "#b2182b", "#1b7837"]):
    teil = skaliert[gruppen == nummer]
    achsen[1].scatter(teil[:, 0], teil[:, 1], s=10, color=farbe)
achsen[1].set_xlabel("Besuche (z)")
achsen[1].set_ylabel("Umsatz (z)")
achsen[1].set_title("Schnitt bei k = 3")
plt.tight_layout()
plt.show()
Abbildung 4: Beispiel 2: dieselbe Darstellung in Python.
print(kunden.assign(gruppe=gruppen).groupby("gruppe").mean().round(1))
        besuche  umsatz
gruppe                 
1           2.9  2169.5
2           5.9   905.1
3          13.9  1359.9
print(pd.crosstab(gruppen, gruppe_wahr))
col_0    A   B   C
row_0             
1        0   0  59
2        1  90   1
3      119   0   0

Output Zeile für Zeile

Die acht letzten Verschmelzungshöhen, von oben nach unten gelesen:

Schritt von oben 1 2 3 4 5 6 7 8
Höhe (Python) 23.737 18.384 5.406 4.605 4.471 3.698 3.203 3.070
Höhe (R) 23.693 18.350 5.396 4.596 4.462 3.691 3.197 3.065

Die beiden Zeilen unterscheiden sich um zwei Promille, weil scale() in R durch die Standardabweichung mit Nenner \(n-1\) teilt und StandardScaler in Python durch die mit Nenner \(n\). Die Verhältnisse, worauf es hier ankommt, sind identisch.

Ausgabe Wert hier Wie er zu lesen ist
Der Sprung von 5.406 auf 18.384, also mehr als das Dreifache Genau hier werden Gruppen vereint, die sich stark unterscheiden. Die letzte Verschmelzung oberhalb dieses Sprungs macht zwei Gruppen, die vorletzte drei, deshalb k = 3.
Die Höhen darunter 5.406, 4.605, 4.471, … Gleichmässig abfallend. Zwischen ihnen gibt es keinen ausgezeichneten Punkt, jeder tiefere Schnitt wäre willkürlich.
Gruppengrössen 59, 92, 119 Die eingebauten Gruppen waren 60, 90 und 120 gross.
kophenetische Korrelation 0.845 Über der Faustregel von 0.75: Das Dendrogramm gibt die Abstände gut wieder.
Kreuztabelle 268 von 270 richtig Zwei Kunden aus den Randbereichen wechseln die Gruppe: einer aus jeder der beiden äusseren Gruppen landet in der mittleren.

Warum die Höhen bei Ward so gross sind: Sie sind keine Distanzen zwischen Punkten, sondern der Zuwachs an Streuung, den eine Verschmelzung verursacht. Ein Vergleich mit den Höhen eines Single-Linkage-Baums oder mit Werten aus einem anderen Datensatz ist deshalb sinnlos. Aussagekräftig ist allein das Verhältnis der Höhen innerhalb eines Baums.

Interpretation und Ergebnissatz

Die Gruppenmittelwerte in den ursprünglichen Einheiten machen die Gruppen beschreibbar:

Gruppe n Besuche Umsatz Beschreibung
Grosseinkäufer 59 2.9 2170 kommen fast nie, kaufen dann viel
Gelegenheitskunden 92 5.9 905 kommen selten, geben wenig aus
Stammkunden 119 13.9 1360 kommen oft, geben solide aus

Das Ergebnis stimmt praktisch mit dem von k-Means überein, kein Zufall, denn Ward minimiert dieselbe Grösse wie k-Means, nur schrittweise.

Ein hierarchisches Clustering (euklidische Distanz auf z-standardisierten Merkmalen, Ward-Verknüpfung, n = 270) ergibt bei einem Schnitt auf k = 3 Gruppen von 59, 92 und 119 Kunden. Der Schnitt ist durch den Sprung in den Verschmelzungshöhen begründet (5.41 auf 18.38). Die kophenetische Korrelation beträgt 0.85.

Frage und Datenlage

Ein Dendrogramm entsteht immer, auch aus Daten ohne jede Gruppenstruktur. 200 Punkte aus einer einzigen Wolke werden zum Vergleich herangezogen.

Rechnung

ohne_struktur <- matrix(normalwerte(13013, 400), ncol = 2)

baum_b <- hclust(dist(scale(ohne_struktur)), method = "ward.D2")

par(mar = c(2, 4, 3, 1))
plot(baum_b, labels = FALSE, hang = -1, main = "ohne Struktur",
     xlab = "", sub = "")

hoehen_ohne <- rev(baum_b$height)[1:8]
hoehen_mit <- rev(baum$height)[1:8]
round(data.frame(schritt = 1:8, mit_struktur = hoehen_mit,
                 ohne_struktur = hoehen_ohne,
                 faktor_mit = c(NA, hoehen_mit[-8] / hoehen_mit[-1]),
                 faktor_ohne = c(NA, hoehen_ohne[-8] / hoehen_ohne[-1])), 2)
  schritt mit_struktur ohne_struktur faktor_mit faktor_ohne
1       1        23.69         14.77         NA          NA
2       2        18.35         13.90       1.29        1.06
3       3         5.40          8.02       3.40        1.73
4       4         4.60          7.83       1.17        1.02
5       5         4.46          7.56       1.03        1.04
6       6         3.69          5.66       1.21        1.34
7       7         3.20          4.12       1.15        1.37
8       8         3.06          3.76       1.04        1.09
Abbildung 5: Beispiel 3: Dendrogramm zu Daten ohne Gruppenstruktur.
ohne_struktur = normalwerte(13013, 400).reshape(200, 2)

verbund_b = linkage(StandardScaler().fit_transform(ohne_struktur),
                    method="ward")

hoehen_ohne = verbund_b[-8:, 2][::-1]
hoehen_mit = verbund[-8:, 2][::-1]
print(pd.DataFrame({
    "schritt": range(1, 9),
    "mit_struktur": hoehen_mit.round(2),
    "ohne_struktur": hoehen_ohne.round(2),
    "faktor_mit": np.r_[np.nan, hoehen_mit[:-1] / hoehen_mit[1:]].round(2),
    "faktor_ohne": np.r_[np.nan, hoehen_ohne[:-1] / hoehen_ohne[1:]].round(2),
}).to_string(index=False))
 schritt  mit_struktur  ohne_struktur  faktor_mit  faktor_ohne
       1         23.74          14.89         NaN          NaN
       2         18.38          14.60        1.29         1.02
       3          5.41           8.38        3.40         1.74
       4          4.60           6.61        1.17         1.27
       5          4.47           6.19        1.03         1.07
       6          3.70           6.18        1.21         1.00
       7          3.20           5.01        1.15         1.23
       8          3.07           4.02        1.04         1.25

Output Zeile für Zeile

Schritt von oben mit Struktur Faktor ohne Struktur Faktor
1 23.69 14.77
2 18.35 1.29 13.90 1.06
3 5.40 3.40 8.02 1.73
4 4.60 1.17 7.83 1.02
5 4.46 1.03 7.56 1.04
6 3.69 1.21 5.66 1.34
7 3.20 1.15 4.12 1.37
8 3.06 1.04 3.76 1.09

(R-Ausgabe; die Python-Werte liegen um wenige Promille daneben, siehe Beispiel 2.)

Entscheidend ist die Spalte Faktor: um welches Vielfache eine Verschmelzung höher liegt als die nächsttiefere.

Beobachtung Deutung
Mit Struktur: 3.40 an einer Stelle, sonst 1.0 bis 1.3 Ein klar herausragender Sprung. Er markiert die Gruppenzahl.
Ohne Struktur: höchster Faktor 1.73, dann 1.37, 1.34, 1.09 Kein herausragender Wert. Der grösste Sprung ist nur wenig grösser als die übrigen, das ist reine Zufallsschwankung.
Die Höhen selbst sind ohne Struktur nicht kleiner Ein Baum sieht nicht “leer” aus, wenn es nichts gibt. Die absolute Höhe sagt nichts; nur das Verhältnis zu den Nachbarn tut es.

Das ist die eigentliche Falle des Verfahrens: Ein Schnitt ist immer möglich. Man erhält Gruppen, die keine sind, und kann sie hinterher sogar beschreiben, weil jede Aufteilung Mittelwertunterschiede erzeugt. Der Test darauf, ob überhaupt Struktur vorliegt, gehört vor die Interpretation: Verlauf der Verschmelzungshöhen, Silhouette und Gap-Statistik, siehe Clustergüte.

Interpretation und Ergebnissatz

Die Verschmelzungshöhen fallen gleichmässig ab; der grösste Sprung übertrifft die übrigen nur um den Faktor 1.7, gegenüber 3.4 bei den strukturierten Daten. Die Daten geben keine Gruppenstruktur her, es wird keine Aufteilung berichtet.

Frage und Datenlage

Zwei kompakte Wolken, dazwischen zwölf Punkte auf einer dünnen Linie. Für das Auge sind es zwei Gruppen plus eine Brücke. Was machen Single und Ward daraus?

Rechnung

wolke_a <- cbind(normalwerte(5001, 80, -2, 0.4), normalwerte(5002, 80, 0, 0.4))
wolke_b <- cbind(normalwerte(5003, 80, 2, 0.4), normalwerte(5004, 80, 0, 0.4))
bruecke <- cbind(seq(-1.6, 1.6, length.out = 12),
                 normalwerte(5005, 12, 0, 0.05))
brueckendaten <- rbind(wolke_a, wolke_b, bruecke)
b_dist <- dist(brueckendaten)

baum_single <- hclust(b_dist, method = "single")
baum_ward <- hclust(b_dist, method = "ward.D2")

g_single <- cutree(baum_single, k = 2)
g_ward <- cutree(baum_ward, k = 2)

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
plot(brueckendaten, pch = 16, cex = 0.6, col = c("#2166ac", "#b2182b")[g_single],
     xlab = "x", ylab = "y", main = "Single, k = 2")
plot(brueckendaten, pch = 16, cex = 0.6, col = c("#2166ac", "#b2182b")[g_ward],
     xlab = "x", ylab = "y", main = "Ward, k = 2")
par(mfrow = c(1, 1))

table(single = g_single)
single
  1   2 
171   1 
table(ward = g_ward)
ward
 1  2 
89 83 
round(c(single = cor(b_dist, cophenetic(baum_single)),
        ward = cor(b_dist, cophenetic(baum_ward))), 3)
single   ward 
 0.650  0.921 
Abbildung 6: Beispiel 4: zwei Wolken mit einer Brücke. Links Single, rechts Ward.
wolke_a = np.column_stack([normalwerte(5001, 80, -2, 0.4),
                           normalwerte(5002, 80, 0, 0.4)])
wolke_b = np.column_stack([normalwerte(5003, 80, 2, 0.4),
                           normalwerte(5004, 80, 0, 0.4)])
bruecke = np.column_stack([np.linspace(-1.6, 1.6, 12),
                           normalwerte(5005, 12, 0, 0.05)])
brueckendaten = np.vstack([wolke_a, wolke_b, bruecke])
b_dist = pdist(brueckendaten)

v_single = linkage(b_dist, method="single")
v_ward = linkage(brueckendaten, method="ward")

print("Single:", np.bincount(fcluster(v_single, 2, "maxclust"))[1:].tolist())
Single: [171, 1]
print("Ward:  ", np.bincount(fcluster(v_ward, 2, "maxclust"))[1:].tolist())
Ward:   [83, 89]
print("kophenetisch single:", round(float(cophenet(v_single, b_dist)[0]), 3))
kophenetisch single: 0.65
print("kophenetisch ward:  ", round(float(cophenet(v_ward, b_dist)[0]), 3))
kophenetisch ward:   0.921

Output Zeile für Zeile

Methode Gruppengrössen bei k = 2 kophenetische Korrelation
Single 171 und 1 0.650
Ward 83 und 89 0.921
Ausgabe Wie sie zu lesen ist
Single: 171 zu 1 Die zwölf Brückenpunkte reichen aus, um beide Wolken über eine Kette kurzer Abstände zu verbinden. Alles wird zu einer Gruppe, und der Schnitt bei k = 2 trennt nur noch den entlegensten Einzelpunkt ab. Aus dem Verfahren ist damit ein Ausreisserdetektor geworden.
Ward: 83 zu 89 Die Wolken werden getrennt, die Brückenpunkte auf beide verteilt. Das entspricht der Struktur, die man sehen würde.
kophenetisch 0.650 gegen 0.921 Auch als Zahl klar: Der Single-Baum gibt die tatsächlichen Abstände deutlich schlechter wieder.

Zu erkennen ist der Fall an zwei Stellen, noch bevor man deutet: an den Gruppengrössen, eine Gruppe mit einem einzigen Objekt ist immer ein Warnzeichen und am Dendrogramm, in dem ein langer Ast wächst, an den Blätter einzeln angehängt werden.

Interpretation und Ergebnissatz

Single-Linkage ist nicht falsch, sondern für etwas anderes gebaut: für langgezogene, kettenförmige Strukturen, wo eine Verbindung über Zwischenpunkte genau das Gesuchte ist. Für kompakte Gruppen ist es die falsche Wahl.

Single-Linkage verbindet die beiden Wolken über die Zwischenpunkte und liefert bei k = 2 Gruppen von 171 und 1 Objekt (kophenetische Korrelation 0.65). Berichtet wird die Ward-Lösung mit 83 und 89 Objekten (0.92).

Frage und Datenlage

Der praktische Vorteil des Verfahrens: Es braucht nur eine Distanzmatrix, nicht eine Datenmatrix. Damit lässt sich clustern, wo k-Means gar nicht anwendbar ist, etwa bei einer Mischung aus metrischen, nominalen und ordinalen Merkmalen.

Die Gower-Distanz löst das, indem sie je Merkmal einen Beitrag zwischen 0 und 1 bildet und über alle Merkmale mittelt: bei metrischen die auf die Spannweite bezogene Differenz, bei nominalen 0 bei Gleichheit und 1 sonst, bei ordinalen die Differenz der Ränge.

Rechnung

gemischt <- data.frame(
  umsatz = kunden$umsatz[1:60],
  besuche = kunden$besuche[1:60],
  segment = factor(rep(c("Privat", "Gewerbe"), 30)),
  zufriedenheit = factor(rep(c("niedrig", "mittel", "hoch"), 20),
                         levels = c("niedrig", "mittel", "hoch"), ordered = TRUE)
)

g_dist <- daisy(gemischt, metric = "gower")
summary(as.numeric(g_dist))
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.009628 0.234532 0.370422 0.368464 0.485611 0.945347 
baum_gower <- hclust(g_dist, method = "average")
gruppen_g <- cutree(baum_gower, k = 3)
table(gruppen_g)
gruppen_g
 1  2  3 
30 29  1 
# Gruppenprofile: metrisch als Mittelwert, kategorial als Haeufigkeit
round(aggregate(gemischt[, 1:2], by = list(gruppe = gruppen_g), FUN = mean), 1)
  gruppe umsatz besuche
1      1   1334    14.0
2      2   1343    13.4
3      3   1685    20.5
table(gruppe = gruppen_g, segment = gemischt$segment)
      segment
gruppe Gewerbe Privat
     1       0     30
     2      29      0
     3       1      0
gemischt = pd.DataFrame({
    "umsatz": kunden["umsatz"][:60].values,
    "besuche": kunden["besuche"][:60].values,
    "segment": np.tile(["Privat", "Gewerbe"], 30),
    "zufriedenheit": np.tile([0, 1, 2], 20),
})


def gower(df, ordinal=()):
    """Gower-Distanz von Hand: Python bringt sie nicht mit."""
    teile = []
    for name in df.columns:
        s = df[name]
        if name in ordinal or s.dtype.kind in "if":
            spanne = s.max() - s.min()
            teile.append(np.abs(s.values[:, None] - s.values[None, :]) / spanne)
        else:
            teile.append((s.values[:, None] != s.values[None, :]).astype(float))
    return np.mean(teile, axis=0)


matrix = gower(gemischt, ordinal=("zufriedenheit",))
kondensiert = matrix[np.triu_indices_from(matrix, k=1)]
print("Gower-Distanzen:", np.round([kondensiert.min(), np.median(kondensiert),
                                    kondensiert.max()], 3))
Gower-Distanzen: [0.01  0.37  0.945]
verbund_g = linkage(kondensiert, method="average")
gruppen_g = fcluster(verbund_g, 3, "maxclust")
print("Gruppengrössen:", np.bincount(gruppen_g)[1:].tolist())
Gruppengrössen: [30, 29, 1]
print(gemischt.assign(gruppe=gruppen_g)
      .groupby("gruppe")[["umsatz", "besuche"]].mean().round(1))
        umsatz  besuche
gruppe                 
1       1334.0     14.0
2       1343.0     13.4
3       1685.0     20.5
print(pd.crosstab(gruppen_g, gemischt["segment"]))
segment  Gewerbe  Privat
row_0                   
1              0      30
2             29       0
3              1       0

Output Zeile für Zeile

Ausgabe Wie sie zu lesen ist
Gower-Distanzen Liegen immer zwischen 0 und 1, unabhängig von den Einheiten der einzelnen Merkmale. Eine Distanz von 0.5 heisst: im Mittel über alle Merkmale halb so verschieden wie maximal möglich.
Gruppengrössen Bei vier Merkmalen, von denen zwei kategorial mit wenigen Stufen sind, entstehen Gruppen, die sich stark an diesen Stufen ausrichten, die kategorialen Merkmale erzeugen die grössten Distanzsprünge.
Gruppenprofile Erst die Kreuztabelle mit segment zeigt, was die Gruppen ausmacht. Ohne diesen Schritt bleibt bei gemischten Merkmalen völlig offen, welche Variable trennt.

R und Python liefern hier nicht dieselben Zahlen, und das ist erwartbar: cluster::daisy() behandelt geordnete Faktoren nach der Vorschrift von Podani über Ränge und Bindungen, die selbstgeschriebene Funktion oben nimmt die schlichte Rangdifferenz. Beide Wege sind vertretbar; welcher verwendet wurde, gehört deshalb in den Methodenteil.

Allgemeiner: Gower gewichtet alle Merkmale gleich. Wer zehn metrische und ein kategoriales Merkmal hat, lässt das kategoriale nahezu wirkungslos und umgekehrt. Die Zusammenstellung der Merkmale ist bei Gower eine Gewichtungsentscheidung.

Interpretation

Der Preis der Flexibilität ist die Deutung: Bei gemischten Merkmalen ist weniger offensichtlich, welche Variable eine Gruppe ausmacht, weshalb die Gruppenprofile umso wichtiger werden. Mehr zu den Distanzmassen und ihren Eigenheiten steht unter Distanzmasse.

Verständnisfragen

Die acht letzten Verschmelzungshöhen lauten 23.7, 18.4, 5.4, 4.6, 4.5, 3.7, 3.2, 3.1. Wie viele Gruppen legt das nahe?

Drei
Richtig. Der grosse Sprung liegt zwischen 5.4 und 18.4. Oberhalb davon liegen zwei Verschmelzungen, die drei Gruppen zu einer vereinen; geschnitten wird deshalb unterhalb, bei k = 3.
Zwei
Der oberste Schritt allein zu betrachten, unterschlägt, dass auch die zweite Verschmelzung noch oberhalb des Sprungs liegt.
Acht
Die Anzahl betrachteter Höhen sagt nichts über die Gruppenzahl.

Ein Dendrogramm zu Daten ohne jede Gruppenstruktur, woran erkennt man das?

Kein Sprung ragt aus den übrigen heraus
Richtig. Ein Baum entsteht immer; entscheidend ist das Verhältnis benachbarter Verschmelzungshöhen. Ohne Struktur liegt der grösste Faktor nahe bei den übrigen.
Die Verschmelzungshöhen sind klein
Sie können sogar grösser sein als bei strukturierten Daten. Die absolute Höhe sagt nichts.
Es entstehen keine Gruppen
Ein Schnitt ist immer möglich, und genau das ist die Falle.

Single-Linkage liefert bei k = 2 Gruppen der Grössen 171 und 1. Was ist passiert?

Eine Kette kurzer Abstände hat alles verbunden
Richtig. Single betrachtet den kleinsten Abstand zwischen zwei Gruppen; wenige Zwischenpunkte genügen, um zwei getrennte Wolken zu verschmelzen. Übrig bleibt als zweite Gruppe nur der entlegenste Einzelpunkt.
Es liegen 171 Ausreisser vor
Die grosse Gruppe ist die Regel, nicht die Ausnahme.
Die Daten waren nicht standardisiert
Der Effekt tritt auch bei standardisierten Daten auf; er liegt an der Verknüpfungsmethode.

Warum lassen sich die Verschmelzungshöhen eines Ward-Baums nicht mit denen eines Single-Baums vergleichen?

Weil bei Ward nicht Distanzen, sondern Streuungszuwächse aufgetragen sind
Richtig. Die Höhen messen bei den beiden Verfahren verschiedene Grössen. Aussagekräftig ist nur das Verhältnis der Höhen innerhalb eines Baums.
Weil Ward eine andere Distanzmatrix verwendet
Die Distanzmatrix ist dieselbe; nur die Verknüpfungsregel unterscheidet sich.
Weil Single keine Höhen liefert
Es liefert Höhen, nur andere.

Wozu dient die kophenetische Korrelation?

Sie misst, wie gut der Baum die tatsächlichen Abstände wiedergibt
Richtig. Damit eignet sie sich zum Vergleich von Verknüpfungsmethoden auf denselben Daten. Zur Wahl der Gruppenzahl taugt sie nicht, denn sie hängt nicht vom Schnitt ab.
Sie sagt, wie viele Gruppen zu wählen sind
Sie wird vor jedem Schnitt berechnet und ändert sich durch ihn nicht.
Sie prüft, ob überhaupt Gruppen vorliegen
Auch ein Baum ohne Struktur kann die Abstände treu wiedergeben.

Verlinkte Ressourcen