How do you lookup a DataFrame by index
Working with data in Python often involves using libraries like pandas, and a fundamental data structure in pandas is the DataFrame. Think of a DataFrame like a table or a spreadsheet, with rows and columns of data. When you need to access specific pieces of information within this table, knowing how to look up data by its index is a crucial skill. This article will guide you through the various methods to do just that, making your data manipulation tasks much smoother.
In pandas, each row and each column in a DataFrame has an identifier. The index refers to these row identifiers. By default, when you create a DataFrame, pandas assigns a numerical index starting from 0. However, you can also set a specific column as your index, which can be incredibly useful for making your data more readable and your lookups more intuitive.
Understanding DataFrame Indexing
Before diving into the "how-to," it's important to grasp the two main ways to access data in pandas:
- Label-based indexing: This is when you use the actual labels (names) of your index or columns to select data.
- Integer-based indexing: This is when you use the numerical position of your rows or columns (like in a list) to select data.
Pandas provides specific tools for each of these, and understanding the difference is key to avoiding common mistakes.
Method 1: Using `.loc[]` for Label-Based Indexing
The .loc[] accessor is your go-to for label-based indexing. This means you'll use the actual index *names* (labels) to select rows and columns.
Let's imagine we have a DataFrame called sales_data:
import pandas as pd
data = {'Product': ['A', 'B', 'C', 'D'], 'Revenue': [100, 150, 120, 200], 'Quantity': [10, 15, 12, 20]}
sales_data = pd.DataFrame(data, index=['Jan', 'Feb', 'Mar', 'Apr'])
print(sales_data)
This will output:
Product Revenue Quantity
Jan A 100 10
Feb B 150 15
Mar C 120 12
Apr D 200 20
Looking up a single row by index label:
To get the data for 'Mar', you would use:
mar_data = sales_data.loc['Mar']
print(mar_data)
This will return a pandas Series containing the data for the 'Mar' row:
Product C Revenue 120 Quantity 12 Name: Mar, dtype: object
Looking up multiple rows by index labels:
You can pass a list of index labels to .loc[] to retrieve multiple rows:
q1_sales = sales_data.loc[['Jan', 'Feb', 'Mar']]
print(q1_sales)
This will give you:
Product Revenue Quantity
Jan A 100 10
Feb B 150 15
Mar C 120 12
Looking up specific columns for a row:
You can also select specific columns for a given row label. For example, to get only the 'Revenue' for 'Apr':
apr_revenue = sales_data.loc['Apr', 'Revenue']
print(apr_revenue)
This will output:
200
Looking up multiple rows and multiple columns:
You can specify both rows and columns using lists:
specific_sales = sales_data.loc[['Jan', 'Apr'], ['Product', 'Quantity']]
print(specific_sales)
Which results in:
Product Quantity
Jan A 10
Apr D 20
Method 2: Using `.iloc[]` for Integer-Based Indexing
The .iloc[] accessor is for integer-based indexing. This means you'll use the numerical position of the rows and columns, starting from 0, just like you would with Python lists.
Using the same sales_data DataFrame:
Looking up a single row by integer position:
To get the third row (which is 'Mar' in our example), you'd use position 2:
mar_data_iloc = sales_data.iloc[2]
print(mar_data_iloc)
This will return the same Series as when we used .loc['Mar']:
Product C Revenue 120 Quantity 12 Name: Mar, dtype: object
Looking up multiple rows by integer positions:
To get the first and last rows (positions 0 and 3):
first_and_last = sales_data.iloc[[0, 3]]
print(first_and_last)
This yields:
Product Revenue Quantity
Jan A 100 10
Apr D 200 20
Using slicing with `.iloc[]`:
You can use slicing to get a range of rows. For example, to get the first two rows (positions 0 and 1):
first_two_rows = sales_data.iloc[0:2]
print(first_two_rows)
Note that, like Python slicing, the end index is exclusive.
Looking up specific columns by integer position:
To get the 'Revenue' column (which is at position 1) for the first row (position 0):
first_revenue_iloc = sales_data.iloc[0, 1]
print(first_revenue_iloc)
Output:
100
Looking up multiple rows and multiple columns by integer positions:
specific_data_iloc = sales_data.iloc[[0, 2], [0, 2]]
print(specific_data_iloc)
This will select the first and third rows, and the first and third columns:
Product Quantity
Jan A 10
Mar C 12
Method 3: Direct Indexing (Using `[]`)
You can also use square brackets [] directly on a DataFrame. However, the behavior of this method can be a bit nuanced:
- When used with a single label, it typically selects a column if the label exists as a column name.
- When used with a list of labels, it selects multiple columns.
- If you try to use it with an index label that is also a column name, it can lead to ambiguity. It's generally safer to use
.loc[]for row lookups and column selection.
Selecting a column:
revenue_column = sales_data['Revenue']
print(revenue_column)
This will return a pandas Series for the 'Revenue' column.
Selecting multiple columns:
product_and_quantity = sales_data[['Product', 'Quantity']]
print(product_and_quantity)
This will return a DataFrame with only the 'Product' and 'Quantity' columns.
When to Use Which Method?
Here's a quick guide:
- Use `.loc[]` when you want to select data based on the actual labels (names) of your index and columns. This is usually the most readable and less prone to errors when your index is meaningful.
- Use `.iloc[]` when you want to select data based on its numerical position (0-indexed). This is useful when the index labels are not important or when you're working with a DataFrame where you know the exact row/column positions.
- Use `[]` for selecting columns by name. While it can sometimes be used for row slicing, it's generally recommended to stick with
.loc[]and.iloc[]for row selections to avoid confusion.
Setting a Custom Index
Sometimes, the default numerical index isn't the most helpful. You can set one of your existing columns as the index using the set_index() method.
Let's say we have a DataFrame where 'Product' is a column:
data_with_product_as_col = {'Product': ['A', 'B', 'C', 'D'], 'Revenue': [100, 150, 120, 200], 'Quantity': [10, 15, 12, 20]}
df_no_index = pd.DataFrame(data_with_product_as_col)
df_no_index.set_index('Product', inplace=True)
print(df_no_index)
Now, 'Product' is our index:
Revenue Quantity
Product
A 100 10
B 150 15
C 120 12
D 200 20
With this new index, you can now easily use .loc[] to look up rows by product name:
product_a_data = df_no_index.loc['A']
print(product_a_data)
This will give you:
Revenue 100 Quantity 10 Name: A, dtype: int64
This demonstrates the power of having a meaningful index for easier data retrieval.
Frequently Asked Questions (FAQ)
How do I select a row by its name if the index is not numerical?
You should use the .loc[] accessor. For example, if your DataFrame `my_df` has an index with names like 'Apple', 'Banana', etc., you would use `my_df.loc['Apple']` to select the row labeled 'Apple'.
Why is it important to distinguish between `.loc[]` and `.iloc[]`?
It's crucial because they use different methods of selection: .loc[] uses labels (names), while .iloc[] uses integer positions. Using the wrong one can lead to selecting unintended data or causing errors, especially if your index labels are not integers or if you are trying to select by position.
Can I use direct square brackets `[]` to select rows by index label?
While `[]` is primarily used for column selection, it can sometimes be used for row selection if the index labels are not also column names and if pandas can unambiguously interpret your intent. However, for clear and reliable row selection by label, .loc[] is the recommended and safer method.
What happens if the index label I'm looking for doesn't exist?
If you use .loc[] with an index label that is not present in your DataFrame, pandas will raise a KeyError. For .iloc[], if you provide an integer position that is out of bounds for the DataFrame's size, you will get an IndexError.
How can I reset the index of a DataFrame to its default numerical index after setting a custom one?
You can use the .reset_index() method. If you want to keep the current index as a regular column, you can use `my_df.reset_index(inplace=True)`. If you want to discard the current index and get the default numerical index, you can use `my_df.reset_index(drop=True, inplace=True)`.

