Pázmány Péter Katolikus Egyetem Információs Technológiai és Bionikai Kar
Önálló laboratórium beszámoló/ Written report for Tutored Research Project
Név/Name: Gábor Csaba Attila
Neptun kód/ Neptun code: DU4POE
Képzés/ Program: MI BSc
Dolgozat címe/ Title of the report: Python technológiák adattudományhoz Konzulens(ek) neve/ Advisor(s): Dr. Lukács Gergely István
A hallgató a kitűzött feladatot megfelelő színvonalon és a kiírásnak megfelelően teljesítette./ The student has completed the project on the required level and according to the announcement. Az írásbeli beszámoló javasolt érdemjegye (számmal The written report’s offered grade (with number and letters): , .
_____________________________ Külső konzulens aláírása/ Signature of External Advisor
Budapest, 2017. május 8.
és
betűvel)
_____________________________ Belső konzulens aláírása/ Signature of Faculty Mentor
/
Pázmány Péter Katolikus Egyetem, Információs Technológiai és Bi onikai Kar
Python technológiák adattudományhoz Önálló laboratórium beszámoló
Konzulens: Dr. Lukács Gergely István
Szerző: Gábor Csaba Attila
Tartalomjegyzék 1. Bevezetés ................................................................................................................................ 3 2. Python programozási nyelv .................................................................................................... 4 2.1. Python verziók................................................................................................................. 4 2.2. Python telepítése ............................................................................................................. 4 2.3. Erőforrásigény vizsgálata ................................................................................................ 4 2.3.1. Memória ................................................................................................................... 4 2.3.2. CPU .......................................................................................................................... 7 2.4. Fordítóprogramok............................................................................................................ 8 3. Adatkezelő programkönyvtárak ............................................................................................. 9 3.1. Pandas.............................................................................................................................. 9 3.1.1. DataFrame objektumok ............................................................................................ 9 3.1.2. Pandas és SQL összehasonlítása ............................................................................ 10 3.2. Dask ............................................................................................................................... 12 3.2.1. Feladatvégrehajtási gráfok ..................................................................................... 12 3.2.2. Schedulerek ............................................................................................................ 14 3.2.3. Párhuzamosíthatóság .............................................................................................. 14 3.2.4. Elosztott rendszerek ............................................................................................... 16 4. Dask összehasonlítása más csomagokkal ............................................................................. 17 4.1. Pandas és Dask (DataFrame objektumok) .................................................................... 17 4.2. Egyéb csomagok ........................................................................................................... 19 5. Összegzés és kitekintés ........................................................................................................ 20 Irodalomjegyzék ....................................................................................................................... 21
1. oldal
Ábrajegyzék 1. ábra: Python dekorátor hozzáadása függvényhez memória megfigyelés céljából .............................. 5 2. ábra: memory_profiler futtatása, és a memóriaanalízis eredménye .................................................... 6 3. ábra: mprof parancsok ......................................................................................................................... 6 4. ábra: mprof parancs eredménye, memóriafogyasztás időben követve ................................................ 6 5. ábra: log fájl készítése line_profiler használatához ............................................................................. 7 6. ábra: szkript analízise processzorigény szempontjából ....................................................................... 8 7. ábra: Adatkezelő programkönyvtárak népszerűsége a Google Trends statisztikája alapján ............... 9 8. ábra: SQL lekérdezés és annak megvalósítása Pandas segítségével Python nyelven ....................... 10 9. ábra: adatsorok szűrése...................................................................................................................... 10 10. ábra: aggregáló függvények használata ........................................................................................... 11 11. ábra: INNER JOIN az alapértelmezett a merge() metódusnál ........................................................ 11 12. ábra: Dask felépítése [12] ................................................................................................................ 12 13. ábra: egyedi feladatvégrehajtási gráf tervezése, és eredménye ....................................................... 13 14. ábra: Amdahl-törvény, kapcsolat a processzorok és a teljesítménynövekedés között .................... 15 15. ábra: műveletek párhuzamosíthatósága ........................................................................................... 16 16. ábra: mérés során felhasznált számítógépek és adatok.................................................................... 17 17. ábra: adatsorok előállítása ............................................................................................................... 17 18. ábra: elemenkénti összeadás műveletigénye ................................................................................... 18 19. ábra: indexek beállítása ................................................................................................................... 18 20. ábra: DataFrame objektumok összekapcsolása ............................................................................... 19
2. oldal
1. Bevezetés A digitális világot több mint kétmilliárd ember és vállalkozás, valamint több millió eszköz, szenzor alkotja. Egy kutatás [1] szerint az elérhető adatok mennyisége két évente megduplázódik. 2020-ra várhatóan 44 zettabyte adat lesz hozzáférhető. Ekkorra az adatok körülbelül 37%-a fog olyan információt tartalmazni, amelynek megszerzése érdekében érdemes lehet az adatokat analizálni. Az adatok elemzésének jelentősége ezért ugrásszerűen nő. Az adattudomány feladata a rendelkezésre álló adatok elemzése tudományos módszerekkel. Célja választ nyújtani olyan kérdésekre, amely az adatokból nem nyilvánvaló, de megtudható. A produktív adatelemzés több terület ismeretét, felhasználását feltételezi, mint például adatvizualizáció, mesterséges intelligencia, programozás. Az adattudomány talán két legfontosabb eszköze a Python és az R programozási nyelv. Ez utóbbit az adattudósok nagyjából 40%-a, míg a Pythont több mint 60%-a használja munkája során [2]. Az elmúlt években az adattudomány egyik fő eszközévé a Python vált, ugyanis számos programcsomag áll rendelkezésre mind adatbányászathoz, adatfeldolgozáshoz, illetve vizualizáláshoz. Az adatelemzés egyik legnépszerűbb eszközcsomagja a Pandas [3], amely többek között a könnyű használhatóságának és az SQL-hez hasonló műveleteinek köszönhető. Éppen ezért a dolgozatomban ennek funkcionalitását elemzem, és vizsgálom a lehetséges alternatíváit. Ma már általános, hogy eszközeink több processzormaggal, vagy több processzorral is fel vannak szerelve, így lehetséges a műveletek párhuzamosítása a gyorsabb feladatvégzés érdekében. Egyelőre kevésbé használt, de a párhuzamos feldolgozásra képes programkönyvtár a Dask. Ez többek között a Pandas, a Numpy funkcionalitásához hasonló. Jelen dolgozat célja tehát a fent említett két említett programcsomag vizsgálata funkcionalitás és skálázódás szempontjából. Ennek során áttekintem a Python nyelv legfontosabb tulajdonságait, és bemutatom a programcsomagok vizsgálatához használható Python kiegészítőket. Majd áttekintem a két programcsomagot, azoknak funkcionalitását, végül mérésekkel segítségével igyekszem vizsgálni a bemutatott eszközök skálázhatóságát.
3. oldal
2. Python programozási nyelv 2.1. Python verziók A Python egy általános célú, nagyon magas szintű, interpretált programozási nyelv, amelyet a holland származású programozó, Guido van Rossum hozott létre az 1980-as évek végén [4]. A nyelv többféle programozási paradigmát támogat, így az objektumorientált, a procedurális, az imperatív metodikát, de akár MapReduce kódokat is írhatunk Pythonban [5]. Megalkotásakor az elsődleges cél a kód olvashatósága és a munka megkönnyítése volt a futási idővel szemben. Két fő családja van jelenleg használatban, ezek a 2.x és a 3.x [6]. 2008-ban jelent meg a 3.0 verzió, amely nem kompatibilis a korábbiakkal. Ezáltal új alternatívákat vezet be a régi, elavult megoldások helyett, illetve az implementációban is jelentős optimalizáció történt [7]. A dolgozatban megtalálható kódok az új, 3.6.0-s Python nyelven készültek.
2.2. Python telepítése A Python telepítésére több mód létezik. A standard telepítővel, a Python hagyományos implementációját, az úgynevezett CPythont lehet installálni. Ez egy C nyelven írt értelmező, amely a leginkább használt megvalósítás. Az Anaconda a CPython mellett több száz programkönyvtárat is telepít, amelyek hasznosak nagy mennyiségű adatok kezeléséhez, elemzéséhez és megjelenítéséhez. Ez a legnagyobb nyílt platform az adattudomány számára Python nyelven.
2.3. Erőforrásigény vizsgálata Ugyan a szkriptnyelv megalkotásakor nem a futási sebesség volt a középpontban, bonyolult műveletekkel, nagy adatmennyiséggel való munka során különösen fontos a program optimalizációja. Két sarkalatos pont szkriptek futtatása során annak memória- és processzorigénye. Több eszköz is rendelkezésre áll ezen erőforrások vizsgálatára, akár a standard könyvtárban is. A vizsgálatok során gyakran úgynevezett dekorátorokat kell használni. A dekorátor egy olyan függvény, amely függvényobjektumot vesz át argumentumként, és visszatérési értéke egy függvényobjektum [8]. Ezzel kényelmes megfigyelhetőséget kapunk és a szkript könnyen áttekinthető marad.
2.3.1. Memória A memóriafelhasználás alakulásának ellenőrzésére alapvetően két módszer létezik. A használt memória méretét időben is figyelemmel lehet kísérni, illetve részletes eredményt ad a kód soronkénti vizsgálata.
4. oldal
Ez utóbbi lehetőség talán alkalmasabb lehet optimalizáció során, hiszen könnyebben megtalálható a szkript azon része, amely nem megfelelő hatékonysággal működik. A memory_profiler egy olyan pure Python modul, amely alkalmas függvények, Python programok memóriafogyasztásának soronkénti ellenőrzésére, valamint lehetőséget nyújt az időben történő monitorozásra is. Az előbbi használata igen egyszerű, a vizsgálandó függvényt el kell látni a @profile dekorátorral, amelyet először importálni kell a modulból. Majd a szkriptet egy speciális szkripttel futtatva megkapjuk a részletes eredményt. A modul dokumentációjában szereplő példa [9] két listát allokál, majd a másodikat törli is. Ahogy az 1. ábrán is látható az első sorban importálva van a szükséges eszköz és az analizálandó függvény előtt szerepel a @profile dekorátor. A szkriptet a terminálban a -m memory_profile opcióval futtatva betölti a modult, az eredményét pedig a 2. ábrán látható módon jeleníti meg. A kimenet egy táblázat jellegű információ, amely első oszlopában a sorok száma olvasható. A második oszlop az aktuálisan használt memória nagyságát mutatja Mebibyte-ban, míg a harmadik oszlop a lefoglalt memória növekedésének mértékét adja meg. A könnyebb értelmezhetőség érdekében az utolsó oszlopban a vizsgált kódsor olvasható. Ahhoz, hogy a memory_profiler modul használatával a memóriahasználat időben is követhető legyen, az 1. ábrán látható szkript első sorát, a dekorátor importálását törölni kell. Ezután egy terminálban az mprof run parancs kiadása után a program futtatásáról egy log fájl készül, amelyből az mprof plot parancs (3. ábra) egy interaktív diagramot ad eredményül. A diagramon több paraméter beállítására is lehetőség van, az így kapott egyedi diagram PNG fájlba menthető. Az 1. ábrán szereplő példa eredménye a 4. ábrán látható. from memory_profiler import profile
@profile def my_func(): a = [1] * (10 ** 6) b = [2] * (2 * 10 ** 7) del b return a my_func() 1. ábra: Python dekorátor hozzáadása függvényhez memória megfigyelés céljából
5. oldal
> python -m memory_profiler profilerExample.py
Filename: profilerExample.py
Line #
Mem usage
Increment
Line Contents
================================================ 3
36.3 MiB
0.0 MiB
4
@profile def my_func():
5
43.9 MiB
7.6 MiB
6
196.5 MiB
152.6 MiB
7
43.9 MiB
-152.6 MiB
8
43.9 MiB
0.0 MiB
a = [1] * (10 ** 6) b = [2] * (2 * 10 ** 7) del b return a
2. ábra: memory_profiler futtatása, és a memóriaanalízis eredménye
> mprof run profilerExample.py mprof-script.py: Sampling memory every 0.1s running as a Python program...
> mprof plot Using last profile data. 3. ábra: mprof parancsok
4. ábra: mprof parancs eredménye, memóriafogyasztás időben követve
6. oldal
2.3.2. CPU Egy kód futásának sebességét természetesen nagy mértékben befolyásolja a CPU teljesítménye is, illetve, hogy adott utasításokat a processzor mennyi idő alatt tudja végrehajtani. Látható tehát, hogy ennek vizsgálata is igen fontos a program optimalizációja során, és programok, modulok összehasonlításának alapja lehet. A memóriafogyasztás megfigyelésének lehetőségeinél megismert, sorról sorra történő elemzés jó alap lehet. A Python-ban erre is lehetőség van. A line_profiler modul működése hasonló a memory_profiler csomaghoz, hiszen utóbbi ezen kiegészítő alapján készült. A line_profiler tehát monitorozza minden egyes kódsor futtatási idejét. Azért, hogy a profilozás okozta extra költség minél kisebb legyen, a csomagot C nyelven implementálták Cythonban. Az 1. ábra: Python dekorátor hozzáadása függvényhez memória megfigyelés céljából ábrán már bemutatott példán a processzorteljesítmény analizálása is bemutatható. Tehát ez esetben is el kell látni a függvényt @profile dekorátorral. A terminálba, az 5. ábrán is látható kernprof parancs segítségével a futtatásról egy log fájlba gyűjt információt a rendszer, amelyet felhasználva egy táblázatban megjeleníthető a profilozás eredménye. A 6. ábrán látható a szkript eredménye, amely táblázatos formában jelenik meg. A táblázat felett látható egy időegység adat, amely az egy-egy sorhoz rendelt időegység mértéke. Az első oszlop a sorok számát, míg utolsó oszlopa a szkript adott sorát jeleníti meg. A második oszlop mutatja, hogy az adott sor hányszor lett lefuttatva, amely jelen példában mindig 1, azonban könnyen elképzelhető egy ciklus, ahol egy sor többször is lefuthat. A következő oszlopban látható a teljes futási idő a fent megadott időegységben, emellett az is szerepel, hogy a teljes futási idő hány százalékát tette ki az adott sor utasítása. A line_profiler eszköztől még részletesebb adatot gyűjt a cProfile, amely adatokból a pstats segítségével lehet elemezni a szerzett adatokat. A dolgozatban azonban két programkönyvtár összehasonlítása a cél, ehhez alkalmasabb a már bemutatott line_profiler, így a későbbiekben ezt fogom használni. > kernprof -l profilerExample.py Wrote profile results to profilerExample.py.lprof 5. ábra: log fájl készítése line_profiler használatához
7. oldal
> python -m line_profiler profilerExample.py.lprof Timer unit: 6.03747e-07 s
Total time: 0.239018 s File:profilerExample.py Function: my_func at line 2
Line #
Hits
Time
Per Hit % Time
Line Contents
============================================================== 2
@profile
3
def my_func():
4
1
5
10586
10586.0
2.7
1
216962 216962.0
54.8
b = [2] * (2 * 10 ** 7)
6
1
168336 168336.0
42.5
del b
7
1
7
7.0
0.0
a = [1] * (10 ** 6)
return a
6. ábra: szkript analízise processzorigény szempontjából
2.4. Fordítóprogramok Ha a program sebessége nem megfelelő, és az erőforrásigény vizsgálata alapján a lehetséges optimalizáció megtörtént, van még további lehetőség a hatékonyság növelésére. Léteznek alternatív fordítók, amelyek C-szerű teljesítményt [10] ígérnek egy Python kódnak. Ezeket nevezzük optimalizáló fordítónak. Az optimalizáló fordító egy olyan fordító, amely megpróbálja minimalizálni vagy maximalizálni a futtatható program bizonyos attribútumait. A leggyakoribb követelmények a futtatás idejének, vagy a memóriafelhasználásnak a minimalizálása. A Numba egy optimalizáló fordító a Python programozási nyelvhez, amelynek célja a program sebességének növelése, a tömbökre, nehéz matematikai műveletekre fókuszálva [10]. Alacsony szintű virtuális gép (Low Level Virtual Machine) segítségével optimalizált gépi kódot állít elő. Ennek köszönhetően C-szerű teljesítményt lehet elérni anélkül, hogy nyelvet, vagy interpretert kellene váltani. A Cython egy statikus, optimalizáló fordító a Python és a kibővített Cython programozási nyelvhez [11]. Segítségével a Python és a C nyelvek előnyeit lehet kombinálni, többek között a statikus típusdeklarációval. Használatával a kód C nyelvre fordítható, így a Python kód gyorsasága is biztosított olyan esetekben, ahol a Python interpeter már nem lenne hatékony.
8. oldal
3. Adatkezelő programkönyvtárak 3.1. Pandas A Pandas egy nagy teljesítményű, könnyen használható, nyílt forráskódú programkönyvtár. A Python nyelven elérhető egyik legnépszerűbb adatkezelő bővítmény, a népszerűsége folyamatosan emelkedik (7. ábra), kiemelkedő a többihez képest. Népszerűségének egyik oka lehet a funkcionalitáshoz társuló sebesség, amely annak köszönhető, hogy a különböző függvények kritikus pontjai Cython vagy C nyelven készültek el. A csomag leginkább az adatok előkészítéséhez nyújt segítséget, de akár adatelemzéshez és megjelenítésre is használható, így anélkül lehet lefedni egy teljes munkafolyamatot, hogy szükség lenne a specifikusabb R nyelvre való áttérésre. 120 100 80 python dask: (Világszerte) 60
python blaze: (Világszerte) python pandas: (Világszerte)
40 20 0 22.04.2012
22.04.2013
22.04.2014
22.04.2015
22.04.2016
7. ábra: Adatkezelő programkönyvtárak népszerűsége a Google Trends statisztikája alapján
3.1.1. DataFrame objektumok A Pandas az adatokat DataFrame objektumokban tárolja, amely egy táblázatszerű adatstruktúrát tartalmaz, minden oszlopa különböző típusú lehet. Az adatok többek között csv, txt, excel fájlokból importálhatók, de támogatott akár SQL adatbázisból vagy a népszerű HDF5 formátumból történő olvasás. Ezen fájlformátumokba természetesen a feldolgozott adat mentése is megoldott. A DataFrame objektumok egyaránt rendelkeznek sor- és oszlopindexekkel, ezért a sor- vagy oszloporientált műveletek nagyjából szimmetrikusan végezhetők. Az index objektumok metaadatokat tárolnak az objektumról, amelyek a felhasználó által nem módosíthatók, többféle index használható különböző adattípusokra (Index, Int64Index, MultiIndex, DatetimeIndex, PeriodIndex). A MultiIndex
9. oldal
olyan indextípus, amely segítségével több dimenziós adatot egy alacsonyabb dimenziós struktúrában lehet tárolni, ez azt jelenti, hogy egy tengelyen több indexelési szintet reprezentál.
3.1.2. Pandas és SQL összehasonlítása Mivel az SQL az adattudomány egyik legszéleskörűbben használt nyelve, ezért fontos kérdés, hogy a Pandas mennyiben képes azt helyettesíteni az adatok elemzésében. Az alábbiakban példákon keresztül mutatom be, hogy néhány SQL lekérdezés hogyan írható meg Python nyelven Pandas segítségével. Természetesen a legalapvetőbb képességek, mint a projekció és a szelekció végezhető Pandas használatával is. A 8. ábrán látható, hogy előbbi SQL nyelven a SELECT utasítással végezhető, a Pandas esetén a kiválasztott attribútumok nevét egy listában át kell adni a DataFrame objektumnak. Az SQLből ismert * a teljes sémát megjeleníti, ez Pandas esetén lista, vagyis az attribútumok átadása nélkül érhető el.
SELECT smoker, time FROM tips; tips[[‘smoker’, ‘time’]] 8. ábra: SQL lekérdezés és annak megvalósítása Pandas segítségével Python nyelven
A WHERE kifejezés használható adatsorok kiszűrésére SQL-ben, erre a Pandas több lehetőséget is felkínál. Egyik lehetőség az úgynevezett logikai indexelés, amely során egy logikai vektor szűri az adatsorokat. Ez esetben a True címkéjű sorokat kapjuk eredményül. Természetesen az ÉS, VAGY műveletek is használhatók. A 9. ábrán látható példában két szelekciós feltétel szerepel, amelyeket Pandas esetén is egyszerűen össze lehet kapcsolni, a két szelekció logikai indexeléssel könnyen elvégezhető. Látható, hogy az egyes attribútumokra való szűréskor Pandas használatakor az objektum megnevezése ekkor is szükséges.
SELECT * FROM tips WHERE time = ‘Dinner’ AND tip > 5.00; tips[(tips['time'] == 'Dinner') & (tips['tip'] > 5.00)] 9. ábra: adatsorok szűrése
10. oldal
Gyakran használatosak SQL-ben a csoport függvények is a GROUP BY utasítással együtt. Erre a Pandas megoldása a groupby() metódus, és ennek paramétereként kell megadni a csoportosítani kívánt attribútum nevét, több esetén pedig egy listát kell megadni. Természetesen itt is lehet aggregáló függvényeket használni ezzel párhuzamosan (10. ábra). Az agg() metódusban egy dictionary segítségével lehet megadni, hogy mely attribútumokon milyen művelet hajtódjon végre. Látható, hogy a példában az átlagszámítás a numpy programkönyvtár használatával történt, nem a Pandas végezte a számítást.
SELECT day, AVG(tip), COUNT(*) FROM tips GROUP BY day; tips.groupby('day').agg({'tip': np.mean, 'day': np.size}) 10. ábra: aggregáló függvények használata
Többféle JOIN művelet végrehajtását is támogatja a Pandas a join() és a merge() metódusokkal. Előbbi művelet esetén alapértelmezetten az indexek alapján fogja azt végrehajtani. Mindkét metódusnál paraméterben megadható a JOIN művelet típusa (INNER, LEFT, RIGHT, FULL). A Pandas megengedi a FULL JOIN műveletet is, amely esetén minden sor szerepel az eredményben, akkor is, ha nem talált kapcsolódó sort, ennek érdekessége, hogy például a MySQL relációs adatbáziskezelő ezt nem támogatja. A 11. ábrán látható a merge() működése. Paraméterben várja a két DateFrame objektumot, majd a JOIN feltétel adható meg az on mezőnek, az összekapcsolás típusa pedig a how paraméterben adható meg.
SELECT * FROM df1 INNER JOIN df2 ON df1.key = df2.key; pd.merge(df1, df2, on='key') 11. ábra: INNER JOIN az alapértelmezett a merge() metódusnál
A csomag emellett további lehetőségeket is támogat, mint például az unió, frissítés, törlés, vagy akár különböző aggregáló függvények.
11. oldal
3.2. Dask A Dask egy skálázható, párhuzamos feladatvégzésre alkalmas programkönyvtár Python nyelven. A két fő komponense a nagymennyiségű adatok tárolására alkalmas adatszerkezetek, valamint a dinamikus feladatszabályozás. A Dask az adatokon, objektumokon végrehajtandó műveleteket feladatvégrehajtási gráfokkal reprezentálja, ezeket végül ütemezők megadásával hajtja végre (12. ábra).
12. ábra: Dask felépítése [12]
A 12. ábrán látható a Dask által megvalósított adatszerkezetek. Az array a numpy megvalósítására hasonlít, míg a bag többek között a PySpark lehetőségeit utánozza [12]. A DataFrame objektum a már tárgyalt Pandas eszköztárát valósítja meg párhuzamosított formában.
3.2.1. Feladatvégrehajtási gráfok Általános esetben a programok értelmezését a fordítóprogramok végzik, azonban párhuzamos végrehajtás során az ütemezés néha a fejlesztő felelőssége lehet. Ebben jelent segítséget a feladatokat egy gráf csomópontjaiként reprezentáló Dask [13]. A csomópontok közötti élek jelzik a feladatok közti függőségeket. A 13. ábrán látható egy rövid Python szkript, amely végrehatjási sorrendje a visualize() metódus meghívásával ellenőrizhető. A végrehajtási gráfon látható, hogy az y és z objektumok esetén is elvégzendő összeadás művelet, amelyet a Dask optimalizál, csupán egyszer végzi el a közös módosításokat, és csupán az összeg és átlag kiszámítása történik külön. Tehát a két objektum gráfja össze lett illesztve az optimális működés érdekében.
12. oldal
import Dask.array as da
x = da.arange(10, chunks=10) y = (x+15).sum() z = (x+15).mean() (4*y+z).visualize()
13. ábra: egyedi feladatvégrehajtási gráf tervezése, és eredménye
13. oldal
3.2.2. Schedulerek A Dask lehetőséget biztosít arra, hogy a feladatvégrehajtási gráfot magunk adjuk meg. Ha a gráf elkészült, kiválasztható a végrehajtás ütemezése. Az első ütemezési mód a Dask.threaded.get, amely egy processzen belül több szálat futtat. Ez az alapértelmezett scheduler többek között az Array és a DataFrame esetében. Numerikus számítások, vagy olyan komplex műveletek során, ahol sok a függőség, azért hasznos, mert az adat megosztása a memóriában nem jár költséggel. A Dask.multiprocessing.get lehetőséget biztosít arra, hogy a számítások külön processzeken fussanak le. Ez a módszer jól együttműködik a global interpreter lockkal, így például JSON elemzése jól párhuzamosítható. Ez az alapértelmezett scheduler bag adatszerkezet használatakor. Elosztott rendszeren történő feldolgozáshoz a distributed.Client.get alkalmazása szükséges. Fontos, hogy a klaszter létrehozása jelentős extra költség, azonban nagy számítások esetén sokkal jobban ütemezheti a számításokat. A Dask.async.get_sync, az eddigiektől eltérően nem párhuzamos feldolgozást végez. Főleg profilozás, hibakeresés esetén lehet hasznos.
3.2.3. Párhuzamosíthatóság A párhuzamos programozás megjelenésével több eredmény is született a párhuzamosítás hatékonyságának becslésére. Ezek közül kiemelkedő az Amdahl-törvény, amelyben Gene Amdahl vizsgálja szekvenciális és párhuzamos programok futtatásával, hogy milyen kapcsolat van a processzorok száma és a teljesítménynövekedés között [14]. Az Amdahl-törvény segítségével belátható, hogy a jól párhuzamosítható programok a processzorok számának növelésével jelentős mértékben gyorsíthatók. A törvény értelmében azonban egy bizonyos szint után nem érdemes a processzorok számát növelni, jobb eredmény már nem lesz elérhető (14. ábra). A Dask ugyan párhuzamosítja a feladatvégzést, de kérdéses, hogy milyen
műveletek
párhuzamosíthatók, illetve mennyiben tudja a végrehajtást gyorsítani. Vannak műveletek, amelyek párhuzamosítása triviálisan elképzelhető. Ilyenek például az elemszintű műveletek, vagy a sorszintű kiválasztás (15. ábra).
14. oldal
14. ábra: Amdahl-törvény, kapcsolat a processzorok és a teljesítménynövekedés között
Megfelelő tervezéssel, bonyolultabb műveletek is sikerrel párhuzamosíthatók, ha az adatok rendelkeznek index-szel, akkor azon keresztül akár objektumok összekapcsolása is hatékonyan elvégezhető. Az indexek beállítása ugyan költséges, de sok más metódus futtatását gyorsíthatja, így bizonyos esetekben hasznos lehet.
15. oldal
Triviálisan párhuzamosítható műveletek elemszintű műveletek (összeadás,
df.x +
szorzás, stb)
df.y
sorszintű kiválasztás
df[df.x > 5.00]
aggregálás
df.x.max() Párhuzamosítható műveletek
groupby-aggregálás
df.groupby(df.x).max()
értékszámlálás
df.value_counts()
duplikátumok kiszűrése
df.x.drop_duplicates() dd.merge(df1, df2, left_index=True,
index szerinti összekapcsolás
right_index=True) Lassabban végezhető műveletek
index beállítása
df.set_index(df.x)
nem index szerinti összekapcsolás
dd.merge(df1, df2, on=’names’)
különböző partíción lévő elemekkel végzett műveletek
df.x + df.y
15. ábra: műveletek párhuzamosíthatósága
3.2.4. Elosztott rendszerek A MapReduce egy olyan programozási modell, amely nagy adathalmazok párhuzamos és elosztott feldolgozására képes [15]. Tartalmaz egy map funkciót, amely kulcs-érték párokat dolgoz fel, majd közbülső kulcs-érték párokat állít elő. A reduce funckió pedig az összes közbülső értéket egyesít ugyanazzal a közbülső kulccsal [16]. A Google a világháló indexelésére is használ MapReduce modellt. A Dask.Distributed egy központilag irányítható, elosztott, dinamikus ütemező, amellyel komplex munkafolyamatok végezhetők, mint például MapReduce algoritmusok [17]. Az ütemező a műveleteket ott hajtja végre, ahol az adat van, így csökkenthető a késleltetés, minimalizálható a hálózati forgalom. A Dask tehát alkalmas lehet akár elosztott rendszereken történő feladatvégzésre, mint például a népszerű Hadoop keretrendszer. A Spark egy sokkal komplexebb rendszer, de létezik hozzá egy PySpark programkönyvtár, amellyel Python környezetből férhetünk hozzá a rendszerhez.
16. oldal
4. Dask összehasonlítása más csomagokkal 4.1. Pandas és Dask (DataFrame objektumok) A két programkönyvtár hatékonyságát méréssel vizsgáltam, amely során két számítógépet használtam (16. ábra: mérés során felhasznált számítógépek és adatok ábra). A mérések során arra kívántam rávilágítani, hogy a párhuzamosítás mennyiben képes gyorsítani a feladat végrehajtását. A műveletekhez egy olyan két oszlopból álló DataFrame objektumot használtam, amelynek mindkét oszlopában különböző egész számok voltak véletlenszerűen elrendezve. A méréseket több esetben is elvégeztem 10 millió, illetve 100 millió soros objektumokkal. A szkript, amely a mérés alapját adta, a 17. ábrán olvasható. mérési összeállítás
mérés 1
mérés 2
2
4
1,7
3,6
RAM mérete (Gb)
6
32
adatsorok száma (millió)
10
100
processzormagok száma órajel (GHz)
16. ábra: mérés során felhasznált számítógépek és adatok
import Pandas as pd import Dask.dataframe as dd from memory_profiler import profile from random import shuffle @profile def foo(myDataFrame): [mérendő művelet] x, y = [], [] for i in range(0, 10000000): x.append(i) y.append(i) shuffle(x) shuffle(y) DataSet = list(zip(x, y)) myDataFrame = pd.DataFrame(DataSet, columns=['x', 'y']) myDataFrame = dd.from_Pandas(myDataFrame, npartitions=1) foo(myDataFrame) 17. ábra: adatsorok előállítása
17. oldal
df.x + df.y 0,7
0,601293
futásidő (sec)
0,6 0,5 0,4
pandas
0,3 0,2 0,1
dask 0,13211 0,0051
0,002
mérés 1
mérés 2
0 18. ábra: elemenkénti összeadás műveletigénye
A 18. ábrán feltüntetett eredmények is jól szemléltetik, hogy egy objektumon végzett elemenkénti művelet (jelen esetben összeadás, de akár lehetne kivonás, szorzás, osztás, stb) hatékonyan párhuzamosítható. Hiába lett az adat az első mérés adatának tízszerese, a processzormagok száma pedig csak kétszerese, a futásidő nem változott a Dask esetén, míg a Pandas közel ötször annyi idő alatt végezte el a feladatot a második esetben. Egy szintén könnyen párhuzamosítható művelet az elemekre történő szűrés, a mérés során azon sorokat szűrtem ki, amelyben 50 ezernél nagyobb érték szerepelt, ezt a második mérési konfiguráció esetén 2,01 másodperc, míg a Dask 0,001 másodperc alatt végezte el.
df.set_index(df.x) 2,5
2,172
futásidő (sec)
2 1,5 pandas dask
1 0,45638 0,5
0,544823
0,166898
0 mérés 1
mérés 2
19. ábra: indexek beállítása
Az indexek beállítása egy nem hatékonyan párhuzamosítható folyamat, ekkor szükséges az adathalmaz újra rendezésére, amely költséges. Ez olvasható le a 19. ábráról is, az első mérési összeállítás esetén
18. oldal
háromszoros idő alatt végzett a Dask, a második esetben már négyszeres volt ez a különbség. Tehát azokban az esetekben, ahol a teljes objektum újrarendezése, szükséges a Pandas jobban teljesít. Ennek következtében a Dask nem valósít meg több műveletet, amelyet a Pandas igen. Ezek többek között rendezések, amelyek végrehajtása több esetben nem lenne hatékony. Fontos azonban megjegyezni, hogy éppen ezért a megalkotóknak nem is volt célja minden eljárás megvalósítása, csupán a jól párhuzamosítható, vagy legfontosabb (például set_index(), hiszen ezáltal néhány művelet mégis hatékony lesz) műveletek implementálása volt [12].
dd.merge(df1, df2, left_index = True, right_index = True) 4,2557
4,5 4
futásidő (sec)
3,5 2,8077
3 2,5
1,698
2
pandas dask
1,5 1 0,5
0,0039
0 indexek beállításának költségével
indexek beállításának költsége nélkül
20. ábra: DataFrame objektumok összekapcsolása
Egy szemléletes példa arra, hogy miért érdemes néha költséges műveletet végezni, az a DataFrame objektumok összekapcsolása az indexeik szerint. Objektumok JOIN kapcsolása nem végezhető hatékonyan Dask esetén, csak indexek használatával. A második mérési összeállítással a Dask közel kétszer több időt igényelt az indexek beállítására és az objektumok összekapcsolására (4,25 sec). Azonban, ha csupán a kapcsolás költségét tekintve a Dask több mint 400-szor gyorsabban hajtotta végre. Tehát könnyen belátható, hogy az indexek beállítása ugyan költséges, de ha több összekapcsolásra lesz szükség, akkor érdemes lehet az egyszeri beállítása.
4.2. Egyéb csomagok A DataFrame objektumok mellett a Dask további két lehetőséget is biztosít adatok tárolására. Ezek közül egyik az Array, amely elsősorban a Numpy Array mintájára készült el. A DataFrame típushoz hasonlóan itt is kisebb darabokat hoz létre a Dask, amelyeken párhuzamosan végezhető számolás. A Bag a PySpark RDD adathalmazához hasonló, amely struktúrálatlan vagy részben struktúrált adatokon (például JSON) végez egyszerű műveleteket.
19. oldal
5. Összegzés és kitekintés A beszámoló során igyekeztem áttekintést nyújtani az adattudomány alapvetéseiről, amelyre talán a legalkalmasabb eszköz a Python nyújtotta eszköztár. Könnyen belátható, hogy a Python széleskörű felhasználhatósága miatt egyre inkább kikerülhetetlenebbé válik az adattudomány világában. Ugyan már egyre inkább a skálázódó, párhuzamos adatfeldolgozás felé halad a technológia, a Pandas népszerűsége továbbra is töretlen. Míg a Pandas eszköztára sokkal nagyobb, amely kényelmesebbé teheti a munkát, addig a Dask ezen műveleteknek csupán csak egy töredékét tudja megvalósítani. Ahogy a dolgozatból kiderült, a Dask bizonyos esetekben sokkal hatékonyabb működést tud biztosítani, mint a Pandas. Tehát nem lehet egyértelműen nyilatkozni arról, hogy bármelyik csomag helyettesíthető lenne a kettő közül. Mindkettő hasznos segítséget adhat különböző munkák során. Természetesen a fenti kijelentések nem állapíthatók meg teljes bizonyossággal a mérési eredményekből, amelynek több oka is van. A műveletek hatékonysága nagy mértékben függhet az adatoktól is. Emellett hasznos lehet a mérések pontosítása. Több, különböző méretű adatsoron végzett mérés jobban megmutatná a Dask skálázhatóságát, amely ugyan így is megmutatkozott, de csak kisebb mértékben. A téma kiterjedtsége miatt több irányban is lehetséges a munkát folytatni. A dolgozat terjedelme miatt csak érinteni tudtam az olyan fontos témákat, mint például a MapReduce feldolgozás, amely felhasználása egyre csak terjed. Akár az elosztott rendszerek vizsgálata is érdekes lehet. A Dask ilyenfajta képességeit a gyakorlatban még nem sikerült kipróbálni, de érdemes lenne összevetni a Hadoop rendszerhez készített PySpark programcsomaggal a képességeit.
20. oldal
Irodalomjegyzék [1]
„Executive Summary: Data Growth, Business Opportunities, and the IT Imperatives | The Digital Universe of Opportunities: Rich Data and the Increasing Value of the Internet of Things”. [Online].
Elérhető:
https://www.emc.com/leadership/digital-universe/2014iview/executive-
summary.htm. [Elérés: 12-ápr-2017]. [2]
„Stack Overflow Developer Survey 2016 Results”, Stack Overflow. [Online]. Elérhető: http://stackoverflow.com/research/developer-survey-2016. [Elérés: 12-ápr-2017].
[3]
„Python
Science
and
Data
Analysis
|
LibHunt”.
[Online].
Elérhető:
https://python.libhunt.com/categories/309-science-and-data-analysis. [Elérés: 12-ápr-2017]. [4]
„The Making of Python”. [Online]. Elérhető: http://www.artima.com/intv/pythonP.html. [Elérés: 21-ápr-2017].
[5]
Blackmist, „Develop Python MapReduce jobs with HDInsight”. [Online]. Elérhető: [Elérés:
https://docs.microsoft.com/en-us/azure/hdinsight/hdinsight-hadoop-streaming-python. 21-ápr-2017]. [6]
„Python2orPython3
Python
-
Wiki”.
[Online].
Elérhető:
https://wiki.python.org/moin/Python2orPython3. [Elérés: 21-ápr-2017]. [7]
„Prognyelvek
portál”.
[Online].
Elérhető:
http://nyelvek.inf.elte.hu/leirasok/Python/index.php?chapter=1. [Elérés: 21-ápr-2017]. [8]
„Decorators
I:
Introduction
to
Python
Decorators”.
[Online].
Elérhető:
http://www.artima.com/weblogs/viewpost.jsp?thread=240808. [Elérés: 23-ápr-2017]. [9]
„memory_profiler
0.45:
Python
Package
Index”.
[Online].
Elérhető:
https://pypi.python.org/pypi/memory_profiler. [Elérés: 23-ápr-2017]. [10] „Numba — Numba”. [Online]. Elérhető: http://numba.pydata.org/#numba. [Elérés: 24-ápr-2017]. [11] „Cython: C-Extensions for Python”. [Online]. Elérhető: http://cython.org/. [Elérés: 24-ápr-2017]. [12] Dask Development Team, Dask: Library for dynamic task scheduling. 2016. [13] M. Rocklin, „Dask: Parallel Computation with Blocked algorithms and Task Scheduling”, in Proceedings of the 14th Python in Science Conference, Austin, Texas, 2015, o. 130–136.
21. oldal
[14] G. M Amdahl, „Validity of the single processor approach to achieving large scale computing capabilities”, in AFIPS Joint Computer Conference, California, 1967. [15] Zaharia Matei, M. Chowdhury, M. J. Franklin, S. Shenker, és I. Stoica, „Spark: Cluster Computing with Working Sets”, in HotCloud’10 Proceedings of the 2nd USENIX conference on Hot topics in cloud computing, Boston, MA, 2010, o. 10. [16] J. Dean és S. Ghemawat, „MapReduce: Simplified Data Processing on Large Clusters”, in Communications of The ACM, 2008, o. 107–113. [17] „Dask.distributed — Dask.distributed 1.16.3+1.g24aa4bf documentation”. [Online]. Elérhető: https://distributed.readthedocs.io/en/latest/index.html. [Elérés: 07-máj-2017].
22. oldal