BitBootcampBitBootcamp ← all explainers 🔄 Review ⚙ Settings

PD-01-006-P · Primer — Selecting Columns

The idea in one sentence

Square brackets on a DataFrame let you pull out a single column as a one-dimensional Series (df['name']) or a handful of columns as a smaller two-dimensional DataFrame (df[['name', 'score']]) — and the shape you get back depends entirely on whether you hand the brackets a plain name or a list of names.

Why it exists

A DataFrame is the whole spreadsheet — every column, every row — but the question you have is almost never "show me everything." You want just the names, or just the name and the score, so you can chart it, sum it, or feed it to the next step. Carrying the entire table around when you need two columns is wasteful to read and easy to get wrong.

So pandas gives you the most natural possible syntax for "reach in and grab a column": the same square brackets you already use to index a Python list or dict. df['name'] reads almost like English — "the name column of df." This bracket selection is the very first thing you do to almost every DataFrame, because analysis begins by narrowing from all the data down to the data this question is about.

How it works

You put square brackets after the DataFrame and pass what you want. Pass a single column name as a string — df['name'] — and pandas hands back that one column as a Series: a labeled one-dimensional array, essentially a single column standing on its own with its index attached. Pass a list of names — df[['name', 'score']] — and pandas hands back a DataFrame: a smaller table with exactly those columns, in the order you listed them. That extra pair of brackets is not a typo; the inner [...] is a Python list, and "list in, table out" is the rule to burn into memory. The two results behave differently on purpose: a Series is what you sum, plot, or compare (df['score'].mean()), while a subframe keeps the tabular shape for showing several columns together. Here is the whole idea on a tiny inline table:

import pandas as pd

df = pd.DataFrame({
    'name':  ['Ada', 'Ben', 'Cy'],
    'city':  ['Paris', 'Lyon', 'Nice'],
    'score': [91, 84, 78],
})

one = df['name']            # single name  -> Series (1-D)
print(type(one).__name__)   # Series
print(one)
# 0    Ada
# 1    Ben
# 2     Cy
# Name: name, dtype: object

two = df[['name', 'score']] # list of names -> DataFrame (2-D)
print(type(two).__name__)   # DataFrame
print(two)
#   name  score
# 0  Ada     91
# 1  Ben     84
# 2   Cy     78

Same brackets, same data — but a bare name gave a Series and a list of names gave a smaller DataFrame.

The one gotcha

A one-item list is not the same as a bare name — df['name'] is a Series, but df[['name']] is a DataFrame. The difference is a single pair of brackets, and it silently changes the shape of your result, not just its contents. df['name'] returns a 1-D Series you can call .mean() or .str.upper() on directly; df[['name']] returns a 2-D DataFrame with one column and shape (3, 1), and it prints with a header row and column name like a mini-table. This bites beginners when a later step expects one kind and gets the other — you .plot() something expecting a Series and get a one-column chart, or you try a Series-only method on a subframe and get an AttributeError. The fix is a habit, not a rule to memorize: plain name for one column as a Series; wrap names in a list — even a single name — when you want a DataFrame back. When in doubt, print type(result).__name__ and let it tell you which one you actually have.

See it — one name yields a Series, a list of names yields a subframe

flowchart TD
    A["df: 3 columns<br/>name, city, score"] --> B{"What is inside<br/>the brackets?"}
    B -->|"df['name']<br/>a bare string"| C["Series (1-D)<br/>shape (3,)"]
    B -->|"df[['name','score']]<br/>a list of strings"| D["DataFrame (2-D)<br/>shape (3, 2)"]
    B -->|"df[['name']]<br/>a 1-item list"| E["DataFrame (2-D)<br/>shape (3, 1)"]
    C --> F["Use it: .mean(),<br/>.plot(), .str..."]
    D --> G["Keeps table shape<br/>for many columns"]
    E --> G

The one-line takeaway

Bracket a single column name to get a 1-D Series, and bracket a list of names — even a one-item list — to get a smaller 2-D DataFrame; the extra brackets decide the shape.

Where you'll meet it

You'll meet column selection in the first minute of working with any DataFrame, because almost every pipeline starts by narrowing a wide table down to the columns that matter. You'll reach for a Series every time you compute or chart a single column — df['score'].mean(), df['city'].value_counts(), df['name'].str.lower() — since those methods live on Series, not on whole tables. You'll reach for a subframe whenever you want to keep several columns side by side: previewing df[['name', 'score']], exporting a chosen set, or passing a tidy slice into the next step. And you'll hit the one gotcha the first time a downstream function complains about shape — a model that wanted a 2-D frame but got a Series, or a Series method that fails on a one-column DataFrame. Interviewers probe it directly: "what is the difference between df['a'] and df[['a']]?" Getting that answer crisp — Series versus one-column DataFrame — signals you truly understand the shapes you're moving through, and it's the foundation the rest of pandas selection (.loc, .iloc, boolean masks) is built on.

Named concepts: single column, column list, bracket selection, Series vs subframe