Use with scikit-learn Pipeline#
ktch transformers follow the scikit-learn API.
Basic usage with GPA#
GPA expects flattened input of shape (n_specimens, n_landmarks * n_dim):
import numpy as np
from sklearn.decomposition import PCA
from ktch.landmark import GeneralizedProcrustesAnalysis
# Minimal data: 5 specimens, 4 landmarks, 2D
landmarks_3d = np.array([
[[0, 0], [1, 0], [1, 1], [0, 1]],
[[0.1, 0], [1.1, 0], [1, 1.1], [0, 1]],
[[0, 0.1], [1, 0], [1.1, 1], [0, 1.1]],
[[0.05, 0.05], [1.05, 0], [1, 1.05], [0, 1]],
[[0, 0], [1.05, 0.05], [1, 1], [0.05, 1.05]],
], dtype=float)
# Flatten to (n_specimens, n_landmarks * n_dim)
n_specimens, n_landmarks, n_dim = landmarks_3d.shape
landmarks = landmarks_3d.reshape(n_specimens, n_landmarks * n_dim)
# GPA then PCA
gpa = GeneralizedProcrustesAnalysis()
shapes = gpa.fit_transform(landmarks)
pca = PCA(n_components=2)
pc_scores = pca.fit_transform(shapes)
print(f"PC scores shape: {pc_scores.shape}")
PC scores shape: (5, 2)
Basic usage with EFA#
EFA can be used in a sklearn Pipeline for unsupervised transformations:
from sklearn.pipeline import Pipeline
from ktch.harmonic import EllipticFourierAnalysis
# Minimal data: 3 elliptical outlines with variations
theta = np.linspace(0, 2 * np.pi, 64, endpoint=False)
outlines = np.array([
np.column_stack([1.0 * np.cos(theta), 0.8 * np.sin(theta)]),
np.column_stack([1.2 * np.cos(theta), 0.7 * np.sin(theta)]),
np.column_stack([0.9 * np.cos(theta), 1.0 * np.sin(theta)]),
])
# EFA + PCA pipeline (no y parameter)
pipeline = Pipeline([
('efa', EllipticFourierAnalysis(n_harmonics=10)),
('pca', PCA(n_components=2))
])
pc_scores = pipeline.fit_transform(outlines)
print(f"PC scores shape: {pc_scores.shape}")
PC scores shape: (3, 2)
Classification with EFA coefficients#
For supervised tasks, apply EFA separately before the classification pipeline:
from sklearn.svm import SVC
# More data for classification
np.random.seed(42)
outlines_more = []
labels = []
for i in range(20):
scale = 1.0 + 0.1 * np.random.randn()
outlines_more.append(np.column_stack([scale * np.cos(theta), np.sin(theta)]))
labels.append(0 if scale < 1.0 else 1)
outlines_more = np.array(outlines_more)
labels = np.array(labels)
# Apply EFA first (unsupervised)
efa = EllipticFourierAnalysis(n_harmonics=10)
coefficients = efa.fit_transform(outlines_more)
# Then use PCA + SVC pipeline on coefficients
pipeline = Pipeline([
('pca', PCA(n_components=3)),
('svc', SVC())
])
pipeline.fit(coefficients, labels)
print(f"Training accuracy: {pipeline.score(coefficients, labels):.2f}")
Training accuracy: 0.60
See also
Perform Cross-Validation for cross-validation examples
What is Morphometrics? for background