Common Mistakes and Best Practices in t-SNE
t-SNE is an exploratory embedding. Its persuasive pictures make interpretation discipline more important, not less.
1. Calling visible islands clusters
t-SNE is not a clustering algorithm. It returns coordinates, not membership labels. Distances between islands, relative positions, apparent density, and island size can all change without a corresponding change in the original data.
If clustering is the goal, fit and validate the clustering method in a scientifically defensible feature space. Coloring a t-SNE map by those independently obtained labels can aid inspection, but clustering the two-dimensional t-SNE coordinates is usually unsafe because the embedding deliberately distorts distances and density.
2. Treating perplexity as a universal default
Perplexity is a smooth effective neighborhood size determined through each point's bandwidth. Perplexity must be smaller than the number of samples. Values such as 5–50 are common starting points, not a theorem or universal recommended interval. Compare several values that make sense for the sample size and expected neighborhood scales.
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
digits = load_digits()
X = digits.data[:300] / 16.0
y = digits.target[:300]
X_pca = PCA(n_components=30, random_state=42).fit_transform(X)
embeddings = {
perplexity: TSNE(
n_components=2,
perplexity=perplexity,
init="pca",
learning_rate="auto",
max_iter=350,
random_state=42,
).fit_transform(X_pca)
for perplexity in (10, 30, 50)
}
assert all(value.shape == (300, 2) for value in embeddings.values())
PCA is a computational preprocessing step here, not proof that 30 components are sufficient for every task.
3. Scaling without considering measurement semantics
Distance depends on feature units. Standardization can be appropriate for commensurate continuous measurements, but it is not automatically correct: unit-variance scaling may amplify low-variance noise or sparse background pixels. In the digits example every pixel already shares a 0–16 scale, so division by 16 is transparent. Select scaling and metric from the data-generating meaning, and fit learned preprocessing only on the analysis/training partition when leakage matters.
4. Publishing one seed and one setting
A fixed random_state makes a configuration repeatable; it does not establish robustness. Compare seeds, perplexities, and justified initializations. Do not select a map merely because it shows the cleanest separation.
seed_embeddings = [
TSNE(
n_components=2,
perplexity=30,
init="random",
learning_rate="auto",
max_iter=350,
random_state=seed,
).fit_transform(X_pca)
for seed in (7, 21, 42)
]
assert all(value.shape == (300, 2) for value in seed_embeddings)
Coordinate systems can rotate, reflect, translate, and nonlinearly rearrange. Compare neighborhood diagnostics or aligned solutions, not raw coordinate equality.
5. Using outdated APIs or expecting prediction
In scikit-learn 1.9.1 use max_iter; n_iter was renamed in 1.5. The default learning rate is 'auto'. Barnes-Hut supports only two or three output dimensions. The estimator has no transform method for unseen data, so a new joint fit changes the reference frame.
6. Overlooking runtime
Exact t-SNE uses quadratic time and memory. Barnes-Hut reduces the force approximation toward but remains expensive. Subsample transparently, reduce high-dimensional dense features with PCA or TruncatedSVD where justified, and retain identifiers so the plotted subset is auditable.
Conclusion
Use t-SNE to inspect local neighborhoods, test sensitivity, and generate hypotheses. Validate cluster claims, global relationships, and operational decisions outside the t-SNE map.