Miért fontos az adattisztítás?
Őszintén szólva, az adattisztítás (data cleaning) az a munka, amit senki sem szeret, de mindenki kénytelen megcsinálni. Ha valaha dolgoztál nyers adatokkal, tudod miről beszélek. A tapasztalatok szerint az adatszakértők munkaidejük 60–80%-át töltik adattisztítással – és ez nem túlzás. Mielőtt bármilyen elemzést vagy modellezést elkezdhetnénk, a nyers adathalmazok szinte mindig tartalmaznak hiányzó értékeket, duplikált sorokat, inkonzisztens formátumokat és kiugró értékeket. Ezek kezelése nélkül az eredmények egyszerűen megbízhatatlanok lesznek.
A pandas 3.0.1 (2026. február) viszont jó híreket hozott ezen a fronton. A Copy-on-Write szemantika véglegesítése, a dedikált str típus alapértelmezetté válása, valamint a láncolt értékadás (chained assignment) hibává válása mind tisztább és biztonságosabb kódot eredményeznek. Ebben az útmutatóban a pandas 3.x-re optimalizált, működő kódpéldákon keresztül megyünk végig a teljes adattisztítási folyamaton.
Szóval, vágjunk bele!
Környezet beállítása és minta adathalmaz létrehozása
Először is, telepítsük a szükséges csomagokat és készítsünk egy valósághű minta adathalmazt, amin gyakorolni tudunk.
pip install pandas numpy pandera
Most hozzunk létre egy DataFrame-et, ami tartalmazza a leggyakoribb adatminőségi problémákat (szándékosan raktam bele mindent, ami bosszantó):
import pandas as pd
import numpy as np
# Minta adathalmaz valósághű hibákkal
data = {
"nev": ["Kovács Anna", "Nagy Béla", "kovács anna", "Szabó Csilla",
"Nagy Béla", "Tóth Dávid", None, "Kiss Éva"],
"eletkor": [28, 35, 28, None, 35, 150, 42, 29],
"varos": ["Budapest", "debrecen", "Budapest", "Szeged",
"Debrecen", "Pécs", "Budapest ", "GYŐR"],
"fizetes": ["450000", "520000", None, "380000",
"520000", "490000", "610000", "hiányzik"],
"belepteti_datum": ["2024-01-15", "2024-03-22", "2024-01-15",
"2024/06/10", "2024-03-22", "2024-11-05",
"2024-08-18", "invalid_date"]
}
df = pd.DataFrame(data)
print(df)
print(f"\nSorok száma: {len(df)}, Oszlopok száma: {len(df.columns)}")
Nézd meg, mi mindent pakoltunk bele: hiányzó értékek (None), duplikátumok, inkonzisztens formázás (kis- és nagybetű keverése), hibás adattípusok, és egy 150 éves ember. Klasszikus helyzet.
Az adatok feltérképezése
Mielőtt bármit módosítanánk, meg kell ismernünk az adathalmaz állapotát. Ez az a lépés, amit sokan átugranak – ne tedd!
# Általános információk az adathalmazról
print(df.info())
print("\n--- Leíró statisztikák ---")
print(df.describe(include="all"))
print("\n--- Hiányzó értékek oszloponként ---")
print(df.isnull().sum())
print("\n--- Duplikált sorok száma ---")
print(df.duplicated().sum())
Az info() megmutatja az adattípusokat és a nem-null értékek számát, a describe() összefoglaló statisztikákat ad. Az isnull().sum() oszloponként mutatja a hiányzó értékeket, a duplicated().sum() pedig a duplikált sorok számát. Ezek nélkül tulajdonképpen vakon dolgoznánk.
Hiányzó értékek kezelése
Na, ez az a rész, ahol a legtöbb időt fogod tölteni. A hiányzó értékek (NaN, None) kezelése az adattisztítás legkritikusabb lépése, és a pandas szerencsére több stratégiát is kínál erre.
Hiányzó értékek felderítése
# Részletes hiányzó érték elemzés
hianyzik = df.isnull().sum()
hianyzik_szazalek = (df.isnull().sum() / len(df)) * 100
hianyzik_tabla = pd.DataFrame({
"Hiányzó": hianyzik,
"Százalék": hianyzik_szazalek.round(2)
})
print(hianyzik_tabla[hianyzik_tabla["Hiányzó"] > 0])
Sorok és oszlopok eldobása
Ha egy oszlopban az értékek több mint 50%-a hiányzik, érdemes lehet az egész oszlopot eldobni. Egyébként sorok szintjén dönthetünk:
# Sorok eldobása, ahol a "nev" oszlopban hiányzik az érték
df_tiszta = df.dropna(subset=["nev"])
# Oszlopok eldobása, ahol a hiányzó arány > 50%
kuszob = len(df) * 0.5
df_tiszta = df_tiszta.dropna(axis=1, thresh=int(kuszob))
Értékek pótlása (imputáció)
Numerikus oszlopoknál az átlaggal vagy mediánnal szokás pótolni a hiányzó értékeket. Kategorikus adatoknál a módusz (vagyis a leggyakoribb érték) általában a legjobb választás. Saját tapasztalatom szerint a medián szinte mindig megbízhatóbb, mint az átlag – különösen ha vannak kiugró értékek az adatokban.
# Életkor pótlása mediánnal (robusztusabb az átlagnál kiugró értékek esetén)
median_eletkor = df["eletkor"].median()
df["eletkor"] = df["eletkor"].fillna(median_eletkor)
# Város pótlása módusszal
leggyakoribb_varos = df["varos"].mode()[0]
df["varos"] = df["varos"].fillna(leggyakoribb_varos)
print(f"Mediánnal pótolt életkor: {median_eletkor}")
print(f"Módusszal pótolt város: {leggyakoribb_varos}")
Fontos: A pandas 3.0-ban az inplace=True paraméter elavultnak számít. Helyette mindig az eredmény visszarendelését használjuk, tehát df["oszlop"] = df["oszlop"].fillna(ertek). Ha ezt nem tartod be, előbb-utóbb DeprecationWarning-okba fogsz botlani.
Interpoláció idősoroknál
Ha idősor jellegű adatokkal dolgozol, az interpolate() metódus lineáris vagy egyéb interpolációs módszert tud alkalmazni:
# Lineáris interpoláció numerikus oszlopra
df["eletkor"] = df["eletkor"].interpolate(method="linear")
Duplikátumok felismerése és eltávolítása
A duplikált sorok torzítják az aggregált számításokat és a gépi tanulási modelleket egyaránt. Sok kezdő ezt alulbecsüli, de egy rosszul kezelt duplikátum komoly fejfájást okozhat később.
Duplikátumok azonosítása
# Teljes sor alapú duplikátumok keresése
print("Duplikált sorok:")
print(df[df.duplicated(keep=False)])
# Csak bizonyos oszlopok alapján keresés
print("\nDuplikált nevek:")
print(df[df.duplicated(subset=["nev"], keep=False)])
A keep=False paraméter minden duplikált sort megjelöl (az elsőt is), így az összes érintett sort áttekintheted egyszerre. Ez hasznos, mert néha nem az elsőt akarod megtartani.
Duplikátumok eltávolítása
# Duplikátumok eltávolítása – az első előfordulás megmarad
df = df.drop_duplicates(keep="first")
# Bizonyos oszlopok alapján eltávolítás
df = df.drop_duplicates(subset=["nev", "eletkor"], keep="first")
# Index újraszámozása a törlés után
df = df.reset_index(drop=True)
print(f"Megmaradt sorok száma: {len(df)}")
Egyébként az ignore_index=True paramétert is használhatod közvetlenül a drop_duplicates() metódusban – így egy lépést megspórolsz.
Adattípusok javítása
A helyes adattípusok biztosítják, hogy a műveletek megfelelően működjenek. A pandas 3.0 alapértelmezetten str típust használ a szöveges oszlopokhoz (az object helyett), de a konverziók továbbra is szükségesek lehetnek – sajnos az adatok ritkán érkeznek tökéletes formátumban.
Szöveges oszlop számmá alakítása
# Fizetés oszlop konvertálása – a nem numerikus értékeket NaN-ra cseréljük
df["fizetes"] = pd.to_numeric(df["fizetes"], errors="coerce")
print(df["fizetes"].dtype) # float64
Az errors="coerce" a nem konvertálható értékeket (pl. a „hiányzik" szöveget) automatikusan NaN-ra cseréli, ahelyett hogy hibát dobna. Ez az én személyes kedvencem – rengeteg fejfájástól megkímél.
Dátum típus kezelése
# Dátumok konvertálása – különböző formátumok kezelése
df["belepteti_datum"] = pd.to_datetime(df["belepteti_datum"], errors="coerce")
print(df["belepteti_datum"].dtype) # datetime64[ns]
# Új oszlopok kinyerése dátumból
df["belepteti_ev"] = df["belepteti_datum"].dt.year
df["belepteti_honap"] = df["belepteti_datum"].dt.month
A pd.to_datetime() automatikusan felismeri a legtöbb dátumformátumot, ami nagyon kényelmes. Az errors="coerce" itt is gondoskodik arról, hogy az érvénytelen dátumok (mint az „invalid_date") NaT (Not a Time) értékké váljanak hiba helyett.
Szöveges adatok egységesítése
Az inkonzisztens szöveges adatok – különböző kis-/nagybetűk, szóközök, elírások – komoly problémákat okozhatnak csoportosításnál és összesítésnél. Volt már olyan projektem, ahol három „Budapest" volt az adatban: „Budapest", „budapest" és „Budapest " (szóközzel a végén). Szóval mindig érdemes ellenőrizni.
# Felesleges szóközök eltávolítása és egységes formátumra alakítás
df["varos"] = df["varos"].str.strip().str.title()
# Eredmény ellenőrzése
print(df["varos"].value_counts())
A .str.strip() eltávolítja a vezető és záró szóközöket, a .str.title() pedig nagybetűsíti minden szó első betűjét (pl. „debrecen" → „Debrecen", „GYŐR" → „Győr").
Nevek egységesítése
# Nevek egységesítése: strip + title case
df["nev"] = df["nev"].str.strip().str.title()
# Ismételt duplikátum ellenőrzés a normalizálás után
print(f"Duplikátumok normalizálás után: {df.duplicated(subset=['nev']).sum()}")
Ez egy fontos lépés: a szöveges normalizálás után mindig érdemes újra ellenőrizni a duplikátumokat! Korábban különbözőnek tűnő értékek (pl. „Kovács Anna" és „kovács anna") most már azonossá válhatnak.
Oszlopnevek standardizálása
A Python konvenció szerint az oszlopneveket snake_case formátumban érdemes tartani. Ez apróságnak tűnik, de hosszú távon rengeteg bosszúságtól megkímél:
# Oszlopnevek egységesítése snake_case formátumra
df.columns = (
df.columns
.str.strip()
.str.lower()
.str.replace(" ", "_", regex=False)
.str.replace("é", "e", regex=False)
.str.replace("á", "a", regex=False)
)
print(df.columns.tolist())
Kiugró értékek (outlierek) kezelése
A kiugró értékek torzíthatják a statisztikai elemzéseket és a gépi tanulási modelleket. Emlékszünk a 150 éves emberre a minta adatunkban? Na, pontosan ilyen problémákról van szó. Az IQR (Interquartile Range) módszer az egyik legelterjedtebb technika az outlierek azonosítására.
IQR módszer
def outlier_szures_iqr(df, oszlop):
"""Kiugró értékek azonosítása IQR módszerrel."""
Q1 = df[oszlop].quantile(0.25)
Q3 = df[oszlop].quantile(0.75)
IQR = Q3 - Q1
also_hatar = Q1 - 1.5 * IQR
felso_hatar = Q3 + 1.5 * IQR
outlierek = df[(df[oszlop] < also_hatar) | (df[oszlop] > felso_hatar)]
print(f"Kiugró értékek az '{oszlop}' oszlopban: {len(outlierek)}")
print(f" Alsó határ: {also_hatar}, Felső határ: {felso_hatar}")
return df[(df[oszlop] >= also_hatar) & (df[oszlop] <= felso_hatar)]
# Életkor oszlop szűrése
df = outlier_szures_iqr(df, "eletkor")
Értékek csonkolása (clipping)
Nem mindig akarunk sorokat törölni. Ilyenkor az értékeket egyszerűen egy adott tartományra korlátozhatjuk:
# Életkor korlátozása 18 és 100 közé
df["eletkor"] = df["eletkor"].clip(lower=18, upper=100)
print(df["eletkor"].describe())
A clip() metódus a tartományon kívüli értékeket a megadott alsó vagy felső határra állítja. Ez praktikus megoldás, ha nem akarsz adatot veszíteni, csak a szélsőséges értékeket akarod kezelni.
Teljes adattisztítási pipeline
Rendben, most rakjuk össze az egészet. A fenti lépéseket érdemes egy újrafelhasználható függvénybe szervezni – így nem kell minden alkalommal ugyanazt a munkát elvégezni:
def adattisztitas_pipeline(df):
"""Teljes adattisztítási pipeline pandas 3.x-hez."""
# 1. Oszlopnevek standardizálása
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_", regex=False)
# 2. Szöveges oszlopok normalizálása
szoveges_oszlopok = df.select_dtypes(include=["string", "object"]).columns
for oszlop in szoveges_oszlopok:
df[oszlop] = df[oszlop].str.strip().str.title()
# 3. Duplikátumok eltávolítása
eredeti_meret = len(df)
df = df.drop_duplicates(ignore_index=True)
print(f"Duplikátumok eltávolítva: {eredeti_meret - len(df)} sor")
# 4. Hiányzó értékek kezelése (numerikus: medián, szöveges: módusz)
for oszlop in df.select_dtypes(include=["number"]).columns:
if df[oszlop].isnull().any():
df[oszlop] = df[oszlop].fillna(df[oszlop].median())
for oszlop in szoveges_oszlopok:
if df[oszlop].isnull().any():
df[oszlop] = df[oszlop].fillna(df[oszlop].mode()[0])
# 5. Eredmény ellenőrzése
print(f"Tisztított adathalmaz: {df.shape[0]} sor, {df.shape[1]} oszlop")
print(f"Maradék hiányzó értékek: {df.isnull().sum().sum()}")
return df
# Pipeline futtatása
df_tiszta = adattisztitas_pipeline(df)
print(df_tiszta)
Adatvalidáció Pandera könyvtárral
A tisztítás után érdemes formálisan is validálni az adatainkat – mert az, hogy lefuttattunk egy pipeline-t, még nem jelenti, hogy minden rendben van. A Pandera könyvtár sémákat definiál, amelyek automatikusan ellenőrzik az adathalmaz struktúráját és tartalmát:
import pandera as pa
from pandera.pandas import Column, DataFrameSchema, Check
# Séma definíció
sema = DataFrameSchema({
"nev": Column(str, Check.str_length(min_value=2), nullable=False),
"eletkor": Column(float, Check.in_range(18, 100), nullable=False),
"varos": Column(str, nullable=False),
"fizetes": Column(float, Check.greater_than(0), nullable=True),
})
# Validáció futtatása
try:
sema.validate(df_tiszta, lazy=True)
print("Validáció sikeres! Az adathalmaz megfelel a sémának.")
except pa.errors.SchemaErrors as err:
print("Validációs hibák:")
print(err.failure_cases)
A lazy=True paraméterrel a Pandera az összes hibát összegyűjti, ahelyett hogy az elsőnél megállna. Ezt éles projektben mindig ajánlom bekapcsolni, mert sokkal átláthatóbb hibajelentést kapunk.
Legjobb gyakorlatok összefoglalása
- Mindig készíts biztonsági másolatot az eredeti adatokról a tisztítás előtt:
df_eredeti = df.copy(). Komolyan, ez mentett már meg engem pár alkalommal. - Ismerd meg az adataidat – a feltáró elemzés (EDA) segít megérteni, milyen problémákat kell kezelni. Ne ugorj rögtön a tisztításba.
- Dokumentáld a döntéseidet – miért választottad a medián imputációt az átlag helyett? Miért dobtál el bizonyos sorokat? Egy hónap múlva már nem fogod emlékezni.
- Automatizáld a pipeline-t – írj újrafelhasználható függvényeket, és használj Pandera sémákat a validációhoz.
- Ellenőrizd a végeredményt – a tisztítás után futtass
info(),describe()ésisnull().sum()parancsokat. - Pandas 3.x kompatibilitás – kerüld az
inplace=Truehasználatát, és ne használj láncolt értékadást.
Gyakran ismételt kérdések (GYIK)
Hogyan döntsem el, hogy eldobjam vagy pótoljam a hiányzó értékeket?
Ha az adathalmaz nagy és a hiányzó értékek aránya alacsony (mondjuk 5% alatt), a sorok eldobása (dropna()) teljesen biztonságos. Ha az arány magasabb, vagy a hiányzó adatok nem véletlenszerűen fordulnak elő, akkor az imputáció (medián, módusz vagy akár gépi tanulás alapú módszerek) a jobb választás. A lényeg: mindig vizsgáld meg, hogy a hiányzás mintázata véletlenszerű-e vagy szisztematikus.
Mi a különbség az isnull() és az isna() között?
Röviden: semmi. Az isnull() és az isna() teljesen azonos függvények. Az isna() az újabb konvenció, és kicsit konzisztensebb a notna() párjával. A pandas 3.0-ban mindkettő továbbra is működik, szóval ízlés kérdése.
Hogyan kezeljük a duplikátumokat, ha az adatok szinte azonosak, de nem pontosan egyeznek?
Ilyenkor fuzzy matching technikákat érdemes alkalmazni – például a rapidfuzz könyvtár kiváló erre a célra. Először normalizáld a szöveges mezőket (kisbetűsre, ékezetek eltávolítása, szóközök rendezése), majd hasonlósági küszöbértékkel azonosítsd a közel-duplikátumokat. A fuzzywuzzy is népszerű, de a rapidfuzz gyorsabb.
Mi a Copy-on-Write (CoW) és hogyan érinti az adattisztítást?
A pandas 3.0-ban véglegesített Copy-on-Write szemantika lényege, hogy minden indexelési művelet másolatot hoz létre az eredeti adatról. Ez megszünteti azt a korábbi, néha elég frusztráló viselkedést, amikor nem lehetett biztosan tudni, hogy egy művelet az eredeti adatot módosítja-e vagy sem. A gyakorlatban ez azt jelenti, hogy a df.loc[feltétel, "oszlop"] = ertek formátumot kell használnod a módosításokhoz.
Milyen eszközökkel automatizálhatom az adattisztítást?
A Pandera séma-alapú validációt nyújt (ezt már láttuk fentebb), a Great Expectations termelési környezetben használható adatminőség-ellenőrző keretrendszer, a Kedro pedig teljes adatpipeline-kezelést biztosít. Kisebb projektekhez saját Python függvények és Jupyter notebook-ok is tökéletesen megfelelnek, de ha ismétlődő feladatokkal dolgozol, érdemes befektetni egy dedikált eszközbe.