Ako ste ikad radili s Pandasom dulje od jednog popodneva, vjerojatno ste se barem jednom zapitali: "Zašto se ovaj redak ponaša drugačije nego što sam očekivao?" U devet od deset slučajeva odgovor leži u tome kako ste indeksirali DataFrame. Razlika između .loc i .iloc djeluje sitno, ali zna napraviti razliku između koda koji radi predvidljivo i koda koji tiho vraća krive rezultate (a to je, iskreno, najgora vrsta bugova).
U ovom vodiču idemo detaljno kroz oba selektora — kako se ponašaju, gdje griješe i početnici i iskusniji korisnici, te što se promijenilo s Pandas 2.2. Sve s primjerima koje možete kopirati i odmah testirati.
Što su .loc i .iloc i koja je razlika
Pandas vam zapravo daje dva potpuno različita načina da kažete "želim ove podatke":
.loc— indeksiranje pomoću labela. Koristi imena redaka i stupaca, a kod raspona uključuje i gornju granicu (da, to znači da"a":"c"vraća tri retka, ne dva)..iloc— indeksiranje pomoću cjelobrojne pozicije. Klasično 0, 1, 2... s ponašanjem identičnim Pythonovomlistslicing-u (gornja granica isključena).
Najlakše je razliku zapamtiti kroz jedno pitanje: "Tražim li redak po imenu ili po redoslijedu?" Ako po imenu — .loc. Ako po redoslijedu — .iloc. Toliko.
Brza usporedba u tablici
| Karakteristika | .loc | .iloc |
|---|---|---|
| Tip indeksiranja | Label-based | Integer position-based |
| Uključuje gornju granicu? | Da | Ne |
| Boolean maske | Da | Ne (samo nizovi pozicija) |
| Funkcionira s nizom imena | Da | Ne |
| Funkcionira s callable | Da | Da |
Priprema DataFrame-a za primjere
Prije nego krenemo s primjerima, napravimo mali DataFrame s kojim ću se vraćati kroz cijeli članak. Namjerno sam stavio slovne oznake umjesto standardnog 0–4 indeksa — upravo zato da razlika između .loc i .iloc bude jasna kao dan.
import pandas as pd
import numpy as np
data = {
"ime": ["Ana", "Marko", "Iva", "Petar", "Lana"],
"grad": ["Zagreb", "Split", "Rijeka", "Osijek", "Zadar"],
"godine": [28, 34, 22, 41, 30],
"placa": [4200, 5100, 3800, 6700, 4900],
}
df = pd.DataFrame(data, index=["a", "b", "c", "d", "e"])
print(df)
Rezultat:
ime grad godine placa
a Ana Zagreb 28 4200
b Marko Split 34 5100
c Iva Rijeka 22 3800
d Petar Osijek 41 6700
e Lana Zadar 30 4900
Korištenje .loc – indeksiranje pomoću labela
Odabir jednog retka
# Odabir retka s labelom "c"
df.loc["c"]
# ime Iva
# grad Rijeka
# godine 22
# placa 3800
Odabir više redaka
# Lista labela
df.loc[["a", "c", "e"]]
# Raspon labela (UKLJUČUJE oba kraja!)
df.loc["b":"d"]
Pažnja: df.loc["b":"d"] vraća redove b, c i d. Ovo je jedna od najvećih razlika u odnosu na klasični Python slicing i, iz mog iskustva, prva stvar koja zbuni ljude koji dolaze iz NumPy svijeta.
Odabir redaka i stupaca istovremeno
# Sintaksa: df.loc[redak, stupac]
df.loc["a", "grad"] # "Zagreb"
df.loc["a":"c", ["ime", "placa"]]
df.loc[:, "godine"] # cijeli stupac kao Series
Boolean maskiranje s .loc
Ovo je, po meni, najmoćnija primjena .loc — filtriranje po uvjetu. Vjerojatno ćete ovo koristiti svakodnevno:
# Svi koji imaju više od 4500 plaće
df.loc[df["placa"] > 4500]
# Kombinacija uvjeta s odabirom stupaca
df.loc[df["godine"] >= 30, ["ime", "grad"]]
Postavljanje vrijednosti pomoću .loc
Kad mijenjate vrijednosti, .loc je gotovo uvijek pravi izbor — uglavnom zato što izbjegava ono famozno SettingWithCopyWarning upozorenje koje će vas inače proganjati:
# Povećajmo plaću svima starijima od 30 godina za 10%
df.loc[df["godine"] > 30, "placa"] *= 1.10
Korištenje .iloc – indeksiranje pomoću pozicije
Odabir po cjelobrojnoj poziciji
# Prvi redak (pozicija 0)
df.iloc[0]
# Posljednji redak
df.iloc[-1]
# Prva tri retka (gornja granica ISKLJUČENA)
df.iloc[0:3]
Odabir redaka i stupaca po poziciji
# Drugi redak, treći stupac
df.iloc[1, 2] # 34 (godine za Marka)
# Prva dva retka, prva dva stupca
df.iloc[0:2, 0:2]
# Lista pozicija
df.iloc[[0, 2, 4], [0, 3]]
Korištenje step parametra
# Svaki drugi redak
df.iloc[::2]
# Obrnuti redoslijed redaka
df.iloc[::-1]
Najčešće greške i kako ih izbjeći
1. Pretpostavka da je indeks uvijek numerički
Ovo je klasika. DataFrame ima numerički indeks 0–4, vi pišete df.loc[0:2] i dobijete tri retka. Pišete df.iloc[0:2] i dobijete dva. Sve dok je indeks "uredan", ljudi misle da su .loc i .iloc manje-više isto. A onda netko pokrene sort_values() i kod tiho počne vraćati krive rezultate.
df2 = df.reset_index(drop=True)
df2 = df2.sort_values("placa", ascending=False)
df2.loc[0:2] # redci s indeksima 0, 1, 2 (po originalnom indeksu)
df2.iloc[0:2] # prva dva retka u trenutnom redoslijedu
2. Chained indexing (lančano indeksiranje)
Izbjegavajte konstrukcije tipa df[df["placa"] > 4000]["ime"] = "Novi". Pandas vam ovdje ne garantira hoće li operacija promijeniti originalni DataFrame ili samo neku kopiju u memoriji koja će odmah nestati. Pouzdano rješenje je jedna .loc operacija:
# Loše (može izbaciti SettingWithCopyWarning)
df[df["placa"] > 4000]["ime"] = "Novi"
# Dobro
df.loc[df["placa"] > 4000, "ime"] = "Novi"
3. Korištenje .iloc s boolean Series
Tu je još jedna zamka: .iloc ne prihvaća pandas boolean Series — samo nizove ili liste. Ako trebate boolean filtriranje, posegnite za .loc ili pretvorite masku u NumPy niz:
mask = df["godine"] > 30
df.loc[mask] # radi
df.iloc[mask.values] # radi (kao np.ndarray)
# df.iloc[mask] # NotImplementedError
Kada koristiti .loc, a kada .iloc
- Koristite .loc kada radite s imenovanim indeksima, filtrirate po uvjetu ili postavljate vrijednosti.
- Koristite .iloc kada vam je važan redoslijed (prvi/zadnji redak, svaki n-ti redak) ili kada iterativno obrađujete podatke po pozicijama.
- Za jednostavan odabir stupca preferirajte
df["stupac"]umjestodf.loc[:, "stupac"]— kraće je i čitljivije.
Performanse: .loc vs .iloc vs .at i .iat
Mali, ali korisni dodatak: ako vam treba jedna jedina vrijednost, Pandas ima specijalizirane skalarne selektore koji su znatno brži:
.at[label_redak, label_stupac]— label-based, samo skalarna vrijednost.iat[poz_redak, poz_stupac]— position-based, samo skalarna vrijednost
import time
start = time.perf_counter()
for _ in range(100_000):
val = df.loc["a", "placa"]
print("loc:", time.perf_counter() - start)
start = time.perf_counter()
for _ in range(100_000):
val = df.at["a", "placa"]
print("at:", time.perf_counter() - start)
Na većini sustava .at je 3–5 puta brži od .loc za skalarni dohvat. Ako u petlji čitate pojedinačne vrijednosti, koristite .at ili .iat. Ili još bolje — pokušajte vektorizirati operaciju i potpuno se riješiti petlje.
Pandas 2.2 novosti i kompatibilnost
U Pandas 2.x dolazi do nekoliko važnih promjena vezanih uz indeksiranje, a većina ide u korist čistoći koda:
- Copy-on-Write (CoW) postaje zadano ponašanje. Ovo praktički eliminira
SettingWithCopyWarningjer Pandas više ne pokušava razlikovati pogled (view) od kopije. - PyArrow backend za stringove (
dtype="string[pyarrow]") može značajno smanjiti memoriju kad imate puno tekstualnih podataka u indeksu. - Većina
.loci.ilocoperacija sada vraća kopiju, pa modifikacija rezultata ne mijenja originalni DataFrame.
Ako ne želite čekati Pandas 3.0, CoW možete uključiti odmah:
pd.set_option("mode.copy_on_write", True)
FAQ – Često postavljana pitanja
Koja je razlika između .loc i .iloc u Pandasu?
.loc radi s labelama (imenima redaka i stupaca) i uključuje gornju granicu kod raspona. .iloc radi s cjelobrojnim pozicijama i ne uključuje gornju granicu, baš kao Python list slicing.
Mogu li koristiti .iloc s boolean maskom?
Ne direktno s pandas Series. .iloc prihvaća samo cjelobrojne pozicije, listu pozicija ili NumPy boolean niz. Ako imate pandas boolean Series, koristite .loc[mask] ili pretvorite masku s mask.values i predajte je u .iloc.
Što je brže – .loc ili .iloc?
Za jednu vrijednost obje opcije su sporije od specijaliziranih .at (label) i .iat (pozicija). Za odabir više redaka razlika je zanemariva. Glavna optimizacija je vektorizacija — izbjegavajte petlje kad god je moguće.
Zašto dobivam SettingWithCopyWarning?
Upozorenje se javlja kod lančanog indeksiranja, primjerice df[df.x > 0]["y"] = 1. Pandas ne može jamčiti hoće li se promjena propagirati u originalni DataFrame. Rješenje je jedna .loc operacija: df.loc[df.x > 0, "y"] = 1. U Pandas 2.2+ s uključenim Copy-on-Write upozorenje nestaje jer se ponašanje standardizira.
Mogu li mijenjati više stupaca odjednom s .loc?
Da. Primjer: df.loc[df.x > 0, ["a", "b"]] = [10, 20] postavlja stupac a na 10 i b na 20 za sve retke koji zadovoljavaju uvjet. Možete predati i drugi DataFrame ili 2D array istog oblika.
Zaključak
Razumijevanje razlike između .loc i .iloc jedan je od onih trenutaka kad Pandas iz "tajanstvene biblioteke" prelazi u "alat koji stvarno kontroliram". .loc koristite za label-based pristup i filtriranje, .iloc za pristup po poziciji. Za skalarne operacije sjetite se .at i .iat, a lančano indeksiranje — izbjegavajte ga kao zaraženu datoteku. Uz dolazak Copy-on-Write semantike u Pandas 2.2, indeksiranje napokon postaje predvidljivije nego ikad. Pravi trenutak da svoje navike uskladite s onim što Pandas radi danas, a ne onim što je radio prije pet godina.