Analyse einer Punktewolke

Gegeben ist eine Punktewolke, die offensichtlich 6 Gruppen enthält, die sich jeweils um ein inneres Zentrum gruppiert haben. Wie kann man jetzt herausfinden, wo die einzelnen Gruppen ihr Zentrum haben, wie viele Punkte sich in der Gruppe befinden und welchen Durchmesser ein Kreis um die Gruppe haben muss, wenn mindestens 85 % alle Gruppenpunkte umschlossen sein sollen.

Ein Image mit vielen roter Punkte

Punktewolke.csv Wichtig! Der Delimiter ist hier jedoch kein Komma, sondern ein Simikolon ";"

Zunächst habe ich eine Funktion: find_cluster_circle(cluster_data,accuracy) erstellt und als GruppenAuswahlFunktion.py gespeichert.

from typing import Tuple
import pandas as pd
from scipy.spatial.distance import euclidean
import numpy as np


def find_cluster_circle(cluster_data: pd.DataFrame, accuracy: float = 0.85) -> Tuple[pd.Series, float, int]:
    """
    Findet den Kreis, der eine gegebene Genauigkeit der Punkte in einem Cluster umfasst.

    Parameter:
    - cluster_data: DataFrame mit den Datenpunkten im Cluster. Sollte zwei Spalten für x und y haben.
    - accuracy: Der Anteil der Punkte, die im Kreis enthalten sein sollen (zwischen 0 und 1).

    Rückgabewert:
    - Ein Tupel mit drei Elementen:
        1. Das Zentrum des Kreises als Pandas Series.
        2. Der Radius des Kreises als Float.
        3. Die Anzahl der Punkte im Kreis als Integer.
    """
    # Berechnung des Zentrums des Clusters
    cluster_center = cluster_data.mean()

    # Berechnung der euklidischen Entfernungen zum Zentrum
    distances = cluster_data.apply(lambda row: euclidean(row, cluster_center), axis=1)

    # Finden des Perzentils der Entfernungen entsprechend der gewünschten Genauigkeit
    radius = np.percentile(distances, accuracy * 100)

    # Anzahl der Punkte im Kreis
    points_in_circle = sum(distances <= radius)

    return cluster_center, radius, points_in_circle

Im Anschluss habe ich das Hauptprogramm erstellt, hier wird die Punktewolke mit der Hilfe von sklearn.Cluster analysiert und die einzelnen Cluster werden ermittelt. Einfache kann man es sich nicht machen!


import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
from sklearn.cluster import KMeans
from GruppenAuswahlFunktion import find_cluster_circle
import matplotlib.patches as patches

accuracy = 0.85

# Daten laden
data = pd.read_csv('Punktewolke.csv', delimiter=';')

# Datentypen auf int setzen
data = data.astype(int)

kmeans = KMeans(n_clusters=6, n_init=10)
data['cluster_kmeans'] = kmeans.fit_predict(data)

# DBSCAN
dbscan = DBSCAN(eps=5, min_samples=15)
data['cluster_dbscan'] = dbscan.fit_predict(data)

# Code-Vorbereitung für die Ausführung in der lokalen Umgebung

# Iterieren Sie durch die eindeutigen Cluster-IDs und sammeln Sie die Informationen für jeden Cluster.
cluster_info = {}
unique_clusters = data['cluster_kmeans'].unique()

for cluster_id in unique_clusters:
    cluster_data = data[data['cluster_kmeans'] == cluster_id]
    cluster_center, radius, points_in_circle = find_cluster_circle(cluster_data, accuracy)
    cluster_info[cluster_id] = {'center': cluster_center, 'radius': radius, 'points': points_in_circle}

# Erstellen Sie einen Plot mit allen Punkten und Kreisen
plt.figure(figsize=(10, 10))

# Zeichnen Sie die Punkte für jedes Cluster in einer eigenen Farbe
for cluster_id in unique_clusters:
    cluster_data = data[data['cluster_kmeans'] == cluster_id]
    plt.scatter(cluster_data.iloc[:, 0], cluster_data.iloc[:, 1], label=f'Cluster {cluster_id}')

# Zeichnen Sie die Kreise und Zentren

for cluster_id, info in cluster_info.items():
    center = info['center']
    radius = info['radius']
    points_in_circle = info['points']
    plt.scatter(center.iloc[0], center.iloc[1], color='red', zorder=5)  # Zentrum
    circle = patches.Circle((center.iloc[0], center.iloc[1]), radius, fill=False, linestyle='dashed')  # Kreis
    plt.gca().add_patch(circle)
    # Hinzufügen von Cluster_ID und der Anzahl der Punkte im Kreis als Kommentar über dem Kreis
    plt.annotate(f"Cluster_ID: {cluster_id}\n{points_in_circle} Punkte\n{int(radius) * 2} Durchmesser",
                 (center.iloc[0], center.iloc[1]),
                 textcoords="offset points",
                 xytext=(0, radius),
                 ha='center',
                 va='bottom')

plt.xlabel('X')
plt.ylabel('Y')
plt.title(f'Clusteranalyse mit K-Means und {accuracy * 100}% Kreisen')
plt.legend()
plt.axis('equal')
plt.show()

# Erstellen einer Zusammenfassungstabelle
summary_table = pd.DataFrame(
    columns=['Cluster_ID', 'Anzahl_Punkte_im_Kreis', 'Mittelpunkt_x', 'Mittelpunkt_y', 'Radius'])

for cluster_id, info in cluster_info.items():
    center = info['center']
    radius = info['radius']
    points_in_circle = info['points']

    new_row = {
        'Cluster_ID': cluster_id,
        'Anzahl_Punkte_im_Kreis': points_in_circle,
        'Mittelpunkt_x': center.iloc[0],
        'Mittelpunkt_y': center.iloc[1],
        'Radius': radius
    }

    summary_table = summary_table.append(new_row, ignore_index=True)

# Sortieren der Tabelle nach 'Cluster_ID'

summary_table = summary_table.sort_values(by='Cluster_ID')

print(summary_table)

    

So sieht das Ergebins aus:


Cluster_ID Anzahl_Punkte_im_Kreis Mittelpunkt_x Mittelpunkt_y Radius
0.0 38.0 52.33 235.48 77.01
1.0 23.0 497.44 379.77 44.35
2.0 19.0 234.34 109.13 39.84
3.0 18.0 519.19 125.71 42.80
4.0 18.0 271.00 394.00 44.69
5.0 12.0 376.50 203.85 35.49