You have a Pandas DataFrame containing employee records. You want to select the row labelled E102, but instead, you accidentally select the second row.
The code runs without any obvious warning, yet the result is wrong.
This usually happens when someone understands how to create a DataFrame but is still confused about the difference between loc[] and iloc[].
Both are used to select rows and columns from a Pandas DataFrame. The main difference is simple:
loc[]selects data using row and column labels.iloc[]selects data using numerical positions.
This one distinction controls how Pandas interprets everything written inside the brackets.
According to the official Pandas documentation, loc[] is primarily label-based, while iloc[] is based on integer positions from 0 to length - 1.
Aspiring for a career in Data and Business Analytics? Begin your journey with a Data and Business Analytics Certificate from Jobaaj Learnings.
What Is a Pandas DataFrame?
A Pandas DataFrame is a two-dimensional data structure that stores information in rows and columns.
It looks similar to an Excel worksheet or a table in a database.
Consider the following example:
import pandas as pd
data = {
"Name": ["Aman", "Priya", "Rahul", "Sneha"],
"Department": ["Sales", "Finance", "IT", "Marketing"],
"Salary": [45000, 52000, 60000, 48000]
}
df = pd.DataFrame(
data,
index=["E101", "E102", "E103", "E104"]
)
print(df)
Output:
Name Department Salary
E101 Aman Sales 45000
E102 Priya Finance 52000
E103 Rahul IT 60000
E104 Sneha Marketing 48000
This DataFrame contains two different ways to identify a row.
The first row has:
- Label:
E101 - Position:
0
The second row has:
- Label:
E102 - Position:
1
This difference between a label and a position is the foundation of loc[] and iloc[].
What Is loc[] in Pandas?
loc[] is used to select rows and columns by their labels or names.
The name can be understood as label location.
Its basic syntax is:
df.loc[row_label, column_label]
Pandas officially defines loc[] as an accessor for selecting groups of rows and columns by labels or Boolean arrays.
Selecting One Row With loc[]
To select the employee with the row label E102, write:
df.loc["E102"]
Output:
Name Priya
Department Finance
Salary 52000
Name: E102, dtype: object
Pandas searches for the exact label E102.
It does not count rows from the top.
Selecting One Row and One Column With loc[]
To retrieve Priya’s salary:
df.loc["E102", "Salary"]
Output:
52000
Here:
"E102"is the row label."Salary"is the column label.
Selecting Multiple Rows With loc[]
Pass a list of row labels:
df.loc[["E101", "E103"]]
Output:
Name Department Salary
E101 Aman Sales 45000
E103 Rahul IT 60000
Notice the double square brackets.
The outer brackets belong to loc[], while the inner brackets contain the list of labels.
Selecting Multiple Rows and Columns With loc[]
df.loc[
["E101", "E103"],
["Name", "Salary"]
]
Output:
Name Salary
E101 Aman 45000
E103 Rahul 60000
This selects two named rows and two named columns.
What Is iloc[] in Pandas?
iloc[] selects rows and columns according to their integer positions.
The letter i in iloc can be remembered as integer location.
Its basic syntax is:
df.iloc[row_position, column_position]
Pandas defines iloc[] as a purely integer-location-based indexer. Positions begin at 0 and continue up to length - 1.
Selecting One Row With iloc[]
To select the second row:
df.iloc[1]
Output:
Name Priya
Department Finance
Salary 52000
Name: E102, dtype: object
Python uses zero-based indexing, so:
- Position
0means the first row. - Position
1means the second row. - Position
2means the third row.
Selecting One Row and One Column With iloc[]
To retrieve the value from the second row and third column:
df.iloc[1, 2]
Output:
52000
Here:
1means the second row.2means the third column.
Selecting Multiple Rows With iloc[]
df.iloc[[0, 2]]
Output:
Name Department Salary
E101 Aman Sales 45000
E103 Rahul IT 60000
The row labels remain visible in the result, but they were not used for selection.
Pandas selected the first and third rows based on their positions.
Selecting Multiple Rows and Columns With iloc[]
df.iloc[
[0, 2],
[0, 2]
]
Output:
Name Salary
E101 Aman 45000
E103 Rahul 60000
The first list selects row positions. The second selects column positions.
loc vs iloc: Main Differences
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The easiest memory trick is:
loc = labels
iloc = integer positions
The Most Important Difference: Labels vs Positions
Consider a DataFrame with numerical row labels:
data = {
"Name": ["Aman", "Priya", "Rahul"],
"Marks": [75, 82, 91]
}
students = pd.DataFrame(
data,
index=[10, 20, 30]
)
print(students)
Output:
Name Marks
10 Aman 75
20 Priya 82
30 Rahul 91
Now compare the following statements:
students.loc[20]
This selects the row whose label is 20.
students.iloc[1]
This selects the row at position 1, which is the second row.
Both return Priya’s record, but they reach it differently.
Now consider:
students.loc[1]
This raises a KeyError because no row is labelled 1.
But:
students.iloc[1]
works because a second row exists at position 1.
The official Pandas documentation specifically notes that an integer passed to loc[] is treated as a label, never automatically as a row position.
Slicing With loc[] and iloc[]
Slicing is one of the areas where beginners make the most mistakes.
The end point behaves differently in loc[] and iloc[].
Slicing With loc[] Is Inclusive
Consider:
df.loc["E101":"E103"]
Output:
Name Department Salary
E101 Aman Sales 45000
E102 Priya Finance 52000
E103 Rahul IT 60000
Both E101 and E103 are included.
Unlike standard Python slicing, label-based slices in loc[] include both the start and stop labels when those labels exist.
Slicing With iloc[] Excludes the End Position
df.iloc[0:3]
Output:
Name Department Salary
E101 Aman Sales 45000
E102 Priya Finance 52000
E103 Rahul IT 60000
Positions 0, 1 and 2 are selected.
Position 3 is excluded.
This follows normal Python slicing rules:
start included
stop excluded
Direct Slicing Comparison
df.loc["E101":"E103"]
Includes:
E101, E102 and E103
df.iloc[0:3]
Includes:
positions 0, 1 and 2
Both produce the same rows in this example, but their slicing rules are different.
Selecting Columns With loc[]
To select all rows and one named column:
df.loc[:, "Name"]
Output:
E101 Aman
E102 Priya
E103 Rahul
E104 Sneha
Name: Name, dtype: object
The colon means all rows.
To select multiple columns:
df.loc[:, ["Name", "Salary"]]
Output:
Name Salary
E101 Aman 45000
E102 Priya 52000
E103 Rahul 60000
E104 Sneha 48000
To select a range of named columns:
df.loc[:, "Name":"Salary"]
The ending column is included because loc[] uses inclusive label slicing.
Selecting Columns With iloc[]
To select all rows and the first column:
df.iloc[:, 0]
To select the first and third columns:
df.iloc[:, [0, 2]]
Output:
Name Salary
E101 Aman 45000
E102 Priya 52000
E103 Rahul 60000
E104 Sneha 48000
To select the first two columns:
df.iloc[:, 0:2]
The column at position 2 is excluded.
Filtering a DataFrame With loc[]
One of the most common uses of loc[] is conditional filtering.
Suppose you want employees earning more than ₹50,000:
df.loc[df["Salary"] > 50000]
Output:
Name Department Salary
E102 Priya Finance 52000
E103 Rahul IT 60000
The expression:
df["Salary"] > 50000
produces a Boolean Series:
E101 False
E102 True
E103 True
E104 False
loc[] keeps the rows where the condition is True.
Filtering Rows and Selecting Columns Together
df.loc[
df["Salary"] > 50000,
["Name", "Salary"]
]
Output:
Name Salary
E102 Priya 52000
E103 Rahul 60000
This is one of the cleanest ways to filter rows while returning only the required columns.
Using Multiple Conditions With loc[]
To select Finance or IT employees earning at least ₹50,000:
df.loc[
(df["Salary"] >= 50000)
& (df["Department"].isin(["Finance", "IT"]))
]
Each condition should be placed inside parentheses.
Use:
&for AND|for OR~for NOT
The Pandas documentation also shows Boolean conditions as a standard use of loc[].
Can iloc[] Be Used for Boolean Filtering?
Yes, but there is an important limitation.
iloc[] works with a Boolean array whose length matches the selected axis.
For example:
mask = [False, True, True, False]
df.iloc[mask]
This selects the second and third rows.
However, directly passing an indexed Boolean Series to iloc[] may raise an error because iloc[] works positionally and does not perform the same label alignment as loc[].
Use a NumPy array when positional Boolean filtering is required:
mask = (df["Salary"] > 50000).to_numpy()
df.iloc[mask]
The official indexing guide explains that loc[] supports aligned Boolean Series, while iloc[] expects a Boolean array rather than an indexed Boolean Series.
For everyday conditional filtering, loc[] is usually clearer.
Selecting the First and Last Rows With iloc[]
Because iloc[] supports positional indexing, it is convenient for selecting rows from the beginning or end.
First Row
df.iloc[0]
Last Row
df.iloc[-1]
Last Two Rows
df.iloc[-2:]
First Three Rows
df.iloc[:3]
Negative positions work in the same way as ordinary Python sequences.
This makes iloc[] useful when the order matters but row labels do not.
Selecting Alternate Rows and Columns
You can add a step value while slicing.
Every Second Row
df.iloc[::2]
This selects positions 0, 2, 4 and so on.
Rows in Reverse Order
df.iloc[::-1]
Every Second Column
df.iloc[:, ::2]
These operations are positional, so iloc[] is usually the natural choice.
Updating Values With loc[]
loc[] is not limited to reading data. It can also be used to update values safely.
Suppose you want to increase Rahul’s salary:
df.loc["E103", "Salary"] = 65000
To update several rows based on a condition:
df.loc[df["Salary"] < 50000, "Salary"] = 50000
This changes the salary of every employee currently earning below ₹50,000.
Creating a New Column With loc[]
df.loc[:, "Status"] = "Active"
Updating Values Based on Conditions
df.loc[df["Salary"] >= 55000, "Level"] = "Senior"
df.loc[df["Salary"] < 55000, "Level"] = "Junior"
Using loc[] for conditional updates is usually clearer and safer than chained indexing.
Updating Values With iloc[]
iloc[] can also update values by position.
df.iloc[0, 2] = 47000
This updates the value at:
- First row
- Third column
You can update an entire column position:
df.iloc[:, 2] = df.iloc[:, 2] + 2000
This adds ₹2,000 to every value in the third column.
The code works, but label-based updates are often easier to understand when column names are available.
Compare:
df.iloc[:, 2] = df.iloc[:, 2] + 2000
with:
df.loc[:, "Salary"] = df["Salary"] + 2000
The second statement makes the business meaning more obvious.
Why Chained Indexing Should Be Avoided
A beginner may write:
df[df["Salary"] < 50000]["Salary"] = 50000
This is unreliable because it may operate on an intermediate object rather than updating the original DataFrame as intended.
A clearer approach is:
df.loc[df["Salary"] < 50000, "Salary"] = 50000
The row condition and target column are handled in one explicit operation.
For production data work, explicit accessors such as loc[] and iloc[] are preferable to ambiguous indexing patterns. The official Pandas guide recommends using its dedicated data-access methods for more controlled indexing.
Difference in Error Types
loc[] and iloc[] can produce different errors because they search for different things.
loc[] and KeyError
df.loc["E999"]
This raises:
KeyError
The label E999 does not exist.
The Pandas documentation states that loc[] raises a KeyError when requested labels are not found.
iloc[] and IndexError
df.iloc[10]
This raises:
IndexError
The DataFrame does not contain a row at position 10.
Pandas states that iloc[] raises an IndexError for an out-of-bounds position, except when an out-of-range slice is used.
Out-of-Range Slices With iloc[]
This usually does not raise an error:
df.iloc[1:100]
Pandas simply returns all available rows from position 1 onward.
This matches normal Python and NumPy slicing behaviour.
Single Brackets vs Double Brackets
The number of brackets can change the result type.
Selecting a Single Row
df.loc["E101"]
Returns a Series.
df.loc[["E101"]]
Returns a DataFrame.
The same pattern applies to iloc[]:
df.iloc[0]
Returns a Series.
df.iloc[[0]]
Returns a DataFrame.
This matters when the selected data will be passed to another function that expects a DataFrame.
loc[] and iloc[] With Default Indexes
Suppose no custom index is assigned:
df = pd.DataFrame({
"Name": ["Aman", "Priya", "Rahul"],
"Marks": [75, 82, 91]
})
The index will be:
0, 1, 2
In this case:
df.loc[1]
and:
df.iloc[1]
return the same row.
However, they still do not mean the same thing.
df.loc[1]searches for the label1.df.iloc[1]selects the second row.
The difference becomes visible after changing or rearranging the index.
Understanding loc[] After Sorting
Consider:
df = pd.DataFrame({
"Name": ["Aman", "Priya", "Rahul"],
"Marks": [75, 82, 91]
}, index=[101, 102, 103])
df = df.sort_values("Marks", ascending=False)
The new row order is:
103 Rahul
102 Priya
101 Aman
Now:
df.loc[101]
selects Aman because his row label is 101.
But:
df.iloc[0]
selects Rahul because Rahul currently occupies the first position.
This example shows why loc[] is stable around labels, while iloc[] depends on the present order of the DataFrame.
Using Date Labels With loc[]
loc[] is especially useful for time-series data.
sales = pd.DataFrame(
{
"Revenue": [12000, 15000, 17000, 14000]
},
index=pd.to_datetime([
"2026-01-01",
"2026-01-02",
"2026-01-03",
"2026-01-04"
])
)
Select one date:
sales.loc["2026-01-02"]
Select a date range:
sales.loc["2026-01-02":"2026-01-04"]
The ending date is included.
Label-based selection makes loc[] a natural option for DataFrames indexed by dates, employee IDs, order IDs or other meaningful identifiers.
Using loc[] With Column Conditions
loc[] can also filter columns using a Boolean condition.
Consider:
scores = pd.DataFrame({
"Maths": [80, 70, 90],
"English": [60, 75, 85],
"Science": [88, 79, 92]
})
To select columns whose average is above 80:
scores.loc[:, scores.mean() > 80]
The row section uses : to select every row.
The column section uses a Boolean condition.
Using iloc[] in Machine Learning Workflows
iloc[] is commonly useful when a dataset follows a fixed positional structure.
Suppose the first four columns are features and the last column is the target:
X = df.iloc[:, 0:4]
y = df.iloc[:, -1]
Here:
Xcontains all rows and the first four columns.ycontains all rows and the last column.
This approach is concise, but it depends on column order.
When column names are known, label-based selection may be more readable:
X = df.loc[:, ["Age", "Income", "Experience", "Score"]]
y = df.loc[:, "Purchased"]
The better choice depends on whether column identity or column position is more important.
When Should You Use loc[]?
Use loc[] when:
- Rows have meaningful labels.
- You know the column names.
- You need conditional filtering.
- You want to update rows based on a condition.
- You are working with dates or identifiers.
- Code readability is important.
- You want label-based slicing.
- Boolean Series alignment is useful.
Examples:
df.loc["E102"]
df.loc[:, ["Name", "Salary"]]
df.loc[df["Salary"] > 50000]
df.loc[df["Department"] == "IT", "Salary"] = 65000
When Should You Use iloc[]?
Use iloc[] when:
- You need the first, second or last row.
- Selection depends on numerical order.
- Column labels are unknown or unnecessary.
- You want Python-style positional slicing.
- You need every second row or column.
- You are dividing a dataset by column position.
- You want to select rows after sorting based on their current order.
Examples:
df.iloc[0]
df.iloc[-1]
df.iloc[0:3]
df.iloc[:, [0, 2]]
loc[] vs iloc[] Practical Examples
Select the Third Row
Using position:
df.iloc[2]
Using label:
df.loc["E103"]
Select Name and Salary Columns
Using labels:
df.loc[:, ["Name", "Salary"]]
Using positions:
df.iloc[:, [0, 2]]
Select First Two Rows
Using positions:
df.iloc[0:2]
Using labels:
df.loc["E101":"E102"]
Select Employees With Salary Above ₹50,000
df.loc[df["Salary"] > 50000]
Select the Last Two Rows
df.iloc[-2:]
Retrieve a Single Value
Using labels:
df.loc["E102", "Salary"]
Using positions:
df.iloc[1, 2]
What Are at[] and iat[]?
Pandas also provides at[] and iat[] for accessing one scalar value.
at[] Uses Labels
df.at["E102", "Salary"]
iat[] Uses Positions
df.iat[1, 2]
The relationship is:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pandas recommends at[] or iat[] when only one value needs to be retrieved or changed.
Common Mistakes With loc[] and iloc[]
Mistake 1: Passing Column Names to iloc[]
Incorrect:
df.iloc[:, ["Name", "Salary"]]
iloc[] expects integer positions, not column names.
Correct:
df.iloc[:, [0, 2]]
Or:
df.loc[:, ["Name", "Salary"]]
Mistake 2: Passing Positions to loc[] Without Checking Labels
df.loc[0]
This does not automatically mean the first row.
It searches for a row whose label is 0.
For the first row, use:
df.iloc[0]
Mistake 3: Forgetting the Inclusive loc[] Slice
df.loc["E101":"E103"]
This includes E103.
Students often expect standard Python behaviour and assume the last label will be excluded.
Mistake 4: Forgetting the Exclusive iloc[] Slice
df.iloc[0:3]
This does not include position 3.
It selects positions 0, 1 and 2.
Mistake 5: Mixing Labels and Positions
Incorrect:
df.loc["E102", 2]
This searches for a column labelled 2.
Correct label-based version:
df.loc["E102", "Salary"]
Correct position-based version:
df.iloc[1, 2]
Mistake 6: Using an Indexed Boolean Series With iloc[]
Potentially incorrect:
df.iloc[df["Salary"] > 50000]
Better:
df.loc[df["Salary"] > 50000]
Or convert the condition to an array:
df.iloc[(df["Salary"] > 50000).to_numpy()]
Mistake 7: Relying on Positions After Column Reordering
Suppose salary is originally the third column:
df.iloc[:, 2]
If someone later rearranges the DataFrame, position 2 may represent another column.
Using:
df.loc[:, "Salary"]
is often safer when the column identity matters.
Is loc[] Faster Than iloc[]?
The speed difference is usually not important for normal analysis.
The correct selection method matters more than a small performance difference.
Use:
loc[]when your logic depends on labels.iloc[]when your logic depends on positions.at[]oriat[]when repeatedly accessing one scalar value.
Do not choose an accessor only because it appears shorter.
Readable and correct code is more valuable than saving a small amount of execution time in ordinary DataFrame operations.
Why loc[] and iloc[] Matter for Data Careers
Data Analysts, Business Analysts, Data Scientists and Python Developers regularly work with tabular data.
Understanding loc[] and iloc[] helps with:
- Data cleaning
- Row filtering
- Feature selection
- Updating incorrect values
- Building reports
- Preparing machine learning data
- Analysing time-series records
- Creating reusable data pipelines
These concepts also appear frequently in Python and Pandas interviews.
An interviewer may provide a DataFrame and ask you to:
- Select particular rows.
- Filter employees by salary.
- Retrieve the last column.
- Update values conditionally.
- Explain inclusive and exclusive slicing.
- Compare
loc[],iloc[],at[]andiat[].
Memorising syntax is not enough. You should be able to explain why a particular accessor is appropriate.
Pandas loc and iloc Interview Questions
What Does loc[] Stand For?
It can be remembered as label location.
It selects rows and columns using labels.
What Does iloc[] Stand For?
It means integer location.
It selects rows and columns using zero-based positions.
Does loc[] Include the Ending Label?
Yes. A label slice such as:
df.loc["A":"C"]
includes both A and C when those labels exist.
Does iloc[] Include the Ending Position?
No. A slice such as:
df.iloc[0:3]
includes positions 0, 1 and 2.
Can loc[] Use Boolean Conditions?
Yes.
df.loc[df["Salary"] > 50000]
is a standard conditional-filtering pattern.
Can iloc[] Use Negative Numbers?
Yes.
df.iloc[-1]
selects the last row.
What Error Does loc[] Raise for a Missing Label?
It usually raises a KeyError.
What Error Does iloc[] Raise for an Invalid Position?
It usually raises an IndexError.
Practice Exercise
Create the following DataFrame:
import pandas as pd
data = {
"Student": ["Ankit", "Meera", "Rohan", "Kavya", "Zoya"],
"Course": ["Python", "SQL", "Power BI", "Python", "Excel"],
"Score": [78, 85, 72, 91, 88]
}
students = pd.DataFrame(
data,
index=["S01", "S02", "S03", "S04", "S05"]
)
Try completing these tasks:
- Select the row labelled
S03. - Select the third row by position.
- Select the
StudentandScorecolumns. - Select the first three rows.
- Select students scoring above 80.
- Select the final row.
- Change the score of
S01to 82. - Select every second row.
Solutions
# 1. Row labelled S03
students.loc["S03"]
# 2. Third row by position
students.iloc[2]
# 3. Student and Score columns
students.loc[:, ["Student", "Score"]]
# 4. First three rows
students.iloc[:3]
# 5. Scores above 80
students.loc[students["Score"] > 80]
# 6. Final row
students.iloc[-1]
# 7. Update S01 score
students.loc["S01", "Score"] = 82
# 8. Every second row
students.iloc[::2]
Quick Summary of loc[] and iloc[]
Use loc[] when you know labels:
df.loc[row_label, column_label]
Use iloc[] when you know positions:
df.iloc[row_position, column_position]
Remember:
loc = label-based
iloc = integer position-based
Also remember the slicing rule:
loc includes the ending label
iloc excludes the ending position
Conclusion
The difference between loc[] and iloc[] is not about which one is better.
They solve different selection problems.
loc[] is the better choice when your analysis depends on meaningful names, IDs, dates, conditions or column labels. iloc[] is better when your analysis depends on row order, column order or numerical positions.
Before writing the code, ask one question:
Am I selecting this data because of what it is called, or because of where it appears?
Use loc[] when the answer is what it is called.
Use iloc[] when the answer is where it appears.
Ready to Take the Next Step in Your Career? Apply Now!
Categories

