PCA

Project many features down to two dimensions.

PCA rotates the data onto axes of decreasing variance. Two components make a scatter you can draw. It is a transformer: fit on train, transform train and test.

Goal

Project four features to two components and color the scatter by label.

Two components

from sklearn.datasets import make_classification
from sklearn.decomposition import PCA

X, y = make_classification(n_samples=200, n_features=6, n_informative=3, random_state=0)
pca = PCA(n_components=2, random_state=0)
Z = pca.fit_transform(X)
print("explained variance ratio", pca.explained_variance_ratio_.round(3))
print("Z shape", Z.shape)

The ratios sum to at most 1. They say how much of the original spread the two axes keep.

Scatter

from sklearn.datasets import make_classification
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

X, y = make_classification(n_samples=200, n_features=6, n_informative=3, random_state=0)
Xs = StandardScaler().fit_transform(X)
Z = PCA(n_components=2, random_state=0).fit_transform(Xs)
plt.scatter(Z[:, 0], Z[:, 1], c=y, alpha=0.75)
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.title("PCA")
plt.show()

Scale before PCA so a wide column does not become PC1 by default.

Inside a pipeline

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

X, y = make_classification(n_samples=200, n_features=8, n_informative=3, random_state=0)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=0
)
pipe = Pipeline(
    [
        ("scale", StandardScaler()),
        ("pca", PCA(n_components=3, random_state=0)),
        ("clf", LogisticRegression(max_iter=200)),
    ]
)
pipe.fit(X_train, y_train)
print("accuracy:", round(accuracy_score(y_test, pipe.predict(X_test)), 3))
Tip

n_components=0.9 (a float) keeps enough axes to cover 90% of variance. An int keeps that many axes.