Adattisztítás Pandas-szal: Útmutató hiányzó adatok, duplikátumok és outlierek kezeléséhez

Lépésről lépésre útmutató a pandas adattisztítás teljes folyamatához: hiányzó értékek pótlása, duplikátumok eltávolítása, adattípusok javítása, outlierek kezelése és Pandera validáció – működő kódpéldákkal, pandas 3.x kompatibilis.

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() és isnull().sum() parancsokat.
  • Pandas 3.x kompatibilitás – kerüld az inplace=True haszná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.

A Szerzőről Editorial Team

Our team of expert writers and editors.