EDA and Feature Engineeringpandas 3.0 · scikit-learn 1.9 · seaborn 0.13 · Python 3.12+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
72 small wins to finish your pathNext lesson →

One hot encoding

One hot encoding turns a column of n categories into n columns of 0 and 1, one per category, where each row carries a single 1 in the column that names its category.

Last updated: 12 Oct, 2026 · pandas 3.0 · scikit-learn 1.9

Nominal and ordinal features ended on the encoder that claims nothing about order. This is that encoder, and the two arguments that decide what it produces today.

Syntax: pd.get_dummies takes the frame or the column and returns the indicator columns.

python
pd.get_dummies(df, dtype=int)                   # 0 and 1, not True and False
pd.get_dummies(df, drop_first=True, dtype=int)  # n-1 columns per category
pd.get_dummies(df, columns=["Sex"], dtype=int)  # only the named columns

The clip encodes a binary gender column, first with get_dummies and then with Series.map, and argues for writing the result back into the frame rather than building a second one.

One hot encoding a binary column · from the EDA and Feature Engineering video · 1:10:55 to 1:14:14

The page runs the same two calls on the Titanic Sex column, which holds female and male where the clip's column holds F and M.

What get_dummies returns now

The default dtype is bool

Called with no arguments, get_dummies returns True and False, not 1 and 0. That is a change from the uint8 older tutorials show. Models accept the boolean columns, and everything you print, add up or correlate afterwards reads differently.

python
pd.get_dummies(df["Sex"])             # dtype bool
pd.get_dummies(df["Sex"], dtype=int)  # dtype int64

drop_first keeps n-1 of the n columns

The columns come out in alphabetical order of the level, and drop_first removes the first of them. A row that belonged to the dropped level is the row whose remaining columns are all 0, so no information is lost.

python
pd.get_dummies(df["Sex"], drop_first=True, dtype=int)   # female is dropped

Encoding Sex, both ways

ExampleTitanic Sex with the default dtype and with dtype=int
import pandas as pd

URL = ("https://raw.githubusercontent.com/krishnaik06/Feature-Engineering-Live-sessions/"
       "master/titanic.csv")
df = pd.read_csv(URL, usecols=["Sex", "Embarked", "Survived"])

plain = pd.get_dummies(df["Sex"])
print("default dtypes:", dict(plain.dtypes))
print(plain.head(3).to_string())

print()
ints = pd.get_dummies(df["Sex"], drop_first=True, dtype=int)
print("with dtype=int:", dict(ints.dtypes))
print(ints.head(5).to_string())

Reading the two frames

  • The first call returned bool. Two columns named female and male, holding True and False. Multiplying, summing or correlating those columns works and prints values that look wrong to anyone expecting 1 and 0.
  • dtype=int returned int64. The same information as 1 and 0, which is what every tutorial written before pandas 2 shows and what most downstream code assumes.
  • drop_first=True left one column. female came first alphabetically and was dropped, so the remaining column is male and a 0 in it means female.
  • The first five rows read 1, 0, 0, 0, 1. A male passenger, three female passengers and a male passenger, which is what the Titanic frame starts with.

Why n-1 columns carry n categories

Embarked has three levels, so this is where the argument becomes visible. With all three columns kept, they add up to 1 on every row, which means any one of them can be worked out from the other two. A linear model cannot separate three columns that always sum to a constant, and the extra column buys nothing.

ExampleEmbarked with three columns and with two
import pandas as pd

URL = ("https://raw.githubusercontent.com/krishnaik06/Feature-Engineering-Live-sessions/"
       "master/titanic.csv")
embarked = pd.read_csv(URL, usecols=["Embarked"])["Embarked"]

full = pd.get_dummies(embarked, dtype=int)
print("all levels   :", list(full.columns))
print("row sums     :", full.sum(axis=1).value_counts().to_dict())

cut = pd.get_dummies(embarked, drop_first=True, dtype=int)
print("drop_first   :", list(cut.columns))
print("rows that are all zero:", int((cut.sum(axis=1) == 0).sum()))
print("C passengers in the raw column:", int((embarked == "C").sum()))
print("missing Embarked:", int(embarked.isnull().sum()))

What the row sums give away

  • 889 rows sum to 1 and 2 rows sum to 0. The two rows summing to zero are the two passengers whose port is missing. get_dummies ignores NaN rather than encoding it, so a missing value and a dropped level look identical.
  • After drop_first, 170 rows are all zero. That is the 168 passengers who boarded at Cherbourg plus the 2 whose port is unknown.
  • Those two groups are now the same thing to the model. Fill or flag the missing values before encoding, which Missing as its own category covers, or keep all n columns so the all-zero pattern means only one thing.
  • Three columns always sum to 1. So the third is the other two subtracted from 1, and dropping it removes a redundancy rather than information.

get_dummies vs OneHotEncoder

pd.get_dummiessklearn OneHotEncoder
Returnsa DataFrame with named columnsan array, or a DataFrame with set_output
Remembers the training categoriesnoyes, in categories_ after fit
A level only in the test datamakes a column the model never sawignored or raised, your choice
A training level missing from the test datathe column disappearsthe column stays, filled with 0
Drop one columndrop_first=Truedrop='first'
Fits in a pipelineno, it is a functionyes, it is a transformer

The two middle rows are the reason the scikit-learn version exists, and Handling an unknown category builds the whole case on them.

Where one hot is the right choice

  • A nominal column with few levels. Port, payment method, device type. The frame grows by a handful of columns and no order is invented.
  • Before a linear or distance-based model. Linear regression, logistic regression and K nearest neighbours all read a coded column as a scale, so they need the indicator form.
  • When the levels are fixed and known. A column whose levels come from a database enumeration will not surprise you with a new one.
Watch out. pd.get_dummies reads whatever frame you hand it. Run it on the training data and then on the test data and you get two different sets of columns, with no error to tell you. A fitted encoder is the fix.
Try it yourself
  • Drop dtype=int from the second call and print ints.sum(). The totals are the same and the dtype is not.
  • Pass dummy_na=True to the Embarked call and count the rows that are all zero now.
  • Call pd.get_dummies(df, columns=["Sex"], dtype=int) on the whole frame and look at which columns survived untouched.

You understood something today that you didn't yesterday.