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:
- Inicjalizacja: Losowy wybór K centroidów.
- Przypisanie: Każdy punkt jest przypisywany do najbliższego centroidu.
- Aktualizacja: Centroidy są przeliczane jako średnie punktów w klastrze.
- Iteracja: Kroki 2-3 są powtarzane aż do zbieżności.
- 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
-
Eksperymentuj z K - przetestuj różne wartości K na swoich danych.
-
Porównaj metody inicjalizacji - sprawdź wpływ różnych metod na wyniki.
-
Feature engineering - dodaj nowe cechy i sprawdź wpływ na klastry.
-
Walidacja klastrów - użyj różnych metryk do oceny jakości.
-
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
- K-means Tutorial – scikit-learn
- K-means Guide – Towards Data Science
- K-means – StatQuest (YouTube)
- Clustering Tutorial – Machine Learning Mastery