Selecting Columns and Rows
Reviewed & published by Brayan K
Selecting in pandas means pulling out the specific columns or rows you care about from a DataFrame using square-bracket indexing — the everyday way you narrow a table down to just the data you need.
Part of the free Pandas course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Master single-column vs multi-column selection, row slicing, and the attribute-access shortcut — the foundation of every analysis you'll do.
What You'll Learn in This Lesson
- Select one column with df["col"] (returns a Series)
- Select multiple columns with df[["a", "b"]] (returns a DataFrame)
- The difference between a Series and a DataFrame
- Select rows by position with slicing df[0:3]
- Use df.col attribute access — and when it breaks
- Build a small DataFrame from a dictionary to practice
1 Selecting a Single Column — df["col"]
To grab one column, put its name in square brackets after the DataFrame: df["age"]. This returns a Series — a one-dimensional labeled array, basically a single column with its index attached.
Let's build a small DataFrame from a dictionary and pull out one column:
import pandas as pd
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"],
})
ages = df["age"] # a Series
print(type(ages)) # <class 'pandas.core.series.Series'>import pandas as pd
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"],
})
# Select the "age" column -> returns a Series
ages = df["age"]
print(ages)
print(type(ages)) # <class 'pandas.core.series.Series'>
print(ages.mean()) # 30.0 -> you can call methods on a Series
# ✅ Expected output:
# 0 25
# 1 30
# 2 35
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
# 30.02 Selecting Multiple Columns — df[["a", "b"]]
To select more than one column, pass a list of column names inside the brackets. Because you pass a list, you end up with double brackets: df[["name", "city"]]. The result is a DataFrame, not a Series.
subset = df[["name", "city"]] # list inside brackets -> DataFrame
print(type(subset)) # <class 'pandas.core.frame.DataFrame'>🧠 The bracket rule in one line:
- df["age"] → single brackets → Series
- df[["age"]] → double brackets → DataFrame (one column)
- df[["age", "city"]] → list → DataFrame (many columns)
You also control column order this way — the columns appear in the order you list them, which is handy for reshaping output.
import pandas as pd
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"],
})
# Pass a LIST of columns -> get a DataFrame back
subset = df[["city", "name"]] # note: reordered on purpose
print(subset)
print(type(subset)) # <class 'pandas.core.frame.DataFrame'>
# Double brackets keep DataFrame shape even for ONE column
print(df[["age"]]) # still a DataFrame, with the "age" header
# ✅ Expected output:
# city name
# 0 NYC Alice
# 1 LA Bob
# 2 Chicago Carol
# <class 'pandas.core.frame.DataFrame'>
# age
# 0 25
# 1 30
# 2 353 Selecting Rows by Slice — df[0:3]
Square brackets behave differently when you pass a slice. df[0:3] selects rows by position (0, 1, 2) — just like slicing a Python list. The end index is exclusive.
print(df[0:2]) # first two rows (positions 0 and 1)
print(df[1:]) # everything from row 1 onwardimport pandas as pd
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol", "Dan"],
"age": [25, 30, 35, 40],
})
# A SLICE selects rows by position (end is exclusive)
print(df[0:2])
print(df[2:])
# ✅ Expected output:
# name age
# 0 Alice 25
# 1 Bob 30
# name age
# 2 Carol 35
# 3 Dan 40⚡ Shortcut: Attribute Access (df.col)
Pandas lets you read a column as if it were an attribute: df.age instead of df["age"]. It's a nice shortcut while exploring, but it has real limits.
- df.age — simple name ✓
- df.city — no spaces ✓
- df["first name"] — has a space ✗
- df.count — clashes with a method ✗
- df.2024 — not a valid identifier ✗
import pandas as pd
df = pd.DataFrame({
"product": ["Pen", "Notebook", "Eraser"],
"price": [1.50, 3.00, 0.75],
"stock": [120, 80, 200],
})
# 🎯 YOUR TURN:
# 1) Select just the "price" column as a Series.
# 2) Select "product" and "stock" together as a DataFrame.
# Replace the ___ blanks below.
price_series = df[___] # hint: single brackets, one name
two_cols = df[___] # hint: a LIST of two names
print(price_series)
print(two_cols)
# Expected output:
# 0 1.50
# 1 3.00
# 2 0.75
# Name: price, dtype: float64
# product stock
# 0 Pen 120
# 1 Notebook 80
# 2 Eraser 200📋 Quick Reference
| Goal | Syntax | Returns |
|---|---|---|
| One column | df["age"] | Series |
| One column (keep frame) | df[["age"]] | DataFrame |
| Many columns | df[["age", "city"]] | DataFrame |
| Rows by position | df[0:3] | DataFrame |
| Attribute shortcut | df.age | Series (limited) |
🎯 Mini Challenge: Build and Slice a Table
Build a DataFrame of three movies, then practice every selection style you learned.
- Create columns: title, year, rating
- Pull rating as a Series and print its average
- Select title and rating as a DataFrame
- Slice the first two rows
import pandas as pd
movies = pd.DataFrame({
"title": ["Inception", "Coco", "Parasite"],
"year": [2010, 2017, 2019],
"rating": [8.8, 8.4, 8.5],
})
# 1) rating as a Series + average
print(movies["rating"].mean()) # 8.566666666666666
# 2) two columns as a DataFrame
print(movies[["title", "rating"]])
# 3) first two rows
print(movies[0:2])
# ✅ Expected output:
# 8.566666666666668
# title rating
# 0 Inception 8.8
# 1 Coco 8.4
# 2 Parasite 8.5
# title year rating
# 0 Inception 2010 8.8
# 1 Coco 2017 8.4❓ Frequently Asked Questions
Lesson complete — you can carve up any DataFrame!
You now know that single brackets give a Series, a list of names gives a DataFrame, and a slice selects rows. You also know when the handy df.col shortcut breaks down.
Practice quiz
What type does df['a'] return for a single column?
- A Series
- A DataFrame
- A list
- A dict
Answer: A Series. Single-bracket access of one column returns a Series.
What does df[['a']] (double brackets) return?
- A Series
- A DataFrame with one column
- A NumPy array
- A scalar value
Answer: A DataFrame with one column. Passing a list of names returns a DataFrame, even for one column.
How do you select columns 'a' and 'c' together?
- df['a', 'c']
- df.a.c
- df[['a', 'c']]
- df.cols('a','c')
Answer: df[['a', 'c']]. Pass a list of names: df[['a', 'c']].
Which accessor selects by LABEL?
- df.iloc[...]
- df.at_pos[...]
- df.get[...]
- df.loc[...]
Answer: df.loc[...]. loc is label-based; iloc is integer-position based.
Which accessor selects by INTEGER POSITION?
- df.iloc[...]
- df.loc[...]
- df.label[...]
- df.pos[...]
Answer: df.iloc[...]. iloc uses integer positions, e.g. df.iloc[0, 1].
What does df.loc[df['a'] > 1] do?
- Sorts by column a
- Keeps rows where column a exceeds 1
- Drops column a
- Renames a
Answer: Keeps rows where column a exceeds 1. A boolean mask inside loc filters rows by condition.
How do you select a single cell at row 'r', column 'b' by label?
- df['r']['b']
- df.cell('r','b')
- df.loc['r', 'b']
- df.get('r','b')
Answer: df.loc['r', 'b']. df.loc['r', 'b'] returns the scalar at that label intersection.
What does df.iloc[0:2] return?
- The first column
- Every other row
- Nothing
- The first two rows
Answer: The first two rows. Integer slicing 0:2 selects rows at positions 0 and 1.
Which is the recommended way to set a value to avoid the chained-assignment warning?
- df.loc[mask, 'col'] = value
- df[mask]['col'] = value
- df['col'][mask] = value
- df[mask, 'col'] = value
Answer: df.loc[mask, 'col'] = value. Use a single .loc call so pandas writes to the original frame.
What does df.filter(like='date') select?
- Columns whose name contains 'date'
- Rows equal to 'date'
- The first date column only
- Cells matching 'date'
Answer: Columns whose name contains 'date'. filter(like=...) keeps labels that contain the substring.
Continue this course
- Previous: Inspecting Data: head, tail, info, describe
- Next: Filtering with Boolean Masks — Keep only the rows that match your conditions using boolean masks
- Quick reference: Pandas cheat sheet › Selecting & Filtering
Frequently asked questions
What is the difference between df["col"] and df[["col"]]?
df["col"] returns a one-dimensional Series, while df[["col"]] returns a DataFrame with a single column. Use double brackets when you want to keep the DataFrame shape or select multiple columns at once.
Why does df[0:3] select rows but df[0] raises an error?
When you pass a slice like 0:3 to a DataFrame, pandas treats it as row selection by position. A bare integer like df[0] is interpreted as a column label lookup, so it fails unless a column is literally named 0.
Should I use df.column attribute access?
Attribute access (df.age) is convenient for quick exploration, but it only works when the column name has no spaces, does not clash with a DataFrame method, and is a valid Python identifier. Bracket access df["age"] always works and is the safer habit.