A Séma, dtypes és hiányzó értékek
alfejezetben explicit sémával betöltöttük az orders_raw.csv
fájlt, és sémajelentésben azonosítottuk a hibás sorokat. Most a
következő lépéshez érkeztünk: a betöltött DataFrame-ben
javítani és kiválasztani szeretnénk adatokat anélkül, hogy a módosítás
csendben egy ideiglenes másolaton történne, vagy hogy a pandas
címkeillesztését összekeverjük a NumPy pozicionális műveletével.
A konkrét feladatunk két részből áll. Először ellenőrizni fogjuk,
hogy két eltérő indexű Series összeadása hogyan viselkedik
a pandasban: az indexek unióján, a hiányzó címkéken pd.NA
vagy NaN értékkel, nem pedig pozicionálisan. Ezután egy
láncolt értékadás (chained assignment) javítását végezzük
el: a business szegmensű ügyfelek
priority_flag oszlopát szeretnénk módosítani, és a hibás
df[df.segment == "business"]["priority_flag"] = 0 formát
explicit .loc alakba írjuk át.
Előzetes eredménybecslés: a két eltérő indexű sorozat összeadásánál a
kimenet hossza legfeljebb a két index uniójának mérete; ha az egyik
sorozatnak van olyan címkéje, amely a másikban nincs, azon a helyen
hiányzó érték jelenik meg. A láncolt értékadásnál pedig azt várjuk, hogy
a hibás forma nem módosítja az eredeti DataFrame-et, vagy
figyelmeztetést ad, míg az explicit .loc forma helyesen
frissíti az értékeket.
A pandas DataFrame-ből két alapvető módon választhatunk
ki sorokat és oszlopokat.
df.loc[sortartomány, oszloptartomány]
(label-based location): a címkékkel dolgozik. A sortartomány a
sorindex címkéire, az oszloptartomány az oszlopnevekre hivatkozik. A
loc szeletelésnél a végpontot is beleérti; ez eltér a
Python hagyományos slice-szemantikájától.df.iloc[sorpozíció, oszloppozíció]
(integer position-based location): egész pozíciókkal dolgozik, 0-tól
kezdődően. A szelet végpontja itt kizárt, ahogy a Python-listáknál
megszoktuk.A kettő közötti különbség a matematikai híd lényege: a címkék
halmazjellegű azonosítók — nem feltétlenül rendezettek, nem
feltétlenül egyediek, és nem feltétlenül egész számok —, míg a pozíciók
rendezett egész indexek. Egy (n,)-es táblázatnak
mindig van 0…n−1 pozíciója, de a címkéi lehetnek például karakteres
customer_id értékek is. Ha a címkék történetesen egész
számok, akkor sem keverendő a kettő: df.loc[3] a
3 címkéjű sort keresi, df.iloc[3] a negyedik
sort.
Kis kézi példa:
import pandas as pd
df = pd.DataFrame(
{"ertek": [10, 20, 30]},
index=["c001", "c002", "c003"],
)
df.loc["c002", "ertek"] # címke szerint: 20
df.iloc[1, 0] # pozíció szerint: 20A boolean mask (logikai maszk) egy olyan, az
indexhez illeszkedő True/False sorozat, amely
kijelöli a megtartandó sorokat. Előállítása jellemzően
összehasonlítással történik:
df = pd.DataFrame({"segment": ["business", "consumer", "business"],
"priority_flag": [1, 0, 1]})
mask = df["segment"] == "business"A mask itt Series[bool], ahol minden sorhoz
egy logikai érték tartozik. A pandas Series típusú,
címkézett maszkot közvetlenül a .loc fogadja el. A
.iloc nem fogad el közvetlenül ilyen címkézett boolean
Series-t: pozicionális logikai tömbbé kell alakítani
(mask.to_numpy(dtype=bool)), miután ellenőriztük, hogy a
sorrend és a hossz egyezik:
df.loc[mask, "priority_flag"] # indexcímke-alapú kiválasztás
df.iloc[mask.to_numpy(dtype=bool), df.columns.get_loc("priority_flag")] # pozicionális oszlopA .loc esetén a Series-maszk indexe
illeszkedjen a DataFrame indexéhez (eltéréskor
figyelmeztetés vagy kivétel is felléphet); .iloc esetén a
numpy boolean tömb hosszának kell megegyeznie a sorok
számával, és kizárólag pozíció szerint választ. A két megoldás nem
általánosan felcserélhető. Regressziós ellenőrzés:
import pandas as pd
check = pd.DataFrame({"value": [10, 20, 30]})
mask = check["value"] > 10
assert check.loc[mask, "value"].tolist() == [20, 30]
assert check.iloc[mask.to_numpy(dtype=bool), 0].tolist() == [20, 30]
# A check.iloc[mask] pandas 2.2.x alatt NotImplementedError hibát ad.A pandas index-illesztés
(index alignment) azt jelenti, hogy két Series
vagy DataFrame közötti művelet a címkék szerint
történik, nem a pozíció szerint. Ez alapvető különbség a NumPy-hoz
képest, ahol az azonos alakú tömbök elemenként, pozíció szerint
kapcsolódnak össze.
Példa: legyen
s1 = pd.Series([100, 200], index=["a", "b"])
s2 = pd.Series([5, 7], index=["b", "c"])Az s1 + s2 eredménye az indexek unióján, azaz
["a", "b", "c"] felett értelmezett:
| címke | s1 | s2 | összeg |
|---|---|---|---|
| a | 100 | hiány | hiány |
| b | 200 | 5 | 205 |
| c | hiány | 7 | hiány |
A hiányzó címkéken a pandas NaN-t (numerikus
Series esetén) vagy pd.NA-t (nullable dtype
esetén) ad. Ha tehát két eltérő indexű sorozattal dolgozunk, az összeg
hossza az unió mérete lesz, és a közös címkéken kívül hiányzó értékeket
kapunk. Ez a viselkedés a relációs adatbázisok
FULL OUTER JOIN-jához hasonlít, nem a NumPy pozicionális
műveletéhez.
Az index-illesztés a DataFrame-ek közötti műveletekre is
vonatkozik: ha például df1 + df2-t írunk, a kimenet sorait
a két sorindex uniója, oszlopait a két oszlopkészlet uniója adja. A
hiányzó kombinációkon NaN áll.
A Copy-on-Write (CoW, „írásra másolás”) a pandas
2.0-ban bevezetett, a 3.x sorozatban alapértelmezetté vált
memóriakezelési mód. A lényege: amikor egy DataFrame-ből
szeleteléssel vagy szűréssel új objektumot hozunk létre, a pandas
nem másolja azonnal az adatokat, hanem egy nézetet
(view) tart fenn, és a tényleges másolás csak akkor
történik meg, amikor valaki módosítani próbálja az adatot. Ezzel a
gyakori, csak olvasásra szolgáló műveletek memóriaigénye csökken, a
módosítások pedig nem „szivárognak vissza” az eredeti objektumba.
A gyakorlati következmény számunkra: a láncolt
értékadás (chained assignment) nem megbízható.
Az
df[df["segment"] == "business"]["priority_flag"] = 0kifejezés két egymást követő __getitem__ hívást jelent.
Az első df[df["segment"] == "business"] egy szűrt részt ad;
a második ["priority_flag"] ennek a résznek egy oszlopát.
Az értékadás ezután nem az eredeti df-en, hanem ezen a
köztes objektumon történik — amely CoW bekapcsolásával vagy egyáltalán
nem módosítja az eredetit, vagy SettingWithCopyWarning
figyelmeztetést ad, vagy a pandas verziójától függően egyáltalán nem
hajtódik végre. Az eredmény tehát csendben is lehet hibás: a kód lefut,
de az eredeti táblázat változatlan marad.
A javított alak egyetlen .loc hívás, amely egyszerre
címzi a sorokat és az oszlopot:
df.loc[df["segment"] == "business", "priority_flag"] = 0Itt a pandas egyetlen műveletben tudja, hogy az eredeti
df-et kell módosítani. Ez CoW mellett is helyesen működik,
mert a .loc explicit jelzi a módosítás helyét.
A CoW a pandas 2.x sorozatban opcionális: a kezdőprojekt a
lab/pyproject.toml szerint pandas
2.2.3-mal futott, ahol a CoW alapértelmezetten kikapcsolt. A
pandas 3 a CoW-t alapértelmezetten bekapcsolja. A tanulási környezetünk
ezért két dolgot tesz:
.loc/.iloc értékadást használunk, láncolt
assignmentet soha.import pandas as pd
print(pd.__version__)
# Szemléltető kimenet a kezdőlaborban: "2.2.3"A CoW a pandas 2.2-ben a
pd.options.mode.copy_on_write = True beállítással
kapcsolható be. A könyv példái nem feltételezik ezt a beállítást;
helyette az explicit műveleti stílust tanítjuk, amely mindkét
verziócsaládban azonos eredményt ad. A Broadcasting,
indexelés és másolás alfejezetben látott NumPy-s
view/copy megkülönböztetés itt is
visszaköszön: a pandas a NumPy-tömbök
view/copy viselkedésére épül, de az
index-illesztés rétege megváltoztatja, hogy melyik művelet mikor ad
másolatot.
A laborhoz az orders_raw.csv már betöltött, explicit
sémás változatát használjuk. A példát úgy építjük fel, hogy egy kis,
kézzel ellenőrizhető DataFrame-en mutatjuk be a két
kulcsműveletet, majd ugyanazt a formát alkalmazzuk a nagyobb
táblázatra.
import pandas as pd
# Kézi példa: három rendelés, karakteres indexszel
df = pd.DataFrame(
{
"order_id": ["o1", "o2", "o3"],
"segment": ["consumer", "business", "consumer"],
"priority_flag": [0, 1, 0],
"basket_minor": [1200, 4500, 800],
}
).set_index("order_id")
dfSzemléltető kimenet:
| order_id | segment | priority_flag | basket_minor |
|---|---|---|---|
| o1 | consumer | 0 | 1200 |
| o2 | business | 1 | 4500 |
| o3 | consumer | 0 | 800 |
# Két eltérő indexű sorozat
s1 = pd.Series([100, 200], index=["a", "b"])
s2 = pd.Series([5, 7], index=["b", "c"])
s_sum = s1 + s2
s_sumSzemléltető kimenet:
| index | érték |
|---|---|
| a | NaN |
| b | 205 |
| c | NaN |
Az ellenőrzés kézzel: a b címkén 200 + 5 = 205; az
a és c címkék csak az egyik sorozatban
szerepelnek, ezért ott hiányzó érték áll. A kimenet hossza 3, ami a két
index uniójának mérete.
Hibás forma:
# Rossz: láncolt assignment
df[df["segment"] == "business"]["priority_flag"] = 0
# pandas 2.2-ben CoW nélkül: SettingWithCopyWarning,
# és az eredeti df NEM módosul megbízhatóan.Javított forma:
# Helyes: egyetlen explicit .loc
df.loc[df["segment"] == "business", "priority_flag"] = 0
dfSzemléltető kimenet a javítás után:
| order_id | segment | priority_flag | basket_minor |
|---|---|---|---|
| o1 | consumer | 0 | 1200 |
| o2 | business | 0 | 4500 |
| o3 | consumer | 0 | 800 |
Az o2 rendelés priority_flag értéke 1-ről
0-ra változott, a többi sor érintetlen. Ez a módosítás egyetlen
.loc hívással, az eredeti DataFrame-en
történt.
# Feltételezzük az s021-ből ismert betöltést:
# df_raw = pd.read_csv("lab/data/orders_raw.csv", dtype=schema)
# Itt a már betöltött df_raw egy részletével dolgozunk.
# Ellenőrzés: a business sorok módosítása explicit .loc formában
business_mask = df_raw["segment"] == "business"
df_raw.loc[business_mask, "priority_flag"] = 0
# Elvárt ellenőrzés: a business sorokban minden priority_flag 0
assert (df_raw.loc[business_mask, "priority_flag"] == 0).all()A kézi példa ellenőrizhető invariantja: a
s_sum = s1 + s2 sorozat indexe megegyezik a két bemeneti
index uniójával (["a", "b", "c"]), és a közös címkén az
érték a két bemenet összege (205). A hiányzó címkéken NaN
áll. Ez a FULL OUTER JOIN-szemantika, nem a NumPy
pozicionális művelete: ha a két sorozatot NumPy-tömbökként adnánk össze,
a kimenet [105, 207] lenne, ami a második elemet tévesen
200 + 7 = 207-ként számolná, mert a NumPy nem tud a címkékről.
A láncolt értékadás ellenőrzése: a hibás forma után az eredeti
df priority_flag oszlopát megvizsgálva azt
látjuk, hogy a business sor továbbra is 1 maradt (vagy a
pandas verziójától függően figyelmeztetést kapunk). Az explicit
.loc forma után az érték 0, és ezt assert-tel
automatikusan ellenőrizzük. A maszk (business_mask) maga is
Series[bool], amelynek indexe illeszkedik a
DataFrame sorindexéhez; a .loc ezt a címkék
szerint illeszti, ezért a hossza pontosan megegyezik a sorok
számával.
Verzióérzékenység: a fenti példák pandas 2.2.3-mal
készültek. Pandas 3-ban a CoW alapértelmezett, ezért a hibás láncolt
forma ott már nem is ad SettingWithCopyWarning-t, hanem
egyszerűen nem módosítja az eredetit, vagy kivételt dob. Az explicit
.loc forma mindkét verziócsaládban azonos eredményt ad;
ezért ez a hordozható stílus.
1. feladat — Értelmezés. Adottak az alábbi sorozatok:
a = pd.Series([1, 2, 3], index=["x", "y", "z"])
b = pd.Series([10, 20], index=["y", "w"])Mi lesz az a + b eredmény indexe és értékei? Válaszát
indokolja az index-illesztés szabálya alapján, majd nevezze meg, mi a
különbség a NumPy pozicionális összeadásához képest.
2. feladat — Implementálás. Töltse be a
lab/data/orders_raw.csv fájlt a Séma,
dtypes és hiányzó értékek alfejezetben megismert explicit sémával.
Készítsen boolean maszkot azokra a sorokra, ahol a
distance_km hiányzik. Javítsa a hiányzó távolságokat
explicit .loc használatával a business
szegmensű rendelések medián távolságára, ha a segment
oszlop értéke business; a consumer sorokban
használja a consumer mediánt. Ellenőrizze, hogy a javítás
után nincs hiányzó distance_km érték. (A mediánnal a Közép, szóródás és robusztus összegzés fog
részletesen foglalkozni; itt a df["distance_km"].median()
hívás elégséges.)
3. feladat — Hibadiagnózis. A következő kódot találja egy notebookban:
df[df["basket_minor"] < 0]["basket_minor"] = 0A futtatás után a negatív basket_minor érték továbbra is
jelen van a df-ben, bár a kód nem dobott kivételt.
Magyarázza el, mi a hiba oka, és írja át a kódot explicit
.loc formára. Indokolja, miért működik a javított változat
CoW mellett is.
4. feladat — Transzfer megváltozott feltételre. A
laborban a priority_flag 0/1 jelzés. Tegyük fel, hogy az
üzleti szabály megváltozik: a consumer szegmensű rendelések
priority_flag értékét 1-re kell állítani, ha a
basket_minor nagyobb, mint 5000. Írjon olyan kódot, amely
ezt a módosítást kizárólag explicit .loc művelettel végzi
el, majd ellenőrizze a feltétel teljesülését assert
segítségével. A megoldásban a feltétel és a beírandó érték is a rendelés
pillanatában ismert adatból származzon.
5. feladat — Index-illesztés ellenőrző invariant.
Két eltérő indexű Series összeadásánál a kimeneti indexnek
meg kell egyeznie a két bemeneti index uniójával. Írjon egy kis
függvényt assert-ekkel, amely két tetszőleges
Series-re ellenőrzi: (a) a kimeneti index mint halmaz
megegyezik a két bemeneti index uniójával; (b) a közös címkéken a
kimeneti érték a két bemenet összege. Tesztelje a függvényt az 1.
feladat a és b sorozatán.
1. megoldás. Az a + b indexe a két
index uniója: ["w", "x", "y", "z"] (a sorrend a pandas
rendezési szabálya szerint alakul, az itteni sorrend az unió rendezett
formája). A y címke mindkét sorozatban szerepel, ott az
érték 2 + 20 = 22; a többi címkén NaN:
| index | érték |
|---|---|
| w | NaN |
| x | NaN |
| y | 22 |
| z | NaN |
A NumPy pozicionális összeadás ezzel szemben az
[1+10, 2+20] = [11, 22] eredményt adná, mert a tömböknek
nincs címke-fogalmuk; a harmadik elem (3) elveszne, mert a
második tömb rövidebb. A pandas ezt a hibát elkerüli, de hiányzó
értékeket hoz be — ezért a kimeneti NaN-eket mindig
ellenőrizni kell, mielőtt az összeget további számításba vonnánk.
2. megoldás. A Séma, dtypes és hiányzó értékek sémáját használva:
import pandas as pd
schema = {
"order_id": "string",
"customer_id": "string",
"order_ts": "string", # majd parse_dates
"promised_days": "Int64",
"quantity_sum": "Int64",
"item_rows": "Int64",
"basket_minor": "Int64",
"shipping_fee_minor": "Int64",
"currency": "string",
"distance_km": "Float64",
"priority_flag": "Int64",
"delivery_days": "Int64",
"actual_delivery_at": "string",
"label_available_at": "string",
"target_late": "Int64",
"segment": "string",
"region": "string",
"acquisition_channel": "string",
}
df_raw = pd.read_csv(
"lab/data/orders_raw.csv",
dtype=schema,
parse_dates=["order_ts", "actual_delivery_at", "label_available_at"],
)
missing = df_raw["distance_km"].isna()
business_med = df_raw.loc[df_raw["segment"] == "business", "distance_km"].median()
consumer_med = df_raw.loc[df_raw["segment"] == "consumer", "distance_km"].median()
df_raw.loc[missing & (df_raw["segment"] == "business"), "distance_km"] = business_med
df_raw.loc[missing & (df_raw["segment"] == "consumer"), "distance_km"] = consumer_med
assert not df_raw["distance_km"].isna().any()Indoklás: a mediánt külön számoljuk a két szegmensre, mert a távolság
eloszlása szegmensfüggő lehet; a csoportos medián jobb, mint egyetlen
globális érték. A .loc explicit sor-oszlop párt ad, így a
módosítás az eredeti df_raw-n történik. Az
assert a végső invariantet ellenőrzi: nincs hiányzó
távolság. (A szegmens szerinti medián imputáció modellezési döntés; a
valós munkában ez egy indokolt, naplózott döntés, nem automatikus
lépés.)
3. megoldás. A hiba oka a láncolt értékadás. A
df[df["basket_minor"] < 0] kifejezés egy új, szűrt
DataFrame-et ad, amely CoW nélkül nézet lehet, CoW-val
pedig másolat. A rákövetkező ["basket_minor"] = 0 ezen a
köztes objektumon hajtódik végre, nem az eredeti df-en. A
pandas ezért SettingWithCopyWarning-ot adhat, de az eredeti
táblázatban a negatív érték megmarad — a kód „sikeres”, de
hatástalan.
Javított alak:
df.loc[df["basket_minor"] < 0, "basket_minor"] = 0Ez a forma egyetlen műveletben adja meg a sorokat (boolean mask) és
az oszlopot, így a pandas pontosan tudja, hogy az eredeti
df módosítandó. CoW mellett is helyes, mert a
.loc explicit módosítást jelez, nem egy ideiglenes
objektumon operál.
4. megoldás.
cond = (df_raw["segment"] == "consumer") & (df_raw["basket_minor"] > 5000)
df_raw.loc[cond, "priority_flag"] = 1
# Ellenőrző invariant: a feltételnek megfelelő sorokban a zászló 1
assert (df_raw.loc[cond, "priority_flag"] == 1).all()
# Ellenőrző invariant: a feltételnek nem megfelelő consumer sorokban a zászló változatlan
# (itt csak azt ellenőrizzük, hogy a módosítás nem érintette a többi sort
# egy korábban rögzített mintán; a teljes ellenőrzéshez a módosítás előtti állapot kell.)A feltétel két részből áll: a szegmens consumer, és a
kosárérték meghaladja az 5000 minor egységet. A &
operátor elemenkénti logikai ÉS a két boolean Series
között; a kerek zárójelek a Python precedenciája miatt kötelezőek. A
módosítás kizárólag a feltételnek megfelelő sorokat érinti.
5. megoldás.
def check_series_alignment(s1: pd.Series, s2: pd.Series) -> None:
out = s1 + s2
expected_index = s1.index.union(s2.index)
# (a) a kimeneti index mint halmaz megegyezik az unióval
assert set(out.index) == set(expected_index)
# (b) közös címkéken az összeg helyes
common = s1.index.intersection(s2.index)
for idx in common:
assert out.loc[idx] == s1.loc[idx] + s2.loc[idx]
# Numerikus pontosság miatt éles tesztben:
# assert abs(out.loc[idx] - (s1.loc[idx] + s2.loc[idx])) < 1e-12
# Teszt az 1. feladat adatain
a = pd.Series([1, 2, 3], index=["x", "y", "z"])
b = pd.Series([10, 20], index=["y", "w"])
check_series_alignment(a, b)
print("Az index-illesztési invariant teljesül.")A függvény két invariantet ellenőriz: a kimeneti index mint halmaz az
unió, és a közös címkéken az összeg pontos. Lebegőpontos sorozatoknál a
== helyett toleranciás összehasonlítást kell használni,
ahogy a Lebegőpont, hiányzó érték és stabil
számítás alfejezetben tárgyaltuk; itt egész értékekkel a pontos
egyenlőség is elfogadható a szemléltető példában.
A leggyakoribb tévedés, hogy a pandas Series műveleteket
NumPy pozicionális szemantikával várjuk. Tekintsük a következő két
rendelés-összeg sorozatot:
rev_a = pd.Series([100, 200], index=["o1", "o2"]) # o1, o2 rendelések
rev_b = pd.Series([50, 70], index=["o2", "o3"]) # o2, o3 rendelésekHa valaki NumPy-szokás szerint azt várja, hogy
rev_a + rev_b eredménye [150, 270], akkor
téved: a pandas a címkék szerint illeszt, és az eredmény indexe
["o1", "o2", "o3"], értékei pedig
[NaN, 250, NaN]. Az o1 és o3
rendeléseknél hiányzó érték keletkezik, mert azok a másik sorozatban nem
szerepelnek. A helyes összegzéshez az indexeket előbb igazítani kell,
például rev_a.add(rev_b, fill_value=0) hívással, amely a
hiányzó címkéket 0-ként kezeli, és így [100, 250, 70]
eredményt ad.
Egy másik, kifejezetten a loc/iloc
keveréséből fakadó hiba: ha a sorindex 0, 1, 2, ... egész
számokból áll, a df[0] kifejezés nem az első sort, hanem a
0 oszlopnevet keresi (ha ilyen nincs,
KeyError-t ad). Az iloc[0] az első sort adja.
Az index értékeinek és a pozícióknak az összekeverése olyan csendes
hibát okozhat, amely csak az index átrendezésekor derül ki. Ezért minden
módosításnál dönteni kell: loc címkével, vagy
iloc pozícióval, de a kettőt soha nem szabad implicit módon
váltogatni.
df.loc[df["x"] > 0, "y"] és a
df[df["x"] > 0]["y"] kifejezés között? Válasz:
az első egyetlen .loc hívás, amely a sorokat és az oszlopot
egyszerre címzi, ezért módosíthatja az eredeti df-et; a
második láncolt kiválasztás, amelynek az értékadása nem az eredetin
történik.Series összeadásakor a kimeneti index
mi lesz? Válasz: a két index uniója; a hiányzó címkéken hiányzó
érték áll.DataFrame-be, mert az objektum nem nézet,
hanem másolatként kezelendő.Copy-on-Write használatakor a
df.loc[maszk, oszlop] = érték alakú, egy lépésben végzett
módosítás előnyösebb a láncolt hozzárendelésnél. A nézetek és másolatok
viselkedése, illetve a címkék szerinti automatikus indexillesztés két
külön kérdés: mindkettőt önállóan kell ellenőrizni.
A pandas kiválasztás két útja a loc (címke szerint, a
szeletvégpontot is beleértve) és az iloc (pozíció szerint,
a végpontot kizárva); a címkézett Series maszk a
.loc-kal használható; a .iloc pozicionális
boolean tömböt fogad, például mask.to_numpy(dtype=bool)
alakban. A Series-ek közötti műveletek index-illesztéssel
történnek: a kimeneti index a bemeneti indexek uniója, a hiányzó
címkéken NaN vagy pd.NA áll — ez nem NumPy
pozicionális szemantika. A Copy-on-Write a pandas 3-ban alapértelmezett,
a kezdőlabor 2.2.3-as verziójában opcionális; a láncolt értékadás egyik
módban sem megbízható, ezért minden módosítást egyetlen explicit
.loc/.iloc hívással végzünk. A következő
alfejezetben a duplikáció, a kulcs és az adatminőségi szerződés
ellenőrzését építjük ezekre a kiválasztási műveletekre.