Nagyméretű adathalmazok rendezése

Számos esetben igényként merülhet fel egy adathalmaz valamilyen ismérv szerinti sorba rendezése. Például ha a szabványos könyvtár itertools moduljának groupby() függvényét akarjuk használni, akkor a helyes működéshez az argumentumként átadott adatsorozatnak rendezettnek kell lenni. A sorba rendezést megtehetjük például a sorted() beépített függvénnyel, amely beállítástól függően növekvő vagy csökkenő sorrendben rendezi az értékeket és egy listában adja vissza azokat. Ez mindaddig jól működik, amíg az adatok számossága akkora, hogy ez a rendezett lista befér a számítógép memóriájába.

Mi van azonban akkor, ha olyan nagy adathalmazzal kell dolgozni, amely már nem tölthető be a memóriába a rendezéshez?

Ebben az esetben olyan módszert kell alkalmazni, amely a rendezés folyamán a háttértárat is használja és a rendezett adatok is egy fájlban fognak tárolódni. Mivel tehát ilyenkor a rendezéshez nem csak a belső memóriát, hanem a külső háttértárat is használjuk, ezért az ilyen eljárást külső rendezésnek (external sorting) nevezik. Ennek megfelelően az olyan adatrendezést, amely csak a számítógép memóriájában történik belső rendezésnek (internal sorting) hívják.

A külső rendezés – mivel többszöri háttértárba írás és olvasás történik – természeteseb lassúbb a belső rendezésnél, ami úgy is felfogható, hogy ez az ára annak, hogy a nagy adathalmazunk rendezése egyáltalán megvalósítható.

A következő ábrán a külső rendezés, pontosabban a külső, összefésüléses eljáráson alapuló rendezés (external merge sort) elvét és főbb lépéseit láthatjuk. Az egyes lépések számokkal jelennek meg és az ábra alján olvasható a szöveges kifejtésük.

Most egy olyan sort_large_dataset nevű függvényt készítünk, amely az ábrán bemutatott elvet követve egy adott nevű fájl soraiként tárolt véletlenszerű karakterláncokat képes valamilyen ismérv (pl. hossz) szerint sorban rendezni, és a rendezett adatokat tartalmazó fájlt egy megadott nevű fájlba menteni.

Ahhoz, hogy a függvényt tesztelni tudjuk, először létrehozunk egy nagyméretű fájlt a véletlenszerűen előállított karakterláncokkal. Ezek generálásához használjuk az előző, „Véletlenszerű karakterláncok előállítása” című bejegyzésben mutatott függvények egyikét, amely karakterismétlődést is megenged az előállított karaktersorozatokban. Alább látjuk, hogy e függvényt használva hogyan állítunk elő egy 100 millió sort tartalmazó kb 1GB méretű szövegfájlt, amelynek neve dataset.txt.

E fájl adatainak rendezését végző sort_large_dataset() függvény definíciója a következő. A megértést a részletes kommentek és a fenti ábra segítik. A működéshez Python 3.12+ verzió szükséges.

A függvényt az alábbi kódsorokkal teszteljük.

A rendezés eltart pár percig, és utána a hosszuk szerint rendezett karakterláncok a sorted_dataset.txt fájlban állnak elő. A helyes működés első ellenőrzőpontja, hogy a két fájl mérete egyezik-e. Ha nem, akkor biztos, hogy nem jó valami. Megnyugtató, hogy esetünkben ezek egyeznek. További teszthez a már említett groupby() függvényt használjuk. Ezzel azt nézzük meg, hogy adott karakterlánchossz hányszor fordul elő. Látható, hogy ez egyenletes eloszlású. Ezt is vártuk, hiszen a karakterlánc-előállító függvényben használt choice() egyenletes eloszlást produkál. A fájl sorainak ellenőrzéséhez pedig egyszerűen a hosszgyakoriságok összegét kell venni. Ez is egyezik a rendezendő adatfájl sorainak számával.

A forráskódok elérhetők a https://github.com/pythontudasepites/external_sorting linken is.

E bejegyzésben elsődlegesen a fájlkezelés, valamint speciális iterátorok és konténerek voltak a középpontban. Ezekkel a Python tudásépítés lépésről lépésre című e-könyvben részletesen a „Mentsük, ami menthető! – fájlok és mappák” fejezet valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet „Speciális iterátorok” és „Speciális konténer típusok” alfejezetei foglalkoznak.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.