Przejdź do głównej zawartości

kmeans_clustering

czym jest K-means clustering? ⭐

  • K-means to jeden z najpopularniejszych algorytmów uczenia nienadzorowanego.
  • Dzieli dane na K grup (klastrów) na podstawie podobieństwa.
  • Każdy klaster ma centroid (środek), a punkty są przypisywane do najbliższego centroidu.
  • Jest algorytmem iteracyjnym, który dąży do minimalizacji sumy kwadratów odległości.
  • Służy do segmentacji danych, redukcji wymiarów i odkrywania wzorców.

jak działa K-means? ⭐

Algorytm:

  1. Inicjalizacja: Losowy wybór K centroidów.
  2. Przypisanie: Każdy punkt jest przypisywany do najbliższego centroidu.
  3. Aktualizacja: Centroidy są przeliczane jako średnie punktów w klastrze.
  4. Iteracja: Kroki 2-3 są powtarzane aż do zbieżności.
  5. Zakończenie: Gdy centroidy przestają się zmieniać.

Kryterium zbieżności:

  • Suma kwadratów odległości (SSE) przestaje się zmniejszać.
  • Centroidy przestają się znacząco zmieniać.
  • Maksymalna liczba iteracji zostaje osiągnięta.

przykładowy kod (K-means clustering)

import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score, calinski_harabasz_score
import matplotlib.pyplot as plt
import seaborn as sns

# Generowanie danych
np.random.seed(42)
n_samples = 300

# Generowanie trzech klastrów
cluster1 = np.random.multivariate_normal([0, 0], [[1, 0], [0, 1]], n_samples//3)
cluster2 = np.random.multivariate_normal([4, 4], [[1, 0], [0, 1]], n_samples//3)
cluster3 = np.random.multivariate_normal([0, 4], [[1, 0], [0, 1]], n_samples//3)

# Łączenie danych
X = np.vstack([cluster1, cluster2, cluster3])

# Tworzenie DataFrame
data = pd.DataFrame(X, columns=['feature1', 'feature2'])

print("Rozmiar danych:", data.shape)
print("Przykładowe dane:")
print(data.head())

# Wizualizacja oryginalnych danych
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.scatter(data['feature1'], data['feature2'], alpha=0.6)
plt.title('Oryginalne dane')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.grid(True, alpha=0.3)

1. podstawowy K-means

# Skalowanie danych (ważne dla K-means)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(data)

# Trenowanie K-means
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
kmeans.fit(X_scaled)

# Przewidywanie klastrów
labels = kmeans.labels_
centroids = kmeans.cluster_centers_

print(f"Liczba iteracji: {kmeans.n_iter_}")
print(f"Inertia (SSE): {kmeans.inertia_:.2f}")

# Dodanie etykiet do danych
data['cluster'] = labels

print("\nRozkład klastrów:")
print(data['cluster'].value_counts().sort_index())

# Wizualizacja wyników
plt.subplot(1, 3, 2)
colors = ['red', 'blue', 'green']
for i in range(3):
cluster_data = data[data['cluster'] == i]
plt.scatter(cluster_data['feature1'], cluster_data['feature2'],
c=colors[i], alpha=0.6, label=f'Klaster {i}')

# Centroidy
centroids_unscaled = scaler.inverse_transform(centroids)
plt.scatter(centroids_unscaled[:, 0], centroids_unscaled[:, 1],
c='black', s=200, marker='x', label='Centroidy')

plt.title('K-means Clustering (K=3)')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.grid(True, alpha=0.3)

2. wybór optymalnej liczby klastrów

# Metody wyboru optymalnego K
def evaluate_kmeans(X, k_range):
"""Ocena K-means dla różnych wartości K"""
inertias = []
silhouette_scores = []
calinski_scores = []

for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
kmeans.fit(X)

inertias.append(kmeans.inertia_)
silhouette_scores.append(silhouette_score(X, kmeans.labels_))
calinski_scores.append(calinski_harabasz_score(X, kmeans.labels_))

return inertias, silhouette_scores, calinski_scores

# Testowanie różnych wartości K
k_range = range(2, 11)
inertias, silhouette_scores, calinski_scores = evaluate_kmeans(X_scaled, k_range)

# Wizualizacja wyników
plt.subplot(1, 3, 3)
plt.plot(k_range, inertias, 'bo-', label='Inertia')
plt.xlabel('Liczba klastrów (K)')
plt.ylabel('Inertia')
plt.title('Elbow Method')
plt.legend()
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# Szczegółowa analiza
plt.figure(figsize=(15, 4))

plt.subplot(1, 3, 1)
plt.plot(k_range, inertias, 'bo-')
plt.xlabel('Liczba klastrów (K)')
plt.ylabel('Inertia')
plt.title('Elbow Method')
plt.grid(True, alpha=0.3)

plt.subplot(1, 3, 2)
plt.plot(k_range, silhouette_scores, 'ro-')
plt.xlabel('Liczba klastrów (K)')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Analysis')
plt.grid(True, alpha=0.3)

plt.subplot(1, 3, 3)
plt.plot(k_range, calinski_scores, 'go-')
plt.xlabel('Liczba klastrów (K)')
plt.ylabel('Calinski-Harabasz Score')
plt.title('Calinski-Harabasz Analysis')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("Wyniki oceny:")
for k, inertia, sil, cal in zip(k_range, inertias, silhouette_scores, calinski_scores):
print(f"K={k}: Inertia={inertia:.2f}, Silhouette={sil:.3f}, Calinski={cal:.0f}")

3. analiza klastrów

# Wybór optymalnego K (na podstawie silhouette score)
optimal_k = k_range[np.argmax(silhouette_scores)]
print(f"\nOptymalna liczba klastrów (silhouette): K = {optimal_k}")

# Trenowanie z optymalnym K
optimal_kmeans = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)
optimal_kmeans.fit(X_scaled)

# Analiza klastrów
data['optimal_cluster'] = optimal_kmeans.labels_

# Statystyki klastrów
cluster_stats = data.groupby('optimal_cluster').agg({
'feature1': ['mean', 'std', 'count'],
'feature2': ['mean', 'std']
}).round(3)

print("\nStatystyki klastrów:")
print(cluster_stats)

# Wizualizacja optymalnych klastrów
plt.figure(figsize=(12, 4))

plt.subplot(1, 3, 1)
colors = plt.cm.Set1(np.linspace(0, 1, optimal_k))
for i in range(optimal_k):
cluster_data = data[data['optimal_cluster'] == i]
plt.scatter(cluster_data['feature1'], cluster_data['feature2'],
c=[colors[i]], alpha=0.6, label=f'Klaster {i}')

centroids_opt = optimal_kmeans.cluster_centers_
centroids_opt_unscaled = scaler.inverse_transform(centroids_opt)
plt.scatter(centroids_opt_unscaled[:, 0], centroids_opt_unscaled[:, 1],
c='black', s=200, marker='x', label='Centroidy')

plt.title(f'Optymalne K-means (K={optimal_k})')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.grid(True, alpha=0.3)

4. porównanie różnych inicjalizacji

# Różne metody inicjalizacji
init_methods = ['k-means++', 'random']
init_results = {}

for method in init_methods:
kmeans_init = KMeans(n_clusters=3, init=method, random_state=42, n_init=10)
kmeans_init.fit(X_scaled)

init_results[method] = {
'inertia': kmeans_init.inertia_,
'labels': kmeans_init.labels_,
'centroids': kmeans_init.cluster_centers_
}

print(f"{method}: Inertia = {kmeans_init.inertia_:.2f}")

# Wizualizacja porównania
for i, (method, result) in enumerate(init_results.items()):
plt.subplot(1, 3, i+2)

data[f'cluster_{method}'] = result['labels']

for j in range(3):
cluster_data = data[data[f'cluster_{method}'] == j]
plt.scatter(cluster_data['feature1'], cluster_data['feature2'],
c=colors[j], alpha=0.6, label=f'Klaster {j}')

centroids_method = scaler.inverse_transform(result['centroids'])
plt.scatter(centroids_method[:, 0], centroids_method[:, 1],
c='black', s=200, marker='x', label='Centroidy')

plt.title(f'K-means - {method}')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

5. K-means dla danych wielowymiarowych

# Generowanie danych wielowymiarowych
np.random.seed(42)
n_samples_multi = 500
n_features = 4

# Trzy klastry w 4D
cluster1_multi = np.random.multivariate_normal([0, 0, 0, 0], np.eye(4), n_samples_multi//3)
cluster2_multi = np.random.multivariate_normal([3, 3, 3, 3], np.eye(4), n_samples_multi//3)
cluster3_multi = np.random.multivariate_normal([0, 3, 0, 3], np.eye(4), n_samples_multi//3)

X_multi = np.vstack([cluster1_multi, cluster2_multi, cluster3_multi])
data_multi = pd.DataFrame(X_multi, columns=[f'feature_{i+1}' for i in range(n_features)])

# Skalowanie
scaler_multi = StandardScaler()
X_multi_scaled = scaler_multi.fit_transform(data_multi)

# K-means dla danych wielowymiarowych
kmeans_multi = KMeans(n_clusters=3, random_state=42, n_init=10)
kmeans_multi.fit(X_multi_scaled)

# Analiza wyników
data_multi['cluster'] = kmeans_multi.labels_

print("K-means dla danych wielowymiarowych:")
print(f"Liczba próbek: {data_multi.shape[0]}")
print(f"Liczba cech: {data_multi.shape[1]}")
print(f"Inertia: {kmeans_multi.inertia_:.2f}")
print(f"Silhouette score: {silhouette_score(X_multi_scaled, kmeans_multi.labels_):.3f}")

# Wizualizacja (pierwsze 2 wymiary)
plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
for i in range(3):
cluster_data = data_multi[data_multi['cluster'] == i]
plt.scatter(cluster_data['feature_1'], cluster_data['feature_2'],
c=colors[i], alpha=0.6, label=f'Klaster {i}')

centroids_multi = scaler_multi.inverse_transform(kmeans_multi.cluster_centers_)
plt.scatter(centroids_multi[:, 0], centroids_multi[:, 1],
c='black', s=200, marker='x', label='Centroidy')

plt.title('K-means - Pierwsze 2 wymiary')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.grid(True, alpha=0.3)

# PCA dla wizualizacji wielowymiarowych danych
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_multi_scaled)

plt.subplot(1, 2, 2)
for i in range(3):
cluster_mask = kmeans_multi.labels_ == i
plt.scatter(X_pca[cluster_mask, 0], X_pca[cluster_mask, 1],
c=colors[i], alpha=0.6, label=f'Klaster {i}')

plt.title('K-means - PCA projection')
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.legend()
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

6. praktyczne zastosowania

# Przykład: Segmentacja klientów
np.random.seed(42)
n_customers = 1000

# Symulacja danych klientów
age = np.random.normal(35, 10, n_customers)
income = np.random.normal(50000, 20000, n_customers)
spending = np.random.normal(2000, 800, n_customers)
visits = np.random.poisson(12, n_customers)

# Tworzenie klastrów klientów
customer_data = pd.DataFrame({
'age': age,
'income': income,
'spending': spending,
'visits': visits
})

# Skalowanie
scaler_customers = StandardScaler()
customer_data_scaled = scaler_customers.fit_transform(customer_data)

# K-means dla segmentacji klientów
kmeans_customers = KMeans(n_clusters=4, random_state=42, n_init=10)
kmeans_customers.fit(customer_data_scaled)

# Analiza segmentów
customer_data['segment'] = kmeans_customers.labels_

segment_analysis = customer_data.groupby('segment').agg({
'age': ['mean', 'std'],
'income': ['mean', 'std'],
'spending': ['mean', 'std'],
'visits': ['mean', 'std']
}).round(2)

print("Analiza segmentów klientów:")
print(segment_analysis)

# Nazwanie segmentów na podstawie charakterystyki
segment_names = {
0: 'Młodzi z niskim dochodem',
1: 'Średni wiek, wysoki dochód',
2: 'Starsi, umiarkowany dochód',
3: 'Wysoki dochód, duże wydatki'
}

customer_data['segment_name'] = customer_data['segment'].map(segment_names)

print("\nCharakterystyka segmentów:")
for segment in range(4):
segment_data = customer_data[customer_data['segment'] == segment]
print(f"\n{segment_names[segment]}:")
print(f" Liczba klientów: {len(segment_data)}")
print(f" Średni wiek: {segment_data['age'].mean():.1f}")
print(f" Średni dochód: {segment_data['income'].mean():.0f}")
print(f" Średnie wydatki: {segment_data['spending'].mean():.0f}")

praktyczne ćwiczenia

  1. Eksperymentuj z K - przetestuj różne wartości K na swoich danych.

  2. Porównaj metody inicjalizacji - sprawdź wpływ różnych metod na wyniki.

  3. Feature engineering - dodaj nowe cechy i sprawdź wpływ na klastry.

  4. Walidacja klastrów - użyj różnych metryk do oceny jakości.

  5. Real-world data - zastosuj K-means do rzeczywistych problemów.

dobre praktyki

  • Skalowanie: Zawsze skaluj dane przed K-means.
  • Wybór K: Używaj elbow method i silhouette analysis.
  • Inicjalizacja: Preferuj k-means++ nad random.
  • Interpretacja: Analizuj charakterystykę klastrów.

wady i zalety

Zalety:

  • Prosty i szybki algorytm
  • Skuteczny dla danych sferycznych
  • Łatwy do interpretacji
  • Skalowalny

Wady:

  • Wymaga określenia liczby klastrów
  • Wrażliwy na inicjalizację
  • Zakłada sferyczne klastry
  • Wrażliwy na outliers

polecane źródła