loc: Select by Label
Use .loc[rows, columns] to select rows, columns, slices and single values by their labels — with end-inclusive slicing and boolean masks.
Watch the lesson
1 video on CodeWithMunnaX
Column selection is easy, but what about rows? "Show me MS Dhoni's row", "rows 2 to 4, only name and runs", "every player with more than 400 runs, only name and team". For combined row-and-column selection pandas gives you two dedicated tools: .loc (by label) and .iloc (by position). This topic covers .loc — the one you'll use most.
(Same players table as the previous topic: 8 players, default index 0–7.)
Definition
DataFrame.locis a label-based indexer:df.loc[row_selector, column_selector]selects rows and columns by their index labels and column names (or by a boolean mask), and its slices include both the start and the end label.
- Label-based — it looks at the names of rows and columns, not their positions. With the default index the row labels happen to be
0, 1, 2…, which is whylocandiloccan look identical at first. - row_selector, column_selector — each can be a single label, a list of labels, a slice
start:end, a boolean mask, or:(everything). The column part is optional. - End-inclusive —
loc[2:4]returns labels 2, 3 and 4. Labels are not numbers to count with, so pandas includes the label you named.
Syntax
python
df.loc[row_label] # one row -> Series
df.loc[[label1, label2]] # several rows -> DataFrame
df.loc[start:end] # slice of rows (end INCLUDED)
df.loc[rows, "col"] # rows + one column
df.loc[rows, ["col1", "col2"]] # rows + several columns
df.loc[:, "colA":"colD"] # all rows, a range of columns
df.loc[row_label, "col"] # one single value (scalar)
df.loc[boolean_mask, cols] # filter rows by a condition| Selector you can pass | Example | Result |
|---|---|---|
| single label | players.loc[0] | that row as a Series |
| list of labels | players.loc[[0, 3]] | DataFrame of those rows |
| slice of labels | players.loc[2:4] | rows 2, 3, 4 (inclusive) |
| slice with step | players.loc[::3] | every 3rd row |
: | players.loc[:, "team"] | all rows |
| boolean mask | players.loc[players["runs"] > 400] | rows where the mask is True |
Example — rows
python
print(players.loc[0]) # one row -> Series
print(players.loc[[0, 3]]) # list of labels -> DataFrame
print(players.loc[2:4]) # slice: 2, 3 AND 4text
player Virat Kohli
team RCB
role Batter
runs 741
wickets 0
strike_rate 154.7
Name: 0, dtype: object
player team role runs wickets strike_rate
0 Virat Kohli RCB Batter 741 0 154.7
3 Jasprit Bumrah MI Bowler 8 20 80.0
player team role runs wickets strike_rate
2 MS Dhoni CSK Keeper 161 0 220.5
3 Jasprit Bumrah MI Bowler 8 20 80.0
4 Hardik Pandya MI All-rounder 216 11 143.0How it works: a single label returns the row turned sideways as a Series — the column names become its index and the row label becomes its Name. Because the row mixes text and numbers, the Series dtype is object. A list of labels (even a one-item list like loc[[0]]) returns a DataFrame. The slice 2:4 included label 4.
Example — rows and columns together
python
print(players.loc[2:4, ["player", "runs"]])
print(players.loc[:, "team":"runs"].head(3))
print(players.loc[0, "player"])text
player runs
2 MS Dhoni 161
3 Jasprit Bumrah 8
4 Hardik Pandya 216
team role runs
0 RCB Batter 741
1 MI Batter 417
2 CSK Keeper 161
Virat KohliHow it works: the part before the comma picks rows, the part after picks columns. Column slices are also inclusive: "team":"runs" gives team, role and runs — every column from team up to and including runs, in table order. A single row label plus a single column name gives one plain value (a scalar).
Example — loc with real labels
loc really shines when the index holds meaningful labels. Make the player name the index (more on set_index later in this chapter):
python
p = players.set_index("player")
print(p.loc["MS Dhoni"])
print(p.loc["Rohit Sharma":"Hardik Pandya", ["team", "runs"]])text
team CSK
role Keeper
runs 161
wickets 0
strike_rate 220.5
Name: MS Dhoni, dtype: object
team runs
player
Rohit Sharma MI 417
MS Dhoni CSK 161
Jasprit Bumrah MI 8
Hardik Pandya MI 216How it works: p.loc["MS Dhoni"] reads like English — "locate MS Dhoni". The slice runs from "Rohit Sharma" to "Hardik Pandya" in the current row order (not alphabetically) and includes Hardik. Now p.loc[0] would fail with KeyError: 0 — there is no row labelled 0 any more.
Example — loc with a boolean mask
python
print(players.loc[players["runs"] > 400, ["player", "runs"]])
print(players.loc[players["team"] == "GT", "player"])text
player runs
0 Virat Kohli 741
1 Rohit Sharma 417
6 Shubman Gill 426
6 Shubman Gill
7 Rashid Khan
Name: player, dtype: strHow it works: players["runs"] > 400 creates a True/False Series (a mask); loc keeps rows where it is True and, in the same step, picks columns. This "filter rows + choose columns" pattern is the most common use of loc in real analysis — and the only correct way to modify filtered rows (see "Modifying Values Safely").
Common mistakes
- ❌ Expecting
players.loc[2:4]to return 2 rows like a Python slice. ✅locslices are end-inclusive: 3 rows (2, 3, 4). - ❌
players.loc[0:4, -1]to get the last column (Munna's notebook comment: "-1 kaam nahi kar raha"). ✅-1is not a column label; useplayers.iloc[0:5, -1]orplayers.loc[0:4, players.columns[-1]]. - ❌
p.loc[0]afterset_index("player")→KeyError: 0. ✅ Use the new labelp.loc["Virat Kohli"], or positionp.iloc[0]. - ❌
players.loc["player"]to get a column. ✅ The first selector is always rows; useplayers.loc[:, "player"]orplayers["player"].
Interview tip
Q: Why is loc slicing inclusive of the end while Python slicing is not?
A: loc works with labels, which may be strings or dates where "one before the end" has no meaning. If you ask for "Rohit Sharma":"Hardik Pandya" you clearly want Hardik included. So label slices include both endpoints; position slices (iloc, Python lists) keep the usual end-exclusive rule.
Key points to remember
- df.loc[rows, columns] selects by index labels and column names.
- One label returns a Series; a list of labels returns a DataFrame.
- loc slices include the end label — loc[2:4] gives rows 2, 3 and 4; 'team':'runs' includes runs.
- A single row label plus a single column name returns one scalar value.
- loc accepts a boolean mask for rows: df.loc[df['runs'] > 400, ['player', 'runs']].
- With a custom index (set_index), loc uses the new labels — the old 0, 1, 2 labels no longer exist.
- Negative numbers are not labels: use iloc for 'last column' style positions.
Try it yourself
loc: rows, slices and columns
loc with the player name as index
loc with a condition
Practice: scout report with loc
Revision Flashcards
Tap a card to flip it and see the answer.
