Skip to main content
April 2, 2026Brian McClain/7 min read

LOC vs ILOC - DataFrame Indexing and Labeling

Master DataFrame indexing with LOC and ILOC methods

LOC vs ILOC: Core Differences

FeatureLOCILOC
Index TypeLabel-basedInteger-based
Column ReferenceColumn namesIndex numbers
Row ReferenceRow namesIndex positions
Range BehaviorInclusiveExclusive
Best ForNamed columnsNumeric positions
Recommended: Use LOC for named column references and ILOC for position-based indexing
Key Memory Aid

LOC means location by name, ILOC means integer location by position. Think 'I' for integer in ILOC.

DataFrame Indexing Scenarios

Column Access by Name

When you need to reference columns like 'item', 'price', 'cals' instead of remembering they are positions 0, 1, 2. LOC is your solution.

Position-Based Selection

When you want first three rows and first three columns regardless of their names. ILOC works with numeric positions.

Non-Contiguous Selection

Selecting scattered rows or columns requires list notation in both LOC and ILOC methods.

Chessboard Example Walkthrough

1

Target Rooks with LOC

Use chessboarddf.loc[rows, columns] with row names '8' and '1', column names 'A' and 'H'

2

Target Bishops

Same rows '8' and '1', but columns 'C' and 'F' for bishop positions

3

Target Pawns

Rows '7' and '2' with all columns using colon notation for complete row selection

Range Behavior Difference

ILOC range :3 gets rows 0,1,2 (exclusive). LOC range :2 gets rows 0,1,2 (inclusive). This trips up many users.

LOC Method Analysis

Pros
Human-readable column and row references
Inclusive range behavior is intuitive
Works seamlessly with named indices
Better for data exploration and analysis
Cons
Requires knowledge of column names
Can be slower than integer indexing
Range syntax differs from ILOC

ILOC Method Analysis

Pros
Fast integer-based indexing
Works regardless of column names
Consistent with NumPy array indexing
Good for programmatic data access
Cons
Less readable code
Requires memorizing column positions
Exclusive range behavior can confuse
Breaks if DataFrame structure changes

DataFrame Indexing Best Practices

0/5
LOC and ILOC are tricky. It's going to take a while. Don't get frustrated with it. Just hang with it. You're going to have to put your time in to get comfortable and good with ILOC versus LOC.
Expert advice on mastering DataFrame indexing methods

This lesson is a preview from our Data Science & AI Certificate Online (includes software) and Python Certification Online (includes software & exam). Enroll in a course for detailed lessons, live instructor support, and project-based training.

In practice, columns are rarely left with default numeric labels like 0, 1, 2, 3. They're assigned meaningful names that reflect the data they contain. When you need to reference, filter, or extract values using these descriptive column names, you'll use LOC (location), not ILOC (integer location). This distinction is fundamental to effective data manipulation in pandas.

Think of LOC as "named location" — it works with the human-readable labels you've assigned to your data structure. While row names often mirror their index numbers (making LOC and ILOC interchangeable for rows), column names typically don't follow this pattern. Consider our food dataframe: instead of numeric columns 0, 1, 2, 3, we have meaningful labels like "item," "price," "calories," and "vegan." When querying for price data, you shouldn't need to remember that "price" is internally stored as column 1 — LOC lets you work with intuitive names.

Let's return to our chessboard example to demonstrate this concept in action. We'll modify the rook pieces from "R" to "RK" to practice targeting cells using named references rather than numeric indices.

Notice how our chessboard's structure mirrors real-world data labeling. Row 0 in ILOC corresponds to row "8" in LOC, while column 0 in ILOC maps to column "A" in LOC. Previously, we've been working with numeric indices — ignoring the traditional chess notation of letters A-H for columns and the reverse numbering 8-1 for rows. Instead of limiting ourselves to 0-7 indexing for both dimensions, we can leverage the descriptive labels that make our code more readable and maintainable.

Here's how we target the rooks using named locations. We'll change the existing "R" symbols to "RK" using LOC instead of ILOC, referencing rows and columns by their chess notation names: A-H for columns and 8-1 for rows, rather than the underlying 0-7 numeric indices.

The syntax is straightforward: chessboarddf.loc[row_labels, column_labels]. For our rook modification, we want rows 8 and 1 (the top and bottom ranks) and columns A and H (the corner files). Instead of writing separate commands for each row as we did previously, we can target both simultaneously: chessboarddf.loc[[8,1], ['A','H']] = 'RK'. This efficiently updates all four corner positions in a single operation.

Perfect — we now see "RK" in all four corner positions. Let's practice with another piece type. Try changing the bishops from "B" to "BP" using the same LOC approach. Take a moment to work through this challenge before continuing.

The solution targets the same rows (8 and 1 for top and bottom ranks) but different columns. Bishops occupy the C and F files, not the corner A and H positions of the rooks: chessboarddf.loc[[8,1], ['C','F']] = 'BP'. This demonstrates how LOC's named referencing makes the code self-documenting — anyone reading this code immediately understands we're modifying pieces on specific chess squares.

For our final chess example, let's modify the pawns from "P" to "PN". This presents a slightly different challenge since pawns occupy entire ranks rather than specific squares. The pawns sit on ranks 7 and 2 (using chess notation), spanning all files from A to H.


Here's the key insight: when we want non-contiguous rows (7 and 2, which don't touch), we pass them as a list. For columns, since we want all files, we use a colon to indicate the full range: chessboarddf.loc[[7,2], :] = 'PN'. The colon serves as a wildcard, selecting all columns while our bracketed list targets specific, non-adjacent rows.

Now let's examine all three modifications together to solidify the LOC concept. We've successfully used named references instead of numeric indices: letters A-H rather than the underlying 0-7 column indices, and chess rank numbers rather than their corresponding array positions. This approach makes our code more intuitive and less prone to off-by-one errors that plague numeric indexing.

Returning to our food dataframe, let's contrast ILOC and LOC approaches side by side. First, we'll use ILOC to extract the first three rows and first three columns — deliberately excluding the "vegan" column and the "garden salad" row to create a focused subset.

With ILOC, we specify numeric ranges: fooddf.iloc[0:3, 0:3]. Remember that ILOC uses exclusive upper bounds, so 0:3 actually returns indices 0, 1, and 2. This gives us exactly what we want: no vegan column, no garden salad row.

Now here's where LOC behavior differs significantly. LOC uses inclusive upper bounds, meaning if you specify row 3, you'll get everything up to and including row 3. For our equivalent LOC operation, we need fooddf.loc[0:2, 'item':'calories']. Notice two critical differences: we stop at row 2 (not 3) because LOC is inclusive, and we must use column names ('item':'calories') rather than numeric indices.

This inclusive versus exclusive behavior is a common source of confusion for data professionals transitioning between the two methods. Take time to internalize this difference — ILOC excludes the upper bound (like Python's standard range function), while LOC includes it. This isn't just a syntax quirk; it reflects the fundamental difference between numeric indexing and named referencing.

Let's practice with a focused exercise. First, use ILOC to get the first two rows and first two columns from our food dataframe. Then replicate this exact selection using LOC. Work through both challenges before checking the solutions.

The ILOC solution is straightforward: fooddf.iloc[:2, :2]. This returns rows 0 and 1 with columns "item" and "price" — the first two in each dimension.


For the LOC equivalent, rows remain the same since we never assigned custom row names (the default numeric labels serve as both index and name). However, columns require their actual names: fooddf.loc[:2, 'item':'price']. We're creating a range from 'item' to 'price', and because LOC is inclusive, this captures exactly the first two columns.

Now let's explore non-contiguous selections — a powerful feature for extracting specific, non-adjacent data points. Suppose we want the first two rows but only the "item" and "calories" columns, deliberately skipping "price." This requires a different syntax since we can't use a colon (which creates ranges) for non-touching elements.

For non-contiguous selections, wrap your targets in lists: fooddf.loc[:1, ['item', 'calories']]. The square brackets indicate we're selecting specific, individual elements rather than a continuous range. This same principle applied to our chess bishops and rooks — pieces that occupy specific squares rather than continuous ranks or files.

The ILOC version follows the same pattern with numeric indices: fooddf.iloc[:2, [0, 2]]. We're selecting columns 0 and 2 (skipping column 1) using list notation for the non-contiguous column selection.

For our final example, let's select non-contiguous rows as well as columns — specifically the first and last rows with only the "item" and "calories" columns. This demonstrates the full flexibility of both approaches when working with scattered data points.

Using ILOC: fooddf.iloc[[0, 3], [0, 2]] — both dimensions require lists since we're skipping elements in both rows and columns. The LOC equivalent: fooddf.loc[[0, 3], ['item', 'calories']] — same list structure, but using meaningful names for columns while keeping the numeric row references (since our rows lack custom labels).

Mastering the distinction between LOC and ILOC is essential for efficient data manipulation. Remember the core principle: ILOC works with integer positions (the underlying numeric structure), while LOC operates with labels (the human-readable names you've assigned). This isn't merely a syntax preference — it's about writing maintainable, self-documenting code that remains robust as your datasets evolve and grow in complexity.

Key Takeaways

1LOC uses label-based indexing with column and row names, while ILOC uses integer position-based indexing
2Column references in LOC use actual column names like 'item' and 'price', eliminating need to remember numeric positions
3Range behavior differs significantly: LOC ranges are inclusive while ILOC ranges are exclusive
4Non-contiguous selections in both methods require list notation with square brackets
5LOC is more user-friendly for data analysis as it allows referencing columns by meaningful names
6ILOC is faster and more programmatic, working with numeric positions regardless of column names
7Both methods can target the same data but LOC provides better code readability and maintainability
8Mastering both methods requires consistent practice as the syntax differences can be confusing initially

RELATED ARTICLES