Skip to main content

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 O(nlog⁡n)O(n\log n) 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.