# Demo 5: Standardization impact demonstration
def demo5_standardization(scale_factor=100, apply_standardization=False, n_samples=300):
"""
Show the impact of standardization on PCA.
"""
# Generate data with two features on different scales
np.random.seed(42)
feature1 = np.random.randn(n_samples) * 1.0 # Small variance
feature2 = np.random.randn(n_samples) * scale_factor # Large variance
# Add some correlation
feature2 = feature2 + feature1 * (scale_factor * 0.3)
X = np.column_stack([feature1, feature2])
# Create figure with two panels
fig = plt.figure(figsize=(14, 10))
# Panel 1: Without standardization
ax1 = plt.subplot(2, 2, 1)
pca_raw = PCA(n_components=2)
X_pca_raw = pca_raw.fit_transform(X)
ax1.scatter(X[:, 0], X[:, 1], alpha=0.4, s=30, color='steelblue')
# Draw PCs
for i, (comp, var) in enumerate(zip(pca_raw.components_, pca_raw.explained_variance_)):
color = 'red' if i == 0 else 'blue'
scale = np.sqrt(var) * 0.3
ax1.arrow(X[:, 0].mean(), X[:, 1].mean(),
comp[0]*scale, comp[1]*scale,
head_width=scale*0.1, head_length=scale*0.15,
fc=color, ec=color, linewidth=2, alpha=0.7,
label=f'PC{i+1}')
ax1.set_xlabel('Feature 1 (scale=1)', fontsize=11)
ax1.set_ylabel(f'Feature 2 (scale={scale_factor})', fontsize=11)
ax1.set_title('Raw Data (No Standardization)', fontsize=12, fontweight='bold')
ax1.legend()
ax1.grid(True, alpha=0.3)
# Variance plot for raw
ax2 = plt.subplot(2, 2, 2)
ax2.bar([1, 2], pca_raw.explained_variance_ratio_, color=['red', 'blue'], alpha=0.7, edgecolor='black')
ax2.set_xlabel('Component', fontsize=11)
ax2.set_ylabel('Explained Variance Ratio', fontsize=11)
ax2.set_title('Variance Without Standardization', fontsize=12, fontweight='bold')
ax2.set_xticks([1, 2])
ax2.set_xticklabels(['PC1', 'PC2'])
ax2.grid(True, alpha=0.3, axis='y')
ax2.set_ylim(0, 1.1)
# Add percentage labels
for i, v in enumerate(pca_raw.explained_variance_ratio_):
ax2.text(i+1, v + 0.02, f'{v:.1%}', ha='center', fontsize=10, fontweight='bold')
# Panel 2: With standardization
ax3 = plt.subplot(2, 2, 3)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
pca_scaled = PCA(n_components=2)
X_pca_scaled = pca_scaled.fit_transform(X_scaled)
ax3.scatter(X_scaled[:, 0], X_scaled[:, 1], alpha=0.4, s=30, color='steelblue')
# Draw PCs
for i, (comp, var) in enumerate(zip(pca_scaled.components_, pca_scaled.explained_variance_)):
color = 'red' if i == 0 else 'blue'
scale = np.sqrt(var) * 1.5
ax3.arrow(0, 0, comp[0]*scale, comp[1]*scale,
head_width=0.15, head_length=0.2,
fc=color, ec=color, linewidth=2, alpha=0.7,
label=f'PC{i+1}')
ax3.set_xlabel('Feature 1 (standardized)', fontsize=11)
ax3.set_ylabel('Feature 2 (standardized)', fontsize=11)
ax3.set_title('Standardized Data', fontsize=12, fontweight='bold')
ax3.legend()
ax3.grid(True, alpha=0.3)
ax3.set_aspect('equal')
# Variance plot for standardized
ax4 = plt.subplot(2, 2, 4)
ax4.bar([1, 2], pca_scaled.explained_variance_ratio_, color=['red', 'blue'], alpha=0.7, edgecolor='black')
ax4.set_xlabel('Component', fontsize=11)
ax4.set_ylabel('Explained Variance Ratio', fontsize=11)
ax4.set_title('Variance With Standardization', fontsize=12, fontweight='bold')
ax4.set_xticks([1, 2])
ax4.set_xticklabels(['PC1', 'PC2'])
ax4.grid(True, alpha=0.3, axis='y')
ax4.set_ylim(0, 1.1)
# Add percentage labels
for i, v in enumerate(pca_scaled.explained_variance_ratio_):
ax4.text(i+1, v + 0.02, f'{v:.1%}', ha='center', fontsize=10, fontweight='bold')
# Add warning if not standardized and scale is high
if not apply_standardization and scale_factor > 50:
fig.text(0.5, 0.95, '⚠️ WARNING: PC1 dominated by large-scale feature!',
ha='center', fontsize=13, fontweight='bold', color='red',
bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.8))
# Add info box
info_text = "Comparison:\n"
info_text += f"Raw PCA - PC1: {pca_raw.explained_variance_ratio_[0]:.1%}, PC2: {pca_raw.explained_variance_ratio_[1]:.1%}\n"
info_text += f"Standardized PCA - PC1: {pca_scaled.explained_variance_ratio_[0]:.1%}, PC2: {pca_scaled.explained_variance_ratio_[1]:.1%}"
fig.text(0.5, 0.02, info_text, ha='center', fontsize=10,
bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.8))
plt.tight_layout()
plt.subplots_adjust(top=0.92, bottom=0.12)
plt.show()
# Print recommendation
if apply_standardization:
print("✅ Using standardization - both features get fair weight!")
else:
if scale_factor > 50:
print("❌ Without standardization, PC1 is biased toward the large-scale feature.")
print(" Try enabling standardization to see the difference!")
else:
print("ℹ️ Features have similar scales, standardization has less impact.")
# Create interactive widgets
interact(demo5_standardization,
scale_factor=IntSlider(value=100, min=1, max=1000, step=50, description='Scale Factor:', continuous_update=False),
apply_standardization=Checkbox(value=False, description='Apply standardization'),
n_samples=IntSlider(value=300, min=100, max=500, step=50, description='N samples:', continuous_update=False));