skip to content

A k-NN uses 6 numeric columns and 5,000 one-hot merchant-ID columns and predicts poorly — how do you fix it?

level: seniorimportance: should knowfreq 44%

answer

  1. one variable occupying five thousand directions
  2. same-merchant rows always win the neighbour list
  3. scaling rare indicator columns makes it worse
  4. keep the ID out of the distance
  5. check contrast and neighbour composition

basics

~20 s

One variable spread over 5,000 columns dominates the distance, so neighbours are chosen by merchant match rather than by the six numeric features. Take the ID block out of the metric, or model it separately, and re-measure the near/far contrast.

solid answer

~50 s

The merchant ID is one variable, but one-hot encoding gives it 5,000 columns' worth of presence in the distance while the real signal sits in six. In raw form, every pair of rows with different merchants picks up the same fixed contribution to squared distance, so the near/far contrast across the bulk of pairs collapses and any row sharing the merchant jumps to the front regardless of how different its numbers are. Standardising the columns makes it worse: a rare merchant's column has a tiny standard deviation, so a single mismatch on it can contribute thousands to the squared distance and bury the numeric features entirely. The fix is to stop letting the ID into the metric — run the k-NN on the six numeric columns, and handle merchant as a partition or hand it to a model that selects features internally. Prove it with a held-out comparison plus the contrast ratio before and after.

go deeper

for a junior

Be ready to notice that 5,000 columns encode a single variable while only six columns hold the numeric signal, and that a distance adds up all of them equally. Saying the ID overwhelms the distance already answers most of it.

for a middle

Explain the decomposition: the block contributes a fixed amount when merchants differ and nothing when they match, so same-merchant rows dominate the neighbour list and the near/far contrast flattens. Be able to say why per-column scaling makes rare merchants worse.

for a senior

Lead with diagnostics — contrast ratio, neighbour composition, an ablation on the six numeric columns — then the fix. Show judgment about keeping the ID as a partition key or a compact summary rather than reflexively discarding it.

for a principal

Own the model-family decision. If merchant identity is genuinely predictive at high cardinality, argue for a family that evaluates columns individually rather than defending a metric-based model with escalating patches, and state what that trades away in interpretability and serving cost.

## Diagnose before you fix Three measurements settle this in minutes. 1. **Contrast ratio.** For a sample of queries, compute `(farthest - nearest) / nearest` against the training rows. If it is small — well under one — the metric has lost its ability to rank. 2. **Neighbour composition.** For each query, count how many of its `k` neighbours share its merchant ID. If that number is far above what the merchant's frequency would produce by chance, the ID is driving the neighbourhood. 3. **Ablation.** Refit on the six numeric columns alone and compare cross-validated scores. If the score jumps, the 5,000 columns were noise carriers, not features. ## Why a wide one-hot block wrecks a distance One-hot encoding turns one variable into a block of columns, and a Euclidean distance treats every column as an equal, independent direction. So a single categorical with 5,000 levels arrives with 5,000 directions' worth of standing, while the six numeric columns that actually carry the signal arrive with six. Work through the raw case. With exactly one merchant per row, two rows either share the merchant — the block contributes 0 to squared distance — or differ, in which case they differ in exactly two columns and the block contributes 2. The distance decomposes as ``` D2 = (block: 0 or 2) + (numeric part) ``` That has two effects. First, if the numeric columns live on small scales, all different-merchant pairs sit at nearly the same distance and the near/far contrast collapses toward one: distance-weighted voting flattens, and any radius threshold becomes all-or-nothing. Second, any row sharing the merchant skips the offset entirely and leaps to the front of the neighbour list, however unlike the query it is numerically — the model degenerates into "look up other rows from the same merchant". Now the standardised case, which candidates often propose as the fix. Standardising a one-hot column divides by `sqrt(p(1-p))`, where `p` is that merchant's share of rows. For a merchant appearing in one row in five thousand, that divisor is about 0.014, so a mismatch on that column contributes roughly `1 / 0.0002`, in the thousands, to the squared distance. The block does not merely compete with the numeric features, it annihilates them, and the neighbour ordering becomes a near-arbitrary function of which rare merchants happen to be involved. Scaling is the right instinct for mixed units and the wrong instinct for a wide categorical block. ## The fixes, in order of directness - **Take the ID out of the metric.** Run the k-NN on the six numeric columns. This is the single change that most reliably restores contrast, and it costs only the information the ID carried — which the model was mishandling anyway. - **Give the block one variable's worth of weight.** If the merchant genuinely matters, weight the whole block so its total contribution is comparable to a single numeric feature, rather than letting 5,000 columns vote independently. - **Partition instead of encode.** Use the merchant as a grouping key — per-merchant or per-segment models, or a same-merchant filter applied before the numeric distance — so the ID selects the comparison set rather than distorting the geometry. - **Compress the ID into a small numeric summary.** Replacing thousands of indicator columns with one or two derived numbers keeps the information at a fraction of the dimensional cost. The choice of encoding scheme is a feature-engineering decision and belongs with that material, not with the metric. - **Change model family.** A gradient-boosted tree ensemble or a regularised linear model evaluates columns individually rather than summing them all into one distance, so a wide sparse block costs far less. If the merchant identity is genuinely predictive and high-cardinality, this is often the honest answer. ## What not to do Raising `k` does not help: it averages over a neighbour set that was chosen badly in the first place. Switching the distance function does not help either, because the problem is how many columns one variable occupies, not how the per-column differences are combined. Collecting more rows does not help, since the number needed to populate a 5,000-dimensional space is unreachable. ## Closing the loop Whatever you change, re-run the three diagnostics. The contrast ratio should rise, the share of neighbours sharing the query's merchant should drop back toward chance unless you deliberately partitioned on it, and cross-validated performance should improve. Reporting all three, rather than only the score, is what makes the fix defensible when someone asks why 5,000 features were thrown away.

  • If every different-merchant pair picks up the same fixed offset, why does the ranking break at all?
    It stays constant only in the raw, single-categorical case. Standardise the columns and each merchant's mismatch cost differs with its frequency, so the offset varies and dominates. Even where it is constant, it flattens the near/far contrast — so distance weights, radius rules and tie-breaks stop discriminating — and rows sharing the merchant still bypass the offset and monopolise the neighbour list.
  • How would you show your fix worked rather than assert it?
    Compare cross-validated performance before and after on the same folds, and report two structural diagnostics alongside it: the near/far contrast ratio, which should rise once the block is out of the metric, and the share of neighbours sharing the query's merchant, which should fall back toward chance. A score improvement plus a mechanism that explains it is much harder to argue with than a score alone.
  • Would a gradient-boosted tree ensemble suffer the same way?
    Far less. Boosting evaluates candidate splits column by column, so a mostly-zero indicator column simply never gets chosen unless it earns the split, whereas a Euclidean distance sums every column whether it helps or not. The wide block costs training time and can encourage overfitting on rare levels, but it does not swamp the informative features the way it swamps a metric.

It is a committee where one member brought five thousand proxies and six other members have one vote each — the outcome is decided before the numbers are heard.

saying these in an interview costs you the question

  • Says standardising the one-hot columns will fix it
  • Blames the choice of k rather than the feature space
  • Suggests raising k to average the problem away
  • Assumes 5,000 columns are harmless because most values are zero
  • Proposes collecting more rows to populate the wide space

context