Convert categorical text into unique numerical labels. Each unique word is assigned a distinct integer ID.
Overview
Overview
Machine learning models are mathematical functions; they understand numbers, not text. Before we can train a model on categorical data (like city names, product types, or sentiments), we must convert that text into a numerical format. Label Encoding is one of the simplest techniques to achieve this. It works by assigning a unique integer to each unique category or "label" in your dataset.
Encoded Sequence
0 TOKENS
Sequence List ScrollView
Decoding Label Encoding
Machine learning models are mathematical functions; they understand numbers, not text. Before we can train a model on categorical data (like city names, product types, or sentiments), we must convert that text into a numerical format. Label Encoding is one of the simplest techniques to achieve this. It works by assigning a unique integer to each unique category or "label" in your dataset.
How It Works: Building the Vocabulary
The process is straightforward and consists of two main steps, which you can see in action by clicking the "Encode Corpus" button above.
1. Create a Vocabulary
First, the encoder scans the entire input text (the "corpus") to find all unique words (or "tokens"). It then sorts these unique words alphabetically to create a consistent vocabulary. This vocabulary acts as a dictionary or a look-up table.
2. Assign Integer IDs
Once the vocabulary is built, the encoder assigns a unique integer ID to each word, starting from 0. The first word in the sorted vocabulary gets ID 0, the second gets ID 1, and so on. The final output is a sequence of these integer IDs, representing the original text.
Why text has to become numbers at all
Every model you are likely to train is arithmetic underneath. Linear regression multiplies inputs by weights. A neural network does matrix multiplications. Even a decision tree, which feels symbolic, is comparing numbers against thresholds.
None of that works on the string "London". So before any of it can happen, categorical columns have to be turned into numbers, and label encoding is the most direct way of doing it: give each distinct value an integer.
City
Encoded
London
0
Manchester
1
Leeds
2
London
0
Leeds
2
Three rules make it predictable. The mapping is built from the values seen during fitting, usually sorted alphabetically. Every occurrence of the same value gets the same number. And the column stays one column wide, which is the property that makes it attractive for columns with hundreds of distinct values.
The problem it creates, stated precisely
The encoder has just told your model that Leeds (2) is twice Manchester (1), and that Manchester sits exactly halfway between London and Leeds. None of that is true. Nothing about the cities implies an order, let alone even spacing.
For models that do arithmetic on feature values, this invented ordering is a real distortion:
Linear and logistic regression fit one coefficient for the column, forcing the effect of the city to be a straight line across the invented number sequence.
KNN and k-means compute distances, so "London to Leeds" is now twice "London to Manchester".
Neural networks multiply the value by a weight and will happily learn spurious patterns from the ordering.
For tree-based models the damage is much smaller, because a tree only asks "is this value ≤ 1.5?", which is a question about set membership rather than magnitude. A tree can carve out any subset of categories given enough splits — it just needs more splits than it would with a better encoding.
So the practical rule is not "never use label encoding". It is: label encoding for tree models and for genuinely ordered categories, one-hot or target encoding for everything else.
When the order is real
Some categories do have an order, and encoding it as numbers is not a distortion but a gift to the model.
This is called ordinal encoding, and the crucial difference from label encoding is that you specify the order rather than letting the alphabet decide. Left to itself, LabelEncoder would map excellent→0, fair→1, good→2, poor→3, which scrambles the meaning entirely.
from sklearn.preprocessing import OrdinalEncoder
order = [["poor", "fair", "good", "excellent"]] # you state the order
enc = OrdinalEncoder(categories=order)
enc.fit_transform([["good"], ["poor"], ["excellent"]]) # [[2], [0], [3]]
One thing ordinal encoding still gets wrong: it assumes equal spacing. The step from "poor" to "fair" is treated as the same size as "good" to "excellent", which may not match reality. If that matters, encode with the actual values — 1, 3, 7, 12 — or use a model that does not care about spacing.
The unseen-category trap
This is the bug that reaches production. You fit the encoder on training data containing London, Manchester and Leeds. Three weeks later a row arrives with "Bristol", and the encoder raises ValueError: y contains previously unseen labels. Your prediction service returns a 500.
Three defences, in increasing order of robustness:
Fit the encoder on the full set of known categories, not just the ones present in the training split. If you have a reference list of cities, use it.
Use OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1), which maps anything unseen to a reserved code instead of raising.
Collapse rare categories into an explicit "other" bucket during training, so the model has actually learned what to do with an unfamiliar value.
The related trap is fitting the encoder separately on train and test. Fit once, on training data, then apply the same fitted object to everything else — refitting produces a different mapping, and your test set silently means something different from your training set.
Three encoders, and when each one is correct
Ordinal, label and one-hot encoding do similar-looking things for different purposes. Using the wrong one is a common and quiet mistake.
example_01.pyscikit-learn
import numpy as np
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder, OneHotEncoder
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
sizes = np.array(["small", "medium", "large", "x-large"])
colours = np.array(["red", "green", "blue"])
print("LabelEncoder is for the TARGET, and takes a 1-D array:")
le = LabelEncoder().fit(["cat", "dog", "bird", "dog"])
print(" classes:", list(le.classes_))
print(" transform:", le.transform(["dog", "bird", "cat"]))
print(" inverse: ", list(le.inverse_transform([0, 1, 2])))
print(" it will happily encode a feature too, and that is the mistake -- it")
print(" has no handle_unknown, so one new category at predict time crashes.")
print()
print("OrdinalEncoder is for FEATURES, takes 2-D, and lets you set the order:")
X = np.array([["small"], ["x-large"], ["medium"], ["large"]])
auto = OrdinalEncoder().fit(X)
told = OrdinalEncoder(categories=[list(sizes)]).fit(X)
print(" alphabetical (the default):")
for c, n in zip(auto.categories_[0], range(4)):
print(" %-8s -> %.0f" % (c, n))
print(" the order you meant:")
for c, n in zip(told.categories_[0], range(4)):
print(" %-8s -> %.0f" % (c, n))
print(" alphabetically, x-large sorts last and large sorts second. the model")
print(" is told large < medium < small < x-large, which is nonsense.")
print()
rng = np.random.default_rng(0)
idx = rng.integers(0, 4, 1500)
size_col = sizes[idx]
price = np.array([10.0, 20.0, 30.0, 40.0])[idx] + rng.normal(0, 2, 1500)
Xa = OrdinalEncoder().fit_transform(size_col.reshape(-1, 1))
Xt = OrdinalEncoder(categories=[list(sizes)]).fit_transform(size_col.reshape(-1, 1))
Xh = OneHotEncoder(sparse_output=False).fit_transform(size_col.reshape(-1, 1))
print("price really does rise with size. one column, three encodings:")
for name, Xe in (("ordinal, alphabetical", Xa), ("ordinal, correct order", Xt),
("one-hot", Xh)):
print(" %-24s linear R2 %.4f forest R2 %.4f"
% (name, cross_val_score(LinearRegression(), Xe, price, cv=5).mean(),
cross_val_score(RandomForestRegressor(n_estimators=100, random_state=0),
Xe, price, cv=5).mean()))
print()
print("the correct ordinal encoding wins for the linear model, because the")
print("order is real and one number captures it. the alphabetical one loses")
print("badly. the forest does not care -- it can split its way to the right")
print("answer from any encoding, it just needs more depth to do it.")
print()
print("now a column with NO order -- colour:")
col = colours[rng.integers(0, 3, 1500)]
worth = {"red": 50.0, "green": 12.0, "blue": 31.0}
value = np.array([worth[c] for c in col]) + rng.normal(0, 2, 1500)
Xo = OrdinalEncoder().fit_transform(col.reshape(-1, 1))
Xoh = OneHotEncoder(sparse_output=False).fit_transform(col.reshape(-1, 1))
print(" ordinal linear R2 %.4f" % cross_val_score(LinearRegression(), Xo, value, cv=5).mean())
print(" one-hot linear R2 %.4f" % cross_val_score(LinearRegression(), Xoh, value, cv=5).mean())
print(" there is no order to find, so forcing one costs you most of the signal.")
print()
print("the rule, in one line each:")
print(" ordered categories, linear model -> OrdinalEncoder, categories= set")
print(" unordered categories, linear model -> OneHotEncoder")
print(" any categories, tree model -> ordinal is fine and much cheaper")
print(" the target column -> LabelEncoder")
Output
Things to try
Use the interactive panel to understand the nuances of this technique.
Observe the Default Text: Click the "Encode Corpus" button with the default text. The vocabulary is created from the unique words: ['deep', 'fascinating', 'is', 'learning', 'machine']. Notice that "machine" and "learning" appear multiple times in the input, but only once in the vocabulary. The encoded sequence then maps each word to its corresponding index in this vocabulary (e.g., 'machine' becomes 4, 'learning' becomes 3).
Add a New Word: Add the word "new" to the end of the text area and click "Encode Corpus" again. The vocabulary will be rebuilt and sorted alphabetically: ['deep', 'fascinating', 'is', 'learning', 'machine', 'new']. Observe that 'new' is assigned the ID 5. All the original words keep their IDs because 'new' comes last alphabetically.
Add a Word at the Beginning (Alphabetically): Now, add the word "a" to the text. Click "Encode Corpus". The new vocabulary is ['a', 'deep', 'fascinating', 'is', 'learning', 'machine', 'new']. Notice what happened: 'a' is now assigned ID 0, and the ID for every other word has shifted up by one ('deep' is now 1, 'fascinating' is 2, etc.). This highlights a key property: the numerical assignments depend entirely on the complete, sorted vocabulary.
The Big Drawback: Unintended Ordinality
While simple, Label Encoding has a major flaw. Most machine learning models will interpret the assigned integers as having an order or rank. For example, if you have categories 'Cat', 'Dog', and 'Fish', they might be encoded as 0, 1, and 2.
The model might incorrectly assume that 'Dog' (1) is "greater" than 'Cat' (0), or that the difference between 'Fish' (2) and 'Cat' (0) is twice the difference between 'Dog' (1) and 'Cat' (0). This is an arbitrary ordinal relationship that doesn't exist in the original data, and it can mislead your model.
When is it okay? Label Encoding is suitable for categorical features that have a clear, inherent order (ordinal features), such as 'Low', 'Medium', 'High' or 'First Class', 'Second Class', 'Third Class'.
When should it be avoided? It should generally be avoided for features without a natural order (nominal features), like 'Country' or 'Color'. For these cases, One-Hot Encoding is a much better choice as it creates a new binary feature for each category, avoiding any implied ranking.
The alternatives, and when each wins
Encoding
Columns added
Invents an order?
Best for
Label / ordinal
0
Yes, unless you set it
Tree models; genuinely ordered categories
One-hot
One per category
No
Linear models, neural networks, few categories
Target (mean) encoding
0
No
High-cardinality columns with a strong signal
Frequency encoding
0
Only by frequency
When how common a value is carries meaning
Hashing
A fixed number
No
Enormous cardinality where collisions are acceptable
Learned embeddings
A chosen width
No
Neural networks with many categories
Target encoding deserves a warning. Replacing each category with the average target value for that category is powerful and leaks badly if done carelessly: a category appearing once takes the target of its own row, and the model reads the answer straight off the feature. Always compute it out-of-fold, and smooth rare categories towards the global average.
A quick heuristic for the everyday case: under about 10 categories, one-hot. Above 50 and using trees, label encoding or the library's native categorical support. Above 50 and using a linear model or a network, target encoding or learned embeddings.
Questions people ask
Should I label-encode the target variable? Yes, that is the one place it is unambiguously right. Classification targets need to be integers, there is no arithmetic performed on them, and scikit-learn's LabelEncoder is designed for exactly this.
Does the order of the integers matter for trees? Slightly. A tree can express any grouping eventually, but an ordering that happens to put similar categories next to each other needs fewer splits. This is part of why target-statistic orderings help boosted trees.
Is label encoding the same as ordinal encoding? Effectively the same operation with a different intent, and in scikit-learn LabelEncoder works on a single target column while OrdinalEncoder works on feature matrices and lets you specify the order. Use the second one for features.
What about missing values? Decide deliberately. Treating "missing" as its own category is often correct — the fact that a field was left blank is frequently informative. Silently imputing it to the most common value throws that away.
Will one-hot encoding always be safer? Safer for linear models, but with 10,000 distinct values it produces 10,000 mostly-zero columns, which is slow, memory-hungry and gives each individual column almost no data to learn from. Safety has a cost.
Recap in one screen
Models need numbers, so categorical text has to be encoded.
Label encoding assigns one integer per category and keeps the column width at one.
Those integers imply an order and a spacing that usually do not exist — harmless for trees, damaging for linear models, KNN and networks.
Use it deliberately for ordered categories, and state the order yourself.
Fit the encoder once, on training data, and plan for categories you have never seen.
Recall check
0 of 3
Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.
What does this module say about “Overview”?
Machine learning models are mathematical functions; they understand numbers, not text. Before we can train a model on categorical data (like city names, product types, or sentiments), we must convert that text into a numerical format. Label Encoding is one of the simplest techniques to achieve this. It works by assigning a unique integer to each unique category or "label" in your dataset.
What does this module say about “How It Works: Building the Vocabulary”?
The process is straightforward and consists of two main steps, which you can see in action by clicking the "Encode Corpus" button above.
What does this module say about “Create a Vocabulary”?
First, the encoder scans the entire input text (the "corpus") to find all unique words (or "tokens"). It then sorts these unique words alphabetically to create a consistent vocabulary. This vocabulary acts as a dictionary or a look-up table. 2. Assign Integer IDs
Cheat sheet
Label Encoding Process
Machine learning models are mathematical functions; they understand numbers, not text. Before we can train a model on categorical data (like city names, product types, or sentiments), we must convert that text into a numerical format. Label Encoding is one of the simplest techniques to achieve this. It works by assigning a unique integer to each unique category or "label" in your dataset.
Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.