Quick answer: One-hot encoding maps each categorical label to a fixed-width row with one active column. Fit a stable class-to-index mapping, preserve its order across data splits, and define a policy for unknown labels before constructing the NumPy array.

One-hot encoding converts class labels into numeric indicator vectors. A label such as 2 becomes a row like [0, 0, 1], where the 1 marks the active class. This format is common in machine learning because models need numeric arrays, not raw category names. With NumPy, the usual pattern is to create an identity matrix with numpy.eye and index into it with class labels.
One-hot encoding is useful when categories have no natural order. It prevents a model from treating class 2 as twice class 1. If you are reshaping features before or after encoding, our NumPy reshape guide and NumPy ravel guide cover related shape cleanup.
The output shape is usually (number_of_samples, number_of_classes). Each row represents one original label, and each column represents one class. This makes the mapping explicit and easy to inspect before a model sees the data. Once categorical features are encoded for a model, PyCaret create_api() for ML APIs shows how PyCaret can expose the trained workflow through create_api().
Basic NumPy One-Hot Encoding
For integer class labels that already start at 0, use np.eye(num_classes)[labels]. The identity matrix supplies one row for each class.
import numpy as np
labels = np.array([2, 0, 1, 2])
num_classes = 3
encoded = np.eye(num_classes, dtype=int)[labels]
print(encoded)
The output has one row per label and one column per class. Labels with value 2 activate the third column, label 0 activates the first column, and label 1 activates the second column.
This pattern is fast because NumPy performs array indexing instead of a Python loop. It is also readable once you remember that each row of the identity matrix already contains the correct one-hot vector.
Infer The Number Of Classes
If the number of classes is not provided, infer it from the largest label. This works when labels are zero-based integers with no negative values.
import numpy as np
labels = np.array([0, 2, 1, 2])
num_classes = labels.max() + 1
encoded = np.eye(num_classes, dtype=int)[labels]
print(encoded.shape)
For production code, validate labels before using them as indexes. Negative values or labels outside the expected range should fail clearly.
Inferring the class count is convenient for quick analysis. In a deployed model, prefer a fixed class list so training and prediction use the same column order.

Validate Labels Before Encoding
A small helper keeps the shape and label checks in one place. This avoids confusing IndexError messages later in a training or preprocessing pipeline.
import numpy as np
def one_hot(labels, num_classes):
labels = np.asarray(labels)
if labels.min() < 0 or labels.max() >= num_classes:
raise ValueError("Labels must be in the range 0 to num_classes - 1.")
return np.eye(num_classes, dtype=int)[labels]
print(one_hot([0, 1, 2], 3))
This helper is intentionally strict. If the labels do not match the expected class range, it raises an error before building an incorrect feature matrix.
Strict validation is especially useful when labels come from user input, files, or a database. Bad labels should be caught during preprocessing instead of producing silent columns in the wrong place.
Encode String Categories
Raw categories are often strings. Use numpy.unique with return_inverse=True to map strings to integer labels, then one-hot encode the integer labels.
import numpy as np
categories = np.array(["red", "blue", "green", "red"])
classes, labels = np.unique(categories, return_inverse=True)
encoded = np.eye(len(classes), dtype=int)[labels]
print(classes)
print(encoded)
The classes array tells you which column belongs to each category. Keep that mapping with the model or preprocessing output so predictions can be interpreted later.
String category order should be treated as part of the feature definition. If the order changes between training and inference, the same category may point to a different column.
Use Float Output For Models
Many machine-learning frameworks accept integer one-hot arrays, but float arrays are often more convenient for numeric pipelines. Set the identity matrix dtype to float.
import numpy as np
labels = np.array([1, 0, 1])
encoded = np.eye(2, dtype=float)[labels]
print(encoded)
print(encoded.dtype)
Use the dtype expected by the next library. If your pipeline also uses text features, the guide on fixing TfidfVectorizer get_feature_names covers a related scikit-learn feature extraction issue.
Float output can also make later numeric operations simpler, especially when encoded features are concatenated with continuous features in the same array.

When To Use scikit-learn
NumPy is enough when labels are already in memory and the class mapping is simple. For reusable preprocessing pipelines, the official scikit-learn OneHotEncoder handles fitting, transforming, unknown categories, and sparse output.
import numpy as np
labels = np.array([0, 1, 2, 1])
encoded = np.eye(3, dtype=int)[labels]
row_sums = encoded.sum(axis=1)
print(row_sums)
Each row should sum to 1 for a single-label one-hot encoding. That check is a useful sanity test before passing encoded data into a model. For count-based feature checks, see the NumPy histogram guide.
The practical rule is simple: use NumPy for direct in-memory label arrays, and use scikit-learn when you need a fitted transformer that preserves category handling across train and test data. Keep the class-to-column mapping with the saved model. That makes future predictions auditable and keeps feature columns consistent across retraining runs.
Fit The Class Vocabulary
Collect or configure the allowed classes once and freeze their column order. Sorting independently in training and inference can change the meaning of every encoded feature.

Build The Matrix
Map each label to an integer index, allocate a rows-by-classes array with the intended dtype, and set one entry per row. Validate that every index is within the class range.
Handle Unknown Categories
Reject unknown labels when the input contract is strict, reserve an unknown column, or use an encoder with documented unknown handling. Do not silently map a new category to an arbitrary class.
Choose Dense Or Sparse
A dense matrix is simple for small class vocabularies. High-cardinality categories can be mostly zeros, so a sparse representation or an embedding may be more appropriate for the downstream model.

Keep Train And Serve Consistent
Persist the class mapping with the model or data pipeline. Check feature count, column order, dtype, and missing-value policy before sending inference data to the model.
Test Shape And Semantics
Test one class, multiple classes, repeated labels, empty input, unknown labels, missing values, dtype, row sums, and train/test round trips. Assert the active column for each known label.
Use the official NumPy identity-array documentation as a primitive comparison for class matrices. Related Python Pool references include NumPy arrays and tests.
For related feature preparation, compare NumPy arrays, shape tests, and class mappings before encoding labels.
Frequently Asked Questions
What is one-hot encoding?
It represents a categorical label as a vector whose one active position identifies the class.
How do I create one-hot arrays with NumPy?
Map labels to integer class indices, allocate a two-dimensional array, and set the corresponding row and column entries to one.
What happens with an unknown label?
Choose an explicit policy such as rejecting it, reserving an unknown column, or using a fitted encoder that documents how unseen categories are handled.
Why do one-hot shapes differ between training and inference?
The class vocabulary or column order changed; fit the mapping once and reuse the same ordered classes for every dataset.