def generate_dataset(dataset_type, random_state=42):
"""Generate different types of datasets"""
if dataset_type == "Well-separated spherical":
X, y = make_blobs(n_samples=300, centers=3, n_features=2,
cluster_std=0.6, random_state=random_state)
K_true = 3
elif dataset_type == "Non-convex (half-moons)":
X, y = make_moons(n_samples=300, noise=0.08, random_state=random_state)
K_true = 2
elif dataset_type == "Concentric circles":
X, y = make_circles(n_samples=300, noise=0.05, factor=0.5, random_state=random_state)
K_true = 2
elif dataset_type == "Different densities":
np.random.seed(random_state)
X1 = np.random.randn(200, 2) * 0.3 + np.array([0, 0])
X2 = np.random.randn(50, 2) * 0.3 + np.array([3, 3])
X3 = np.random.randn(300, 2) * 1.5 + np.array([5, -2])
X = np.vstack([X1, X2, X3])
y = np.array([0]*200 + [1]*50 + [2]*300)
K_true = 3
elif dataset_type == "Elongated clusters":
np.random.seed(random_state)
X1 = np.random.randn(150, 2) @ np.array([[3, 0], [0, 0.5]]) + np.array([0, 0])
X2 = np.random.randn(150, 2) @ np.array([[0.5, 0], [0, 3]]) + np.array([5, 5])
X = np.vstack([X1, X2])
y = np.array([0]*150 + [1]*150)
K_true = 2
return X, y, K_true
def plot_voronoi_kmeans(kmeans, X, ax):
"""Plot Voronoi diagram for K-means clustering"""
try:
from scipy.spatial import Voronoi
vor = Voronoi(kmeans.cluster_centers_)
# Plot Voronoi edges
for simplex in vor.ridge_vertices:
simplex = np.asarray(simplex)
if np.all(simplex >= 0):
ax.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1],
'k--', alpha=0.4, linewidth=1.5)
except:
pass # Skip if Voronoi fails
current_random_state = 42
def plot_failure_cases(dataset_type, K, show_voronoi):
"""Interactive failure cases visualization"""
global current_random_state
X, y_true, K_true = generate_dataset(dataset_type, current_random_state)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
# Left: True structure (if available)
ax1.scatter(X[:, 0], X[:, 1], c=y_true, cmap='tab10',
s=50, alpha=0.6, edgecolors='k', linewidth=0.5)
ax1.set_xlabel('Feature 1', fontsize=12, fontweight='bold')
ax1.set_ylabel('Feature 2', fontsize=12, fontweight='bold')
ax1.set_title(f'True Structure (K={K_true})', fontsize=14, fontweight='bold')
ax1.grid(True, alpha=0.3)
# Right: K-means result
kmeans = KMeans(n_clusters=K, random_state=42, n_init=10)
labels_kmeans = kmeans.fit_predict(X)
ax2.scatter(X[:, 0], X[:, 1], c=labels_kmeans, cmap='viridis',
s=50, alpha=0.6, edgecolors='k', linewidth=0.5)
ax2.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
marker='X', s=500, c='red', edgecolors='black', linewidth=3, zorder=5)
if show_voronoi:
plot_voronoi_kmeans(kmeans, X, ax2)
ax2.set_xlabel('Feature 1', fontsize=12, fontweight='bold')
ax2.set_ylabel('Feature 2', fontsize=12, fontweight='bold')
ax2.set_title(f'K-means Result (K={K})', fontsize=14, fontweight='bold')
ax2.grid(True, alpha=0.3)
# Add assessment
if dataset_type == "Well-separated spherical":
assessment = "✅ K-means works great here!"
color = 'lightgreen'
else:
assessment = "❌ K-means struggles with this structure"
color = 'lightcoral'
ax2.text(0.5, 0.02, assessment, transform=ax2.transAxes,
fontsize=12, fontweight='bold', ha='center',
bbox=dict(boxstyle='round', facecolor=color, alpha=0.8))
plt.tight_layout()
plt.show()
def regenerate_data(b):
global current_random_state
current_random_state = np.random.randint(0, 1000)
print(f"✓ Data regenerated with random_state={current_random_state}")
# Create widgets
dataset_dropdown = widgets.Dropdown(
options=["Well-separated spherical", "Non-convex (half-moons)",
"Concentric circles", "Different densities", "Elongated clusters"],
value="Well-separated spherical",
description='Dataset:',
style={'description_width': '100px'},
layout=widgets.Layout(width='500px')
)
K_slider_failures = widgets.IntSlider(
value=2, min=2, max=5, step=1,
description='K:',
continuous_update=False,
style={'description_width': '100px'},
layout=widgets.Layout(width='400px')
)
voronoi_checkbox = widgets.Checkbox(
value=False, description='Show Voronoi boundaries',
style={'description_width': 'initial'}
)
regenerate_button = widgets.Button(
description='Regenerate Data',
button_style='info',
icon='refresh'
)
regenerate_button.on_click(regenerate_data)
controls = widgets.VBox([
dataset_dropdown,
K_slider_failures,
voronoi_checkbox,
regenerate_button
])
interactive_failures = interactive(plot_failure_cases,
dataset_type=dataset_dropdown,
K=K_slider_failures,
show_voronoi=voronoi_checkbox)
display(controls)
display(interactive_failures.children[-1])