Skip to content

Handling Text & Categorical Attributes

What you’ll learn

  • why most ML algorithms need numbers, not text categories
  • OrdinalEncoderOrdinalEncoder — encoding categories as integers
  • the hidden problem OrdinalEncoderOrdinalEncoder introduces
  • OneHotEncoderOneHotEncoder — encoding categories as binary vectors
  • when one-hot encoding stops being practical

The one text column: ocean_proximity

Everything so far has been numeric except ocean_proximityocean_proximity. It holds a limited set of repeated string values — <1H OCEAN<1H OCEAN, INLANDINLAND, NEAR OCEANNEAR OCEAN, NEAR BAYNEAR BAY, ISLANDISLAND — which makes it a categorical attribute rather than free text. Most ML algorithms prefer numbers, so this needs converting.

Isolate the categorical column
housing_cat = housing[["ocean_proximity"]]
print(housing_cat.head(10))
Isolate the categorical column
housing_cat = housing[["ocean_proximity"]]
print(housing_cat.head(10))

OrdinalEncoder: categories to integers

The simplest conversion maps each category to an integer:

OrdinalEncoder
from sklearn.preprocessing import OrdinalEncoder
 
ordinal_encoder = OrdinalEncoder()
housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)
 
print(housing_cat_encoded[:5])
print(ordinal_encoder.categories_)
OrdinalEncoder
from sklearn.preprocessing import OrdinalEncoder
 
ordinal_encoder = OrdinalEncoder()
housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)
 
print(housing_cat_encoded[:5])
print(ordinal_encoder.categories_)

This works, but it introduces an assumption most algorithms make automatically: that nearby numbers are more similar. That’s true for genuinely ordered categories (“bad”, “average”, “good”, “excellent”), but ocean_proximityocean_proximity isn’t ordered — category 00 (<1H OCEAN<1H OCEAN) and category 44 (NEAR OCEANNEAR OCEAN) aren’t inherently “close” just because 00 and 44 are far apart numerically, and category 00 and 11 aren’t inherently similar just because their integers are adjacent.

OneHotEncoder: one binary column per category

The standard fix is one-hot encoding: create one binary column per category, set to 11 when a row belongs to that category and 00 otherwise. Only one column is “hot” (1) per row — the rest are “cold” (0).

OneHotEncoder
from sklearn.preprocessing import OneHotEncoder
 
cat_encoder = OneHotEncoder()
housing_cat_1hot = cat_encoder.fit_transform(housing_cat)
 
print(housing_cat_1hot.toarray()[:5])
print(cat_encoder.categories_)
OneHotEncoder
from sklearn.preprocessing import OneHotEncoder
 
cat_encoder = OneHotEncoder()
housing_cat_1hot = cat_encoder.fit_transform(housing_cat)
 
print(housing_cat_1hot.toarray()[:5])
print(cat_encoder.categories_)

By default, OneHotEncoderOneHotEncoder returns a sparse matrix — it only stores the location of non-zero entries instead of every zero, which matters a lot once you have categorical columns with hundreds or thousands of categories. Call .toarray().toarray() only when you actually need a dense NumPy array (e.g., to print or inspect it).

diagram Ordinal vs. one-hot encoding mermaid
Same categorical column, two very different numeric representations.

When one-hot encoding isn’t practical

If a categorical attribute has a huge number of possible values (a country code, a user ID, a product SKU), one-hot encoding explodes into a huge number of mostly-zero columns, which can slow down training. In that case, consider:

  • replacing the category with a related numeric feature (e.g., country code → population and GDP per capita)
  • learning a low-dimensional embedding per category (common in deep learning — each category’s representation is learned during training, not fixed up front)

For a handful of categories like ocean_proximityocean_proximity, though, plain one-hot encoding is the right default.

🧪 Try It Yourself

Exercise 1 – Ordinal-encode a categorical column

Exercise 2 – One-hot encode and inspect categories

Exercise 3 – Convert a sparse matrix to a dense array

Next

Continue to Feature Scaling (Normalization & Standardization) — with every column numeric now, the next problem is that they’re all on wildly different scales.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did