Once your data is in a DataFrame, the very next skill is pulling out exactly the rows and columns you want. Pandas gives you two precise tools for this: loc and iloc. They look almost identical but answer different questions, and mixing them up is one of the most common early pandas bugs. This tutorial makes the distinction stick.
The one-line summary: loc selects by label (the index values and column names you can see), while iloc selects by integer position (counting from 0). Everything else follows from that single difference.
Setting up a sample DataFrame
To see labels clearly, we will use a meaningful index rather than the default 0, 1, 2. This is small illustrative sample data:
import pandas as pd
df = pd.DataFrame(
{
"city": ["Hyderabad", "Hyderabad", "Bengaluru", "Pune", "Bengaluru"],
"age": [24, 31, 28, 45, 22],
"salary": [48000, 65000, 52000, 90000, 47000],
},
index=["Aarti", "Bhaskar", "Chitra", "Devan", "Esha"],
)
print(df)
city age salary
Aarti Hyderabad 24 48000
Bhaskar Hyderabad 31 65000
Chitra Bengaluru 28 52000
Devan Pune 45 90000
Esha Bengaluru 22 47000
Now the row labels are names, which makes the label-versus-position difference obvious.
loc: selecting by label
loc uses the labels you see. Give it a row label to get that row as a Series:
print(df.loc["Chitra"])
city Bengaluru
age 28
salary 52000
Name: Chitra, dtype: object
Add a comma and a column label to narrow to a single value, or pass lists to select several rows and columns:
print(df.loc["Chitra", "salary"])
print(df.loc[["Aarti", "Devan"], ["age", "salary"]])
52000
age salary
Aarti 24 48000
Devan 45 90000
A subtle but important rule: loc slices are inclusive of the endpoint. df.loc["Aarti":"Chitra"] returns Aarti, Bhaskar and Chitra, the endpoint included, which is different from normal Python slicing.
iloc: selecting by position
iloc ignores the labels entirely and counts positions from 0, exactly like list indexing:
print(df.iloc[2]) # third row
print(df.iloc[0, 1]) # first row, second column
print(df.iloc[0:2]) # first two rows, endpoint excluded
city Bengaluru
age 28
salary 52000
Name: Chitra, dtype: object
24
city age salary
Aarti Hyderabad 24 48000
Bhaskar Hyderabad 31 65000
Note the contrast with loc: df.iloc[0:2] returns two rows (positions 0 and 1), because iloc slicing excludes the endpoint like standard Python. This inclusive-versus-exclusive difference is the number one thing to remember.
iloc shines for positional jobs: df.iloc[:5] for the first five rows, df.iloc[-1] for the last row, df.iloc[:, -1] for the last column regardless of its name.
Selecting rows and columns together
Both accessors take the form [rows, columns]. The comma is what turns a one-dimensional selection into a two-dimensional one:
# label-based: matching rows, chosen columns
print(df.loc["Bhaskar":"Devan", ["age", "salary"]])
age salary
Bhaskar 31 65000
Chitra 28 52000
Devan 45 90000
To select all rows but only some columns, put a colon in the row slot: df.loc[:, ["age", "salary"]].
loc with conditions: the analyst's favourite
The most powerful use of loc combines a boolean condition for rows with a column list. This selects only the rows that satisfy a test, and optionally only the columns you want:
print(df.loc[df["salary"] > 50000, ["city", "salary"]])
city salary
Bhaskar Hyderabad 65000
Chitra Bengaluru 52000
Devan Pune 90000
The condition df["salary"] > 50000 produces a boolean Series, and loc keeps the rows where it is True. This pattern is the bridge to full row filtering, covered in its own tutorial.
Practical usage
In day-to-day analysis, loc with a condition is how you isolate the slice of data a question is about: customers above a spend threshold, transactions in a date range, rows for one region. iloc is more of a utility, used to peek at the first or last few rows, grab a column by position when names are awkward, or step through data programmatically. Knowing which accessor fits keeps your code readable and correct.
Common mistakes
- Using
ilocwith labels orlocwith positions.df.iloc["Chitra"]raises an error because iloc wants an integer;df.loc[2]fails unless 2 is an actual index label. Match the accessor to what you are passing. - Forgetting the inclusive
locslice.df.loc[0:2]returns three rows, not two. If you expect Python-style exclusive slicing, useiloc. - Chained indexing for assignment. Writing
df[df["age"] > 30]["salary"] = 0may fail silently with a SettingWithCopyWarning. Use a singleloccall instead:df.loc[df["age"] > 30, "salary"] = 0. - Dropping the comma.
df.loc[["age"]]treats the list as row labels, not columns. To select columns you still need the row slot:df.loc[:, ["age"]].
In interviews
"What is the difference between loc and iloc?" is a near-guaranteed pandas question. The complete answer names three points: loc is label-based while iloc is position-based; loc slices include the endpoint while iloc slices exclude it; and both accept a rows-and-columns pair separated by a comma. Interviewers may hand you a DataFrame and ask you to select a specific subset, so practise translating a plain-English request into the right accessor. Mentioning the SettingWithCopyWarning and using df.loc[rows, cols] = value for assignment shows real depth.
Where this fits in your learning path
Precise selection is the skill that unlocks everything downstream in the data analytics path. It builds directly on the structures from Series and DataFrame and leads straight into filtering rows, where boolean conditions take centre stage. If you are just starting, revisit the pandas introduction first. Confident indexing is a baseline expectation for the data analyst role.
Frequently Asked Questions
What is the difference between loc and iloc in pandas?
Does loc include the last element in a slice?
How do I select specific rows and columns at once?
Can I use loc with a boolean condition?
When should I use iloc instead of loc?
Want to Build Your Career in Data Analytics with AI?
Join CodeBegun and train with working industry engineers — See the Data Analytics course in Hyderabad

