EduFlow

Section 2 · pandas Foundations

Selecting with loc and iloc

18 min3 min readPython exercise

Lesson at a glance

Select columns with square brackets, then pick exact rows and columns with .loc (by label) and .iloc (by position), and know which one to use after sorting or filtering.

  1. 1Columns first
  2. 2.loc: select by label
  3. 3.iloc: select by position
  4. 4Why the difference matters: sorting

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

Cartwheel’s product manager asks: “What do we charge for the coffee grinder, product 7?” and “Show me the five priciest products with their costs.” Both are selection problems: out of a table, get exactly these rows and these columns. pandas gives you two precise tools for that, .loc and .iloc, and most beginner confusion comes from mixing them up. The rule is short: .loc uses labels, .iloc uses positions.

Columns first

Square brackets on a DataFrame select columns:

Python
import pandas as pd

products = pd.read_csv("products.csv", index_col="product_id")
print(products["name"].head(3))                    # one column: a Series
print(products[["name", "unit_price"]].head(3))    # a list of columns: a DataFrame

index_col="product_id" made the product id the row index instead of the default 0, 1, 2. That single choice makes everything below read naturally: product 7 is the row labelled 7. On an already-loaded DataFrame, products.set_index("product_id") does the same thing and returns a new DataFrame.

You will also see products.unit_price in tutorials. Dot access works for simple names, but it fails for names with spaces, and it quietly returns the wrong thing when a column shares its name with a DataFrame method: a column called count or size is shadowed by the method of the same name. Square brackets always work, so make them your default.

The double brackets are not a typo. The outer pair is the selection; the inner pair is a Python list of column names. One column in a list still gives a DataFrame.

.loc: select by label

.loc[rows, columns] takes row labels first, then column labels:

Python
import pandas as pd

products = pd.read_csv("products.csv", index_col="product_id")
print(products.loc[7, "unit_price"])                       # one value
print(products.loc[7])                                     # one row, as a Series
print(products.loc[[11, 21, 37], ["name", "unit_price"]])  # chosen rows and columns
print(products.loc[1:3, "name":"unit_price"])              # label slices

Lists pick exactly the labels you name, in that order. Slices take a range of labels, and here is the part to remember: .loc slices include the end label. products.loc[1:3] returns products 1, 2 and 3. The column slice "name":"unit_price" likewise includes unit_price. It makes sense once you think in labels: “from product 1 to product 3” includes product 3.

Ask for a label that does not exist, say products.loc[99], and you get KeyError: 99. That is a feature: a wrong id should fail loudly, not return an empty result you might paste into a report. When a missing label is a normal possibility, test first with 99 in products.index, which returns True or False.

.loc is also the one tool you use for assigning values, as you will see in Lesson 2.5: products.loc[7, "unit_price"] = 135.45 changes exactly one cell.

.iloc: select by position

.iloc ignores labels and counts positions from 0, with slices that exclude the end, exactly like Python lists:

Python
import pandas as pd

products = pd.read_csv("products.csv", index_col="product_id")
print(products.iloc[0])            # first row, whatever its label
print(products.iloc[:3, :2])       # first three rows, first two columns
print(products.iloc[-1]["name"])   # last row's name

Use .iloc when position is what you mean: “the first five”, “the last row”, “every other row”. Use .loc when identity is what you mean: “product 7”, “the unit_price column”.

A table sorted by price with index labels 21, 22, 41, 1 in that order and positions 0 to 3. loc[1] points to the row labelled 1, which sits at position 3. iloc[0] points to the row at position 0, which is labelled 21. position label name (sorted by price) 0 21 Standing Desk 140cm 1 22 Ergonomic Task Chair 2 41 Adjustable Dumbbells ... 1 Cast Iron Skillet 26cm .iloc[0] .loc[1]
.loc finds rows by label, .iloc by position

Why the difference matters: sorting

Sort the products by price and the two tools part ways:

Python
import pandas as pd

products = pd.read_csv("products.csv", index_col="product_id")
by_price = products.sort_values("unit_price", ascending=False)
print(by_price.iloc[:3][["name", "unit_price"]])   # the three priciest
print(by_price.loc[1, "name"])                     # still the skillet

Sorting moves rows but each row keeps its label. by_price.iloc[0] is now the Standing Desk, the most expensive item at 449 dollars; by_price.loc[1] is still the Cast Iron Skillet, wherever it ended up. Filtering behaves the same way: after you keep only some rows, labels have gaps (1, 4, 9…), and positions are renumbered 0, 1, 2. If you ever want fresh 0-to-n labels, reset_index(drop=True) gives them back.

In the exercise you answer the product manager’s questions with .loc and .iloc. Next you select rows by a condition instead of by label: “all orders over 200 dollars”, “all web orders in November”.

Key takeaways

  • df["col"] returns one column as a Series; df[["a", "b"]] (a list inside the brackets) returns a DataFrame.
  • .loc[rows, columns] selects by label, and its slices include the end label.
  • .iloc[rows, columns] selects by position, and its slices exclude the end, like Python lists.
  • After sorting, labels stay attached to their rows, so .loc still finds the same product while .iloc[0] finds whatever is now first.
  • Set a meaningful index such as product_id with index_col= or set_index() so .loc reads like a lookup.

Further reading

Check your understanding

4 questions

Question 1 of 4

products is indexed by product_id (1 to 48). How many rows does products.loc[1:5] return?

Question 2 of 4

You sort products by price, highest first. Which expression returns the most expensive product’s row?

Question 3 of 4

What does products[["name"]] return, compared to products["name"]?

Question 4 of 4

Which line gets the unit price of product 7 in a single, unambiguous step?

Practice

Python exercise

Answer the product manager

products is loaded with product_id as its index. Set grinder_price to the unit price of product 7. Set subset to products 11, 21 and 37 with only the name and unit_cost columns, in that order. Set top5 to the five most expensive products with all their columns, most expensive first. Set cheapest_name to the name of the cheapest product.

Data files: products.csv

import pandas as pd

products = pd.read_csv("products.csv", index_col="product_id")

# 1. Unit price of product 7 (a single number)
grinder_price = None

# 2. Rows for products 11, 21 and 37, with only the name and unit_cost columns
subset = None

# 3. The five most expensive products (all columns), most expensive first
top5 = None

# 4. The name of the cheapest product
cheapest_name = None
Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

(no output)

Checks

  • grinder_price is 129
  • subset has products 11, 21, 37 and the columns name, unit_cost
  • top5 holds the five highest prices in descending order
  • top5 keeps all the columns
  • cheapest_name is Jump Rope Pro