Q&A library Python

Why does selecting one column return a DataFrame instead of a Series?

Python Advanced columnsduplicatesselection

Short answer

If two columns share a name, selecting that name returns both as a DataFrame, so anything expecting a Series breaks. Rename or drop the duplicate, and check for repeated names after a merge.

The short version

If a DataFrame has two columns with the same name, df["a"] returns both of them as a DataFrame, not a Series. Any code expecting a Series then fails in confusing ways. Check for duplicate column names and fix them at the source.

Seeing it

import pandas as pd

df = pd.DataFrame([[1, 2]], columns=["a", "a"])

df["a"].shape    # (1, 2) , a DataFrame, not a Series!

A frame is returned because the name is ambiguous: pandas does not know which of the two columns you meant, so it gives you both. Calling a Series method like .str.upper() on the result then raises an unhelpful error, far from the actual cause.

Detecting duplicates

df.columns.duplicated().any()          # are there any?
df.columns[df.columns.duplicated()]    # which ones

Run this after any operation that combines frames. It is a one-line check that saves a great deal of confusion.

Where duplicates come from

Most often from a concat along the columns axis, where two frames share a column name.

pd.concat([a, b], axis=1)   # if both have 'amount', you now have two

Also from read_csv on a file whose header genuinely repeats a name (pandas normally disambiguates these with a suffix, but not always), and from setting columns manually with a list containing a repeat.

Note that merge does not create duplicates, because it renames clashing columns with _x and _y suffixes. That is precisely why those suffixes exist, and it is a good reason to prefer merge over a columns-axis concat when combining related tables.

Fixing them

If the two columns hold the same data, drop one.

df = df.loc[:, ~df.columns.duplicated()]    # keep the first of each name

If they hold different data, they need different names, and you should rename them at the point they were created rather than patching afterwards, so the meaning of each is clear.

a = a.add_suffix("_left")
b = b.add_suffix("_right")

Prevent rather than cure

When concatenating along columns, rename first so no clash is possible. When building a frame from a list of column names, check the list is unique. And after any join or concat, assert that the columns are still unique.

assert not df.columns.duplicated().any(), "duplicate column names"

The wider lesson

A DataFrame with duplicate column names is technically legal but semantically broken: it makes selection ambiguous, breaks the type contract of df[col], and causes errors that surface far from their cause. Treat a duplicate column name as a bug to fix immediately, not a quirk to work around.

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project