← CS Lab Data Science & Statistics / pandas .loc vs .iloc Open standalone ↗

pandas gives you two indexers because a DataFrame has two coordinate systems. .loc selects by label — the names in the index and the column headers. .iloc selects by integer position — 0-based, like a Python list. Same table, two ways to point at a cell.

Where you meet it

What it does

Both take a [rows, cols] pair and return the intersection. The difference is only what the keys mean: .loc reads them as labels, .iloc as positions. That choice also changes how slices behave — and that is where most bugs live.

.loc slices include the end label; .iloc stops one short. That off-by-one is where the bugs hide.

How it works

The general form is df.loc[rows, cols] (or .iloc). Pass a single key, a list, a slice, or a boolean mask on either axis. The two indexers slice in opposite ways:

import pandas as pd
df = pd.DataFrame(
    {'age': [34, 28, 41, 23, 37]},
    index=['u1', 'u2', 'u3', 'u4', 'u5'],
)

# .loc — by label, end label is INCLUDED
df.loc['u2':'u4', 'age']    # u2, u3, u4   → 3 rows

# .iloc — by position, end is EXCLUDED (like Python)
df.iloc[1:3, 0]             # positions 1, 2 → 2 rows

The pandas docs are blunt about it: for .loc, "contrary to usual Python slices, both the start and the stop are included." For .iloc, "the start bound is included, while the upper bound is excluded."

Watch out

  • The .loc slice is end-inclusive. df.loc['u2':'u4'] returns three rows, not two — the single biggest surprise when switching from list slicing.
  • Chained indexing. df[df.bar > 5]['foo'] = 100 assigns through two steps and silently fails (a SettingWithCopyWarning, or under Copy-on-Write a ChainedAssignmentError). Write it as df.loc[df.bar > 5, 'foo'] = 100.
  • Integer labels are a trap. If the index is integers but not 0,1,2,…, .loc[2] looks up the label 2 while .iloc[2] takes the third row — usually different rows.
  • Don't index with bare [] for cells. df['age'] selects a column; for a row, or a row-and-column pair, reach for .loc/.iloc explicitly.

Go deeper

pandas hat zwei Indexer, weil ein DataFrame zwei Koordinatensysteme hat. .loc wählt per Label — die Namen im Index und die Spaltenköpfe. .iloc wählt per Integer-Position — ab 0, wie eine Python-Liste. Dieselbe Tabelle, zwei Wege, auf eine Zelle zu zeigen.

Wo es vorkommt

Was es tut

Beide nehmen ein [zeilen, spalten]-Paar und liefern die Schnittmenge. Der Unterschied liegt nur darin, was die Schlüssel bedeuten: .loc liest sie als Labels, .iloc als Positionen. Diese Wahl ändert auch, wie sich Slices verhalten — und genau da sitzen die meisten Bugs.

.loc-Slices schließen das End-Label ein, .iloc hört eins früher auf. In diesem Off-by-one sitzen die Bugs.

Wie es funktioniert

Die allgemeine Form ist df.loc[zeilen, spalten] (oder .iloc). Übergib einen Einzelschlüssel, eine Liste, einen Slice oder eine boolesche Maske auf jeder Achse. Die beiden Indexer slicen entgegengesetzt:

import pandas as pd
df = pd.DataFrame(
    {'age': [34, 28, 41, 23, 37]},
    index=['u1', 'u2', 'u3', 'u4', 'u5'],
)

# .loc — per Label, End-Label INKLUSIVE
df.loc['u2':'u4', 'age']    # u2, u3, u4   → 3 Zeilen

# .iloc — per Position, Ende EXKLUSIV (wie Python)
df.iloc[1:3, 0]             # Positionen 1, 2 → 2 Zeilen

Die pandas-Doku sagt es klar: bei .loc sind "im Gegensatz zu üblichen Python-Slices beide — Start und Stop — eingeschlossen". Bei .iloc ist "die untere Grenze eingeschlossen, die obere ausgeschlossen".

Worauf achten

  • Der .loc-Slice ist end-inklusive. df.loc['u2':'u4'] liefert drei Zeilen, nicht zwei — die größte Überraschung beim Umstieg vom Listen-Slicing.
  • Chained Indexing. df[df.bar > 5]['foo'] = 100 weist über zwei Schritte zu und schlägt still fehl (eine SettingWithCopyWarning, unter Copy-on-Write ein ChainedAssignmentError). Schreib es als df.loc[df.bar > 5, 'foo'] = 100.
  • Integer-Labels sind eine Falle. Ist der Index selbst ganzzahlig, aber nicht 0,1,2,…, sucht .loc[2] das Label 2, während .iloc[2] die dritte Zeile nimmt — meist verschiedene Zeilen.
  • Für Zellen nicht das nackte [] nutzen. df['age'] wählt eine Spalte; für eine Zeile oder ein Zeilen-Spalten-Paar greif bewusst zu .loc/.iloc.

Mehr dazu

Next up
Data Science & Statisticspandas Boolean Filtering Data Science & Statisticspandas groupby All concepts →