Data Science 1 (Hierarchisches Clustering)#
Im zweiten Teil dieses Tutoriums beschäftigen wir uns nun mit hierarchischem Clustering.
Hinweis: Aufgaben, die durch einen Asterisk (*) markiert sind, sind Bonusaufgaben. Diese Aufgaben können im Tutorium behandelt werden, dies ist jedoch von den Übungsleitern nicht geplant.
import time
import warnings
from itertools import cycle, islice
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn
from scipy.cluster.hierarchy import dendrogram, linkage
from scipy.spatial import distance_matrix
from sklearn import cluster, mixture
from sklearn.datasets import load_iris
from sklearn.metrics import adjusted_rand_score
# Schriftfarbe für dark/light Mode Nutzer
# Bei Bedarf auf einen passenden Wert setzen
COLOR = "silver"
Aufgabe 3: Theorie#
Ist hierarchisches Clustering ein distanzbasiertes Clusteringverfahren?
Aufgabe 4: Dendrogramme zuordnen#
Hinweis: Es gibt verschiedene Varianten, bei welchem Wert Cluster-Verbindungen in ein Dendrogramm eingetragen werden. In der Vorlesung wurden Beispiele gezeigt, in denen Cluster-Verbindungen bei der Hälfte der Distanz eingetragen wurden (z.B., \(8.5\) für Distanz \(17\) auf Slide 8 von Vorlesung 9). Oben und im Folgenden verwenden wir die heute gängige Definition, bei der Cluster-Verbindungen bei der tatsächlichen Distanz eingetragen werden (im Beispiel von Slide 8 wäre das \(17\) für die Distanz \(17\)).
Aufgabe 5: Natürliche Zahlen Clustern#
Wir nutzen hierarchische Clusteranalyse mit euklidischer Distanzfunktion, um die Menge der natürlichen Zahlen von 1 bis 512 (inklusive 1 und 512) zu clustern.
Falls mehrere Cluster dieselbe Distanz aufweisen, werden die zwei Cluster zusammengeführt, die die kleinste Zahl beinhalten. Wenn zum Beispiel Cluster \(A\) und \(B\) dieselbe Distanz zueinander haben wie Cluster \(C\) und \(D\), führen wir \(A\) und \(B\) zusammen, falls \(\operatorname{min}(A \cup B) < \operatorname{min}(C \cup D)\). Falls \(\operatorname{min}(A \cup B) = \operatorname{min}(C \cup D)\) entscheidet die nächstgrößere Zahl.
Wir interessieren uns dafür, wie groß die beiden Cluster sind, die wir als letztes zusammenführen (also die Cluster, die am Ursprung des Dendrogramms anliegen).
Fragen
Wie groß sind die letzten beiden Cluster für single linkage Clustering?
Wie groß sind die letzten beiden Cluster für complete linkage Clustering?
Aufgabe 6: Hierarchisches Clustering üben#
In dieser Aufgabe sollen Sie ein hierarchisches Clustering per Handsimulation auf zufällig generierten Datenpunkten durchführen. Dabei sollen Sie sowohl Single-Linkage als auch Complete-Linkage verwenden. Berechnen Sie dafür die Euklidische Distanz und runden Sie auf eine Nachkommastelle. Benutzen Sie den vorgegebenen Code, um die Datenpunkte für die Aufgabe zu erzeugen. Vergleichen Sie Ihre eigene Lösung mit der vorgegebenen Distanz-Matrix und dem Dendrogramm.
Achtung! Bei jeder Ausführung wird eine neue zufällige Kombination von Datenpunkten erzeugt. Diese Aufgabe eignet sich daher besonders zum selbstständigen Üben und zur Vorbereitung auf den Test.
Zuerst werden die Datenpunkte für die Durchführung der Handsimulation erzeugt und visualisiert.
# Zufällige Kombination von Datenpunkten erzeugen
coords = (np.random.rand(5, 2) * 10).round(1)
# x- und y-Werte zuteilen
x = coords[:, 0]
y = coords[:, 1]
# Liste von Datenpunkte ausgeben
print(coords)
[[7.4 2.7]
[8.9 0.2]
[9.2 5.2]
[5.9 8.5]
[0.3 1.1]]
# Datenpunkte visualisieren
plt.scatter(x, y)
plt.show()

Aufgabe 6.1#
Erstellen Sie jetzt selbstständig die Distanz-Matrix für die vorgegebenen Datenpunkte, wobei die Abstände auf eine Nachkommastelle gerundet werden. Führen Sie den folgenden Code aus, um Ihre Lösung zu prüfen.
# Distanz-Matrix erstellen
print(distance_matrix(coords, coords).round(1))
[[0. 2.9 3.1 6. 7.3]
[2.9 0. 5. 8.8 8.6]
[3.1 5. 0. 4.7 9.8]
[6. 8.8 4.7 0. 9.3]
[7.3 8.6 9.8 9.3 0. ]]
Aufgabe 6.2#
Führen Sie zuerst hierarchisches Clustering mit Single-Linkage und dann mit Complete-Linkage durch. Benutzen Sie den folgenden Code, um die Dendrogramme zu erstellen und Ihre Lösungen zu prüfen.
# Dendrogram für Single-Linkage Clustering
plt.figure(figsize=(5, 3))
dendrogram(linkage(coords, "single"))
plt.show()

# Dendrogram für Complete-Linkage Clustering
plt.figure(figsize=(5, 3))
dendrogram(linkage(coords, "complete"))
plt.show()

Beispiel-Datenset: IRIS#
Der IRIS-Datensatz beinhaltet 150 Datenpunkte von 4 Attributen von Schwertlilienpflanzen. Gemessen wurden jeweils Kelchblattlänge, Kelchblattbreite, Blütenblattlänge und Blütenblattbreite. Wir werden im Folgenden versuchen, die beobachteten Pflanzen zu clustern. Die Daten beinhalten zusätzlich auch die tatsächliche Art der Schwertlilien (Setosa, Versicolour oder Virginica). Somit können wir unser Clustering am Ende mit der “ground truth” vergleichen. Der Datensatz besteht also aus einer 150x4 Datenmatrix und einem 150x1 Labelvektor.
Iris Dataset importieren#
# Datensatz laden
iris = load_iris()
# Das Objekt enthält Beobachtungen und Labels
iris.data[:10] # wir geben hier nur die ersten 10 Beobachtungen aus
array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[4.6, 3.1, 1.5, 0.2],
[5. , 3.6, 1.4, 0.2],
[5.4, 3.9, 1.7, 0.4],
[4.6, 3.4, 1.4, 0.3],
[5. , 3.4, 1.5, 0.2],
[4.4, 2.9, 1.4, 0.2],
[4.9, 3.1, 1.5, 0.1]])
# Sowie die möglichen Label
list(iris.target_names)
[np.str_('setosa'), np.str_('versicolor'), np.str_('virginica')]
Einführung in Hierarchisches Clustering mit scikit-learn#
In dieser Aufgabe werden Sie die Hierarchical Clustering Implementierung von scikit-learn nutzen, um das IRIS-Datenset mit verschiedenen Linking-Methoden zu clustern.
Im Folgenden geben wir Ihnen ein Beispiel, wie die scikit-learn Implementierung angewandt wird. Ihre Aufgabe ist es, verschiedene Linking-Methoden anzuwenden und die sich ergebenden Clusterings zu vergleichen.
# Führt Hierarchisches Clustering durch und zeichnet das Dendrogramm
linkage_matrix = linkage(iris.data, "ward")
plt.figure(figsize=(20, 5))
dendrogram(linkage_matrix)
plt.show()

# Führt hierarchisches Clustering bis zu einer gegebenen Zahl von Clustern durch
# und gibt Cluster-Assignments für jeden Datenpunkt zurück
assignments = cluster.AgglomerativeClustering(n_clusters=3, linkage="ward").fit_predict(iris.data)
assignments
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, 2, 0, 2, 2, 2,
2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 2, 0, 2, 0, 2, 2, 0, 0, 2, 2, 2, 2,
2, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 0])
Aufgabe 7: Dendrogramme interpretieren#
Interpretieren Sie das aus der Einführung entstandene Dendrogramm.
Was stellt die x-Achse dar?
Was stellt die y-Achse dar?
Aufgabe 8: Vergleich verschiedener Linkage-Methoden*#
Der unten stehende Code generiert zuerst hierarchische Clusterings mit unterschiedlichen Linkage-Methoden. Zusätzlich zu den in der Vorlesung vorgestellten Methoden wird hier auch Ward und average Linkage berechnet. Da wir wissen, dass es drei Klassen der Pflanze gibt, lassen wir scikit-learn mittels der diversen Linkage-Methoden drei Cluster bilden.
Vergleichen Sie verschiedene Verknüpfungskriterien (“linkage methods”) für den IRIS-Datensatz, d.h. neben “single” und “complete” auch “ward” und “average”, indem Sie verschiedene Feature-Kombinationen plotten und die Punkte entsprechend ihres Clusters einfärben.
Was sind die konzeptionellen Unterschiede zwischen den Methoden?
Welches der Kriterien kommt Ihrer Meinung nach am nähsten an die “ground truth” Cluster-Assignments heran?
Hinweis: Vergleichen Sie hierzu auch die Cluster-Assignments des hierarchischen Clusterings (assignments_<type>
) mit den tatsächlichen Clustern (iris.target
). Speichern Sie die Cluster-Assignments der verschiedenen Methoden (z.B., als assignments_ward
, assignments_complete
, …).
Hinweis: Das Ward-Linkage minimiert die Varianz innerhalb eines Clusters.
assignments_ward = cluster.AgglomerativeClustering(n_clusters=3, linkage="ward").fit_predict(
iris.data
)
assignments_complete = cluster.AgglomerativeClustering(
n_clusters=3, linkage="complete"
).fit_predict(iris.data)
assignments_average = cluster.AgglomerativeClustering(n_clusters=3, linkage="average").fit_predict(
iris.data
)
assignments_single = cluster.AgglomerativeClustering(n_clusters=3, linkage="single").fit_predict(
iris.data
)
fig, axes = plt.subplots(5, 6, figsize=(20, 10))
plt.subplots_adjust(left=0.06, right=0.98, bottom=0.09, top=0.98, wspace=0.02, hspace=0.02)
for i, (assignments, name) in enumerate(
[
(assignments_ward, "Ward"),
(assignments_complete, "Complete"),
(assignments_average, "Average"),
(assignments_single, "Single"),
(iris.target, "Ground Truth"),
]
):
for j, features in enumerate([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]):
axes[i][j].scatter(*np.split(iris.data[:, (features)], 2), c=assignments)
if j == 0:
axes[i][j].set_ylabel(name, size="large", color=COLOR)
if i == 4:
axes[i][j].set_xlabel(
f"({iris.feature_names[features[0]][:-5]}, {iris.feature_names[features[1]][:-5]})",
size="large",
color=COLOR,
)
axes[i][j].set_xticks([])
axes[i][j].set_yticks([])
fig.supxlabel("Dataset Features", size="x-large", color=COLOR)
fig.supylabel("Linkage Method", size="x-large", color=COLOR)
plt.show()

Aufgabe 9: Systematischer Vergleich der Assignments#
Cluster-Assignments manuell zu vergleichen ist mühsam, besonders bei großen Datensätzen. Wir werden nun die Metrik adjusted_rand_score
nutzen, um die Assignments jeder Methode zu beurteilen. Die Metrik gibt einen Cluster-Validierungsindex zurück, der zwischen -1 und 1 liegt, wobei 1 bedeutet, dass zwei Clusterings identisch sind (unabhängig davon, welches Label jedem Cluster zugeordnet ist).
Unten werden die Scores beispielhaft berechnet und dann für unseren konkreten Datensatz. Was kann bzgl. der Qualität unserer Linkage-Methoden gesagt werden?
# Beispiel:
ground_truth = [0, 0, 1, 1, 2, 2]
assignments_good_clustering = [1, 1, 2, 2, 0, 0]
assignments_bad_clustering = [0, 1, 2, 0, 1, 2]
print(f"Guter Score: {adjusted_rand_score(ground_truth, assignments_good_clustering)}")
print(f"Schlechter Score: {adjusted_rand_score(ground_truth, assignments_bad_clustering)}")
Guter Score: 1.0
Schlechter Score: -0.25
pd.DataFrame.from_dict(
{
"ward": (adjusted_rand_score(iris.target, assignments_ward),),
"complete": (adjusted_rand_score(iris.target, assignments_complete),),
"average": (adjusted_rand_score(iris.target, assignments_average),),
"single": (adjusted_rand_score(iris.target, assignments_single),),
}
).style.hide(axis="index")
ward | complete | average | single |
---|---|---|---|
0.731199 | 0.642251 | 0.759199 | 0.563751 |
Aufgabe 10: Clustering-Algorithmen*#
Wir haben in diesem Modul zwei Cluster-Verfahren kennengelernt: k-Means und hierarchisches Clustering. Darüber hinaus gibt es viele weitere Clustering-Verfahren. Nachfolgend ist ein Vergleich der verschiedenen Clustering-Verfahren in scikit-learn auf verschiedenen Datensätzen implementiert. Weitere Informationen
Konstruktion der Datensätze#
np.random.seed(0)
n_samples = 1500
# Datasets 1-4
noisy_circles = sklearn.datasets.make_circles(n_samples=n_samples, factor=0.5, noise=0.05)
noisy_moons = sklearn.datasets.make_moons(n_samples=n_samples, noise=0.05)
blobs = sklearn.datasets.make_blobs(n_samples=n_samples, random_state=8)
no_structure = np.random.rand(n_samples, 2), None
# 5. Dataset: Anisotropisch verteilte Daten
random_state = 170
X, y = sklearn.datasets.make_blobs(n_samples=n_samples, random_state=random_state)
transformation = [[0.6, -0.6], [-0.4, 0.8]]
X_aniso = np.dot(X, transformation)
aniso = (X_aniso, y)
# 6. Dataset: 'Kleckse' mit unterschiedlichen Varianzen
varied = sklearn.datasets.make_blobs(
n_samples=n_samples, cluster_std=[1.0, 2.5, 0.5], random_state=random_state
)
default_base = {
"quantile": 0.3,
"eps": 0.3,
"damping": 0.9,
"preference": -200,
"n_neighbors": 10,
"n_clusters": 3,
"min_samples": 20,
"xi": 0.05,
"min_cluster_size": 0.1,
}
datasets = [
(
noisy_circles,
"Noisy Circles",
{
"damping": 0.77,
"preference": -240,
"quantile": 0.2,
"n_clusters": 2,
"min_samples": 20,
"xi": 0.25,
},
),
(
noisy_moons,
"Noisy Moons",
{"damping": 0.75, "preference": -220, "n_clusters": 2},
),
(blobs, "Blobs", {}),
(no_structure, "No Structure", {}),
(
aniso,
"Anisotropic",
{
"eps": 0.15,
"n_neighbors": 2,
"min_samples": 20,
"xi": 0.1,
"min_cluster_size": 0.2,
},
),
(
varied,
"Varied",
{
"eps": 0.18,
"n_neighbors": 2,
"min_samples": 5,
"xi": 0.035,
"min_cluster_size": 0.2,
},
),
]
Anwendung von verschiedenen Cluster-Verfahren#
Hinweis: Das Ausführen der unten stehenden Codezelle braucht etwas länger.
for i, (dataset, d_name, d_params) in enumerate(datasets):
# Aktualisierung der Parameter mit datensatzspezifischen Werten
params = default_base.copy()
params.update(d_params)
X, y = dataset
# Datensätze normalisieren, um die Parameterauswahl zu erleichtern
X = sklearn.preprocessing.StandardScaler().fit_transform(X)
# Bandbreite für 'mean shift' schätzen
bandwidth = cluster.estimate_bandwidth(X, quantile=params["quantile"])
# Konnektivitätsmatrix für strukturierte 'structured Ward'
connectivity = sklearn.neighbors.kneighbors_graph(
X, n_neighbors=params["n_neighbors"], include_self=False
)
# Konnektivität symmetrisch machen
connectivity = 0.5 * (connectivity + connectivity.T)
# Cluster-Objekte mit Hilfe der Datasets I. - VI. erstellen
k_means = cluster.MiniBatchKMeans(n_clusters=params["n_clusters"], init="random", n_init=1)
ward = cluster.AgglomerativeClustering(
n_clusters=params["n_clusters"], linkage="ward", connectivity=connectivity
)
affinity_propagation = cluster.AffinityPropagation(
damping=params["damping"], preference=params["preference"]
)
ms = cluster.MeanShift(bandwidth=bandwidth, bin_seeding=True)
spectral = cluster.SpectralClustering(
n_clusters=params["n_clusters"],
eigen_solver="arpack",
affinity="nearest_neighbors",
)
average_linkage = cluster.AgglomerativeClustering(
linkage="average",
metric="manhattan",
n_clusters=params["n_clusters"],
connectivity=connectivity,
)
dbscan = cluster.DBSCAN(eps=params["eps"])
optics = cluster.OPTICS(
min_samples=params["min_samples"],
xi=params["xi"],
min_cluster_size=params["min_cluster_size"],
)
birch = cluster.Birch(n_clusters=params["n_clusters"])
gmm = mixture.GaussianMixture(n_components=params["n_clusters"], covariance_type="full")
# Speichern der Algorithmus-Parameter/Cluster-Objekte
clustering_algorithms = (
("MiniBatch\nKMeans", k_means),
("Affinity\nPropagation", affinity_propagation),
("Ward", ward),
("MeanShift", ms),
("Spectral\nClustering", spectral),
("Agglomerative\nClustering", average_linkage),
("DBSCAN", dbscan),
("OPTICS", optics),
("BIRCH", birch),
("Gaussian\nMixture", gmm),
)
# Visualisierung der Ergebnisse
plt.figure(figsize=(21, 12.5))
plt.subplots_adjust(left=0.02, right=0.98, bottom=0.001, top=0.96, wspace=0.05, hspace=0.01)
for j, (a_name, algorithm) in enumerate(clustering_algorithms):
t0 = time.time()
# Warnungen bei langen Laufzeiten
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
message="the number of connected components of the connectivity"
+ " matrix is [0-9]{1,2} > 1. Completing it to avoid stopping the tree early.",
category=UserWarning,
)
warnings.filterwarnings(
"ignore",
message="Graph is not fully connected, spectral embedding"
+ " may not work as expected.",
category=UserWarning,
)
warnings.filterwarnings(
"ignore",
message="Affinity propagation did not converge, this model may"
+ " return degenerate cluster centers and labels.",
category=sklearn.exceptions.ConvergenceWarning,
)
algorithm.fit(X)
t1 = time.time()
if hasattr(algorithm, "labels_"):
y_pred = algorithm.labels_.astype(int)
else:
y_pred = algorithm.predict(X)
plt.subplot(len(datasets), len(clustering_algorithms), j + 1)
if i == 0:
plt.title(a_name, size=18, color=COLOR)
if j == 0:
plt.ylabel(d_name, size=18, color=COLOR)
# Farbpalette
colors = np.array(
list(
islice(
cycle(
[
"#377eb8",
"#ff7f00",
"#4daf4a",
"#f781bf",
"#a65628",
"#984ea3",
"#999999",
"#e41a1c",
"#dede00",
]
),
int(max(y_pred) + 1),
)
)
)
# Schwarze Farbe für Ausreißer hinzufügen (falls vorhanden)
colors = np.append(colors, ["#000000"])
plt.scatter(X[:, 0], X[:, 1], s=10, color=colors[y_pred])
plt.xlim(-2.5, 2.5)
plt.ylim(-2.5, 2.5)
plt.xticks(())
plt.yticks(())
plt.text(
0.99,
0.01,
("%.2fs" % (t1 - t0)).lstrip("0"),
transform=plt.gca().transAxes,
size=15,
horizontalalignment="right",
)
plt.show()
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
Cell In[16], line 130
127 # Schwarze Farbe für Ausreißer hinzufügen (falls vorhanden)
128 colors = np.append(colors, ["#000000"])
--> 130 plt.scatter(X[:, 0], X[:, 1], s=10, color=colors[y_pred])
132 plt.xlim(-2.5, 2.5)
133 plt.ylim(-2.5, 2.5)
File /usr/local/lib/python3.13/site-packages/matplotlib/_api/deprecation.py:453, in make_keyword_only.<locals>.wrapper(*args, **kwargs)
447 if len(args) > name_idx:
448 warn_deprecated(
449 since, message="Passing the %(name)s %(obj_type)s "
450 "positionally is deprecated since Matplotlib %(since)s; the "
451 "parameter will become keyword-only in %(removal)s.",
452 name=name, obj_type=f"parameter of {func.__name__}()")
--> 453 return func(*args, **kwargs)
File /usr/local/lib/python3.13/site-packages/matplotlib/pyplot.py:3948, in scatter(x, y, s, c, marker, cmap, norm, vmin, vmax, alpha, linewidths, edgecolors, colorizer, plotnonfinite, data, **kwargs)
3928 @_copy_docstring_and_deprecators(Axes.scatter)
3929 def scatter(
3930 x: float | ArrayLike,
(...) 3946 **kwargs,
3947 ) -> PathCollection:
-> 3948 __ret = gca().scatter(
3949 x,
3950 y,
3951 s=s,
3952 c=c,
3953 marker=marker,
3954 cmap=cmap,
3955 norm=norm,
3956 vmin=vmin,
3957 vmax=vmax,
3958 alpha=alpha,
3959 linewidths=linewidths,
3960 edgecolors=edgecolors,
3961 colorizer=colorizer,
3962 plotnonfinite=plotnonfinite,
3963 **({"data": data} if data is not None else {}),
3964 **kwargs,
3965 )
3966 sci(__ret)
3967 return __ret
File /usr/local/lib/python3.13/site-packages/matplotlib/_api/deprecation.py:453, in make_keyword_only.<locals>.wrapper(*args, **kwargs)
447 if len(args) > name_idx:
448 warn_deprecated(
449 since, message="Passing the %(name)s %(obj_type)s "
450 "positionally is deprecated since Matplotlib %(since)s; the "
451 "parameter will become keyword-only in %(removal)s.",
452 name=name, obj_type=f"parameter of {func.__name__}()")
--> 453 return func(*args, **kwargs)
File /usr/local/lib/python3.13/site-packages/matplotlib/__init__.py:1521, in _preprocess_data.<locals>.inner(ax, data, *args, **kwargs)
1518 @functools.wraps(func)
1519 def inner(ax, *args, data=None, **kwargs):
1520 if data is None:
-> 1521 return func(
1522 ax,
1523 *map(cbook.sanitize_sequence, args),
1524 **{k: cbook.sanitize_sequence(v) for k, v in kwargs.items()})
1526 bound = new_sig.bind(ax, *args, **kwargs)
1527 auto_label = (bound.arguments.get(label_namer)
1528 or bound.kwargs.get(label_namer))
File /usr/local/lib/python3.13/site-packages/matplotlib/axes/_axes.py:4951, in Axes.scatter(self, x, y, s, c, marker, cmap, norm, vmin, vmax, alpha, linewidths, edgecolors, colorizer, plotnonfinite, **kwargs)
4948 if edgecolors is None:
4949 orig_edgecolor = kwargs.get('edgecolor', None)
4950 c, colors, edgecolors = \
-> 4951 self._parse_scatter_color_args(
4952 c, edgecolors, kwargs, x.size,
4953 get_next_color_func=self._get_patches_for_fill.get_next_color)
4955 if plotnonfinite and colors is None:
4956 c = np.ma.masked_invalid(c)
File /usr/local/lib/python3.13/site-packages/matplotlib/axes/_axes.py:4758, in Axes._parse_scatter_color_args(c, edgecolors, kwargs, xsize, get_next_color_func)
4756 if not c_is_mapped:
4757 try: # Is 'c' acceptable as PathCollection facecolors?
-> 4758 colors = mcolors.to_rgba_array(c)
4759 except (TypeError, ValueError) as err:
4760 if "RGBA values should be within 0-1 range" in str(err):
File /usr/local/lib/python3.13/site-packages/matplotlib/colors.py:515, in to_rgba_array(c, alpha)
513 rgba = np.array([to_rgba(cc) for cc in c])
514 else:
--> 515 rgba = np.array([to_rgba(cc) for cc in c])
517 if alpha is not None:
518 rgba[:, 3] = alpha
KeyboardInterrupt:
Aufgabe 10.1#
k-Means läuft (fast immer) schneller als hierarchisches Clustering (siehe Laufzeiten unten rechts in den Bildern). Warum?
Aufgabe 10.2#
Welche Limitierungen von k-Means können Sie gegenüber hierarchischem Clustering erkennen?
Aufgabe 10.3#
Welche Vor- und Nachteile erkennen Sie bei den anderen Clustering-Algorithmen?