The request

From Thomas Whitaker, Head of Merchandising
Your goal
Set result to a DataFrame with the rows at positions 10 to 14 of products (the 11th to the 15th row, 5 rows) and only the columns name, category, unit_price, in that order.
- Use the
productsDataFramenot done yet - Show exactly
namecategoryunit_pricenot done yet - 5 rows, sorted as askednot done yet
Your code
Loading the editor…
Reference solution
| product_id | name | category | subcategory | unit_price |
|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
What Thomas gets, and why
DataFrames
Loaded for you as pandas DataFrames, plus pd and np.
products120 rows · 8 columns
product_idintegernamevarcharcategoryvarcharsubcategoryvarcharunit_pricedecimalunit_costdecimallaunched_ondateis_activeboolean
| product_id | name | category | subcategory | unit_price | unit_cost | launched_on | is_active |
|---|---|---|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 | NaN | 2023-09-11 | true |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 | 503.50 | 2024-05-16 | true |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 | 429.87 | 2023-05-27 | true |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 | 214.70 | 2022-12-20 | true |
| 105 | Avery Leather Bed Frame | Furniture | Beds | 449.00 | 232.70 | 2021-08-31 | true |
customers2,018 rows · 9 columns
customer_idintegerfirst_namevarcharlast_namevarcharemailvarcharcityvarcharstatevarcharsignup_datedatesegmentvarcharreferral_sourcevarchar
| customer_id | first_name | last_name | city | state | signup_date | segment | referral_source | |
|---|---|---|---|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco | CA | 2022-06-02 | consumer | search |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta | GA | 2022-06-02 | consumer | NaN |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles | CA | 2022-06-04 | business | NaN |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle | WA | 2022-06-06 | business | |
| 5 | Kyle | Jimenez | kjimenez27@yahoo.com | Indianapolis | IN | 2022-06-07 | consumer | NaN |
orders12,000 rows · 7 columns
order_idintegercustomer_idintegerorder_datetimestampstatusvarcharchannelvarcharshipping_feedecimalcoupon_codevarchar
| order_id | customer_id | order_date | status | channel | shipping_fee | coupon_code |
|---|---|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web | 0.00 | NaN |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web | 0.00 | NaN |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace | 4.99 | WELCOME10 |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace | 4.99 | WELCOME10 |
| 1005 | 82 | 2023-01-02 11:43:36 | completed | web | 0.00 | WELCOME10 |
order_items30,338 rows · 6 columns
order_item_idintegerorder_idintegerproduct_idintegerquantityintegerunit_pricedecimaldiscount_pctdecimal
| order_item_id | order_id | product_id | quantity | unit_price | discount_pct |
|---|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 | 0.00 |
| 2 | 1001 | 154 | 3 | 11.99 | 0.00 |
| 3 | 1002 | 154 | 4 | 11.99 | 0.00 |
| 4 | 1002 | 194 | 1 | 149.00 | 0.00 |
| 5 | 1002 | 193 | 2 | 19.99 | 0.00 |
returns1,673 rows · 5 columns
return_idintegerorder_item_idintegerreturned_ondatereasonvarcharrefund_amountdecimal
| return_id | order_item_id | returned_on | reason | refund_amount |
|---|---|---|---|---|
| 1 | 14 | 2023-01-10 | damaged | 83.69 |
| 2 | 42 | 2023-01-10 | wrong_item | 19.99 |
| 3 | 105 | 2023-01-14 | wrong_item | 10.99 |
| 4 | 24 | 2023-01-16 | changed_mind | 26.99 |
| 5 | 126 | 2023-01-16 | late | 20.99 |
employees60 rows · 8 columns
employee_idintegerfirst_namevarcharlast_namevarchardepartmentvarchartitlevarcharmanager_idintegerhire_datedatesalaryinteger
| employee_id | first_name | last_name | department | title | manager_id | hire_date | salary |
|---|---|---|---|---|---|---|---|
| 1 | Catherine | Hale | Executive | Chief Executive Officer | NaN | 2016-03-01 | 285000 |
| 2 | Robert | Kim | Executive | Chief Operating Officer | 1 | 2017-01-09 | 235000 |
| 3 | Megan | Collins | Sales | Head of Sales | 1 | 2018-04-16 | 172000 |
| 4 | Chloe | Gutierrez | Sales | Account Executive | 3 | 2020-01-21 | 72000 |
| 5 | Frank | Bell | Sales | Account Executive | 3 | 2023-05-21 | 73000 |
web_sessions40,000 rows · 7 columns
session_idintegercustomer_idintegerstarted_attimestampdevicevarcharsourcevarcharpages_viewedintegerconvertedboolean
| session_id | customer_id | started_at | device | source | pages_viewed | converted |
|---|---|---|---|---|---|---|
| 1 | NaN | 2023-01-01 23:02:24 | mobile | paid | 1 | false |
| 2 | 18 | 2023-01-01 15:07:49 | desktop | paid | 1 | false |
| 3 | NaN | 2023-01-01 16:55:46 | desktop | direct | 7 | false |
| 4 | NaN | 2023-01-01 12:43:28 | mobile | organic | 3 | false |
| 5 | NaN | 2023-01-01 13:06:21 | mobile | social | 2 | false |
marketing_spend3,276 rows · 3 columns
spend_datedatechannelvarcharamountdecimal
| spend_date | channel | amount |
|---|---|---|
| 2023-01-01 | paid | 492.99 |
| 2023-01-01 | 37.38 | |
| 2023-01-01 | social | 200.53 |
| 2023-01-02 | paid | 709.13 |
| 2023-01-02 | 64.13 |
The lesson
Two ways to point at rows
Every DataFrame row has a position (0 for the first row, 1 for the second, and so on) and an index label (the value shown on the left). Right after loading a CSV they are the same numbers, which hides the difference. After filtering or sorting, they’re not, and picking the wrong one gives you the wrong rows without any error.
pandas gives you one tool for each:
.ilocselects by integer position..locselects by label: index labels for rows, column names for columns.
iloc: positions
.iloc works like Python list slicing, end excluded:
products.iloc[0] # first row, as a Series
products.iloc[10:15] # positions 10, 11, 12, 13, 14
products.iloc[-3:] # last three rows
products.iloc[10:15, 1:4] # same rows, columns at positions 1 to 3
Column positions are fragile, because adding a column shifts them. So in practice you’ll usually
pick rows with .iloc and columns by name in a second step:
products.iloc[10:15][["name", "category", "unit_price"]]
loc: labels
.loc[rows, columns] takes labels for both:
products.loc[10:14, ["name", "category", "unit_price"]]
products.loc[:, ["name", "unit_price"]] # all rows, two columns
products.loc[3, "name"] # one cell
Important: label slices in .loc include the end. products.loc[10:14] returns five rows,
labels 10 through 14. That’s because labels aren’t always numbers (they can be dates or names),
and “from Monday to Friday” should include Friday.
When labels and positions drift apart
Sort the products by price and the index labels travel with their rows:
by_price = products.sort_values("unit_price", ascending=False)
by_price.iloc[0] # the most expensive product (position 0)
by_price.loc[0] # the row whose label is 0, wherever it ended up
If you meant “the first row”, iloc is right and loc silently gives you a different product.
That’s why you shouldn’t rely on index labels after reshaping the data unless you set them on
purpose, for example with reset_index(drop=True) or set_index("product_id").
Using a meaningful index
You can make an ID column the index, then look rows up by ID:
by_id = products.set_index("product_id")
by_id.loc[102, "name"] # 'Alder Oak Coffee Table'
by_id.loc[[101, 102, 103]] # several products at once
This is the pandas version of a dictionary lookup, and it’s handy when you need the same rows repeatedly.
The one-step rule
When you want to change values, do it in one .loc call:
df.loc[row_selector, "column"] = value. Chaining two selections, such as
df[df["x"] > 0]["column"] = value, may modify a temporary copy instead of df. Modern pandas
warns about this (and pandas 3 never modifies the original that way), so form the habit now.
Common mistakes
- Expecting
products.loc[10:15]to give five rows. It gives six, because.locincludes the end. - Using
.loc[0]after a sort to get the top row. - Mixing positions and names in one
.iloccall:products.iloc[10:15, "name"]fails, since.iloconly accepts integers.
Lantern lit!
Practised: locilocindexrow selection
- Solved
- No hints
- First run
