Big Data elemzési módszerek házi feladat dokumentáció Készítette: Novák Gergely és Vuchetich Bálint Konzulens: Kocsis Imre Budapest, 2015. december 10. 1
Feladat A termékcsoportok forgalmának időbeli összehasonlítása. Adatsor A Kaggle Acquire Valued Shoppers Challenge adatsorából dolgoztunk, amely itt található meg: https://www.kaggle.com/c/acquire-valued-shoppers-challenge/data A feladat bemenő adathalmaza egy 19,8 gigabájtos, vásárlási tranzakciókat tartalmazó CSV fájl a következő attribútumokkal: id - egyéni vásárlói azonosító chain - áruházlánc azonosító dept osztály: a category mező aggregált csoportosítása (pl. víz category - termék kategória (pl. ásványvíz company - a eladó cég azonosítója brand - márka azonosítója date - a vásárlás dátuma productsize - a termék mérete (pl. 1,5l ásványvíz productmeasure - a termék mértékegysége (pl. liter purchasequantity - a vásárolt termék-egységek száma purchaseamount - a vásárlás értéke dollárban Ezek közül a feladathoz nekünk csak a vásárlás dátumára, a vásárolt mennyiségre és a kategóriára van szükségünk. A két lehetséges felbontású kategória változó ( dept és category közül a nagyobb felbontásút, az aggregált kategóriát ( dept választottuk, mert annak a kardinalitása is százas nagyságrendben van, ami szerintünk a megjeleníthetőség felső határát súrolja (a category értékkészletének számossága a kezelhetetlen tízezres nagyságrendben van. Adatfeldolgozás A Unix Shell-es részeket lokálisan használtuk, míg a mappert és reducert Python-ban írtuk, és az AWS-en futtatuk. Első nekifutásra RHadoopban próbálkoztunk, ott viszont olyan akadályba ütköztünk, hogy a nagy mennyiségű adat miatt kevésnek bizonyult a lokálisan rendelkezésünkre álló 12 GB memória, ezért döntöttünk emellett a technológia mellett. Előfeldolgozás cat transactions.csv awk -F, '{ print $7 "," $3 "," $10 }' > transactions_filtered.csv Beolvassuk soronként, majd a nekünk szükséges mezőket (dátum, kategória, mennyiség adjuk csak tovább. 2
Amazon Web Services Feltöltöttük az előfeldolgozott fájlt S3-ra (Amazon Simple Storage Service, majd indítottunk egy klasztert három m3.xlarge géppel (egy master és két core. Az EMR-en (DElastic MapReduce szerencsére van olyan beépített funkció, amivel lehet Python Streaming taskot futtatni. Amint végzett a step-pel, kigenerálta a logokat, és a megadott S3 bucket-ben megjelentek a kimeneti fájlok. mapper.py #!/usr/bin/python import sys import datetime 3
for line in sys.stdin: line=line.split("," if line[0]!= "": int(line[0][5:7], line[0] = line[0][:4] + '-' + str(datetime.date(int(line[0][:4], int(line[0][8:10].isocalendar([1] print('%s\t%d' % (line[0] + ',' + line[1], int(line[2] Beolvassunk az adatokat a standard inputról, majd minden sorra (ha nem üres, átalakítjuk a dátumot 2012-01-02 formátumról 2012-01-re, ahol a második tag a hetet jelöli. Ezt követően kiírjuk a dátumot, a kategóriát és a mennyiséget. reducer.py #!/usr/bin/python import sys lastkey = None current = 0 for line in sys.stdin: line = line.strip( key, value = line.split('\t' try: value = int(value if key!= lastkey: if lastkey is not None: print ('%s\t%d' % (lastkey, current lastkey = key current = 0 current += value except ValueError: pass if lastkey is not None: print ('%s\t%d' % (lastkey, current A reducer kulcsok szerint sorban kapja a bemeneteket, így nincs más dolgunk, míg összegezni ezeket kulcsonként. Minden kulcsra az összegzés addig fut, amíg egy újfajta kulcsot nem kap a bemeneten, ekkor stdout-ra kiírja a kulcsot és az összeget. 4
Utófeldolgozás cat 2015-12-04-2011/part-* tr "\\t" "," > depts-sold.csv A kapott adatokat összefűzzük egy fájlba, az elosztott rendszer tulajdonsága, hogy a kimenetet több fájlba írja ki. Vizualizáció Az adatok előszűrése és aggregálása után a feladat következő része a vizualizáció megvalósítása volt. A célunk az volt, hogy látványos, interaktív és a specifikációt maradéktalanul kielégítő vizualizációs technikát alkalmazzunk. A feladat egy triviális megközelítése volt egy olyan diagram, amelynek vízszintes tengelye az időt (2012 április 2013 július, függőleges tengelye pedig az adott napon/héten eladott darabszámot mutatja. Az egyes kategóriák egy-egy ebben a koordinátarendszerben különböző színekkel megjelenített függvény (idősor. Végül a lehetséges aggregációs szintek közül két okból döntöttünk a heti felbontás mellett: egyrészt így globális, nem pedig heti trendekre tud fókuszálni a megjelenítés (a hétköznapok/hétvégék okozta rezegtetést kisimítjuk, másrészt lényegesen kevesebb adatot kell a memóriában tartanunk. Adatok beolvasása Az előkészített adatok beolvasását a következő kódrészlet végzi el: df = read.csv("data/depts-sold.csv" df$date = as.date(as.posixlt(paste(df$year, df$week, 1, format = "%Y %U %u" df = df[, c("dept", "date", "amount"] df = df[with(df, order(dept, date,] Az adattáblában a year oszlop az évet, a week pedig azon belül a hét sorszámát jelöli. A kód második sora ezekből létrehoz egy date attribútumot, majd a harmadik eldobja a többit és csak ezt tartja meg. Shiny Az alkalmazáshoz a Shiny webes keretrendszert használtuk. A keretrendszer megismeréséhez végigcsináltuk a http://shiny.rstudio.com/tutorial/ oldalon található tutorial sorozatot. Minden Shiny alkalmazás (legalább egy ui.r és egy server.r állományból áll: az ui.r a webes felület kialakításáért, a server.r pedig a szerveroldali logikáért felelős. Esetünkben a webes felületen a megjelenítendő kategóriákat tudjuk kiválasztani, a logika pedig kirajzolja azt. Az alábbi kód definiálja a webes felületet: # ui.r library(shiny library(plotly 5
shinyui(fluidpage( titlepanel("shopper", sidebarlayout( sidebarpanel( checkboxgroupinput("selected_depts", label = h4("categories", inline = TRUE, choices = NULL, tags$style(type="text/css", ".checkbox-inline { margin-left: 10px; min-width: 40px } ", mainpanel( plotlyoutput("plt" Látható, hogy az oldalsávon levő checkbox csoport értékei (choices nem kerül definiálásra: azt majd a szerveroldali komponens fogja elvégezni a betöltött adatok alapján. A CSS azt a problémát oldja meg, hogy a jelölőnégyzetek asszimetrikusan helyezkedjenek el: A checkboxgroupinput alapértelmezett megjelenítése A CSS-el igazított megjelenítés 6
Plotly A server.r a jelölőnégyzet csoport feltöltését és a diagram kirajzolását végzi. shinyserver( function(input, output, session { updatecheckboxgroupinput(session, "selected_depts", choices = choices, inline = TRUE output$plt = renderplotly({ validate( need(length(input$selected_depts > 0, "Please select at least one category!" depts = df[df$dept %in% input$selected_depts, ] depts$dept = factor(depts$dept p = plot_ly(depts, x = as.date(date, y = amount, color = dept layout(p, title = "Sells of product categories", xaxis = list(title = "Date", yaxis = list(title = "Amount", showlegend = TRUE } } Megjegyezzük, hogy a fejlesztés során sokáig az add_trace( függvénnyel próbáltuk a különböző kategóriákhoz tartozó függvényeket megjeleníteni, annak használatával azonban mindig csak az utolsó kategória jelent meg (annyiszor, ahányat kiválasztottunk. Nem találtunk olyan dokumentációt, amely ezt a jelenséget megmagyarázta volna, mindenesetre az add_trace( a mi tapasztalatunk szerint csak statikusan ( több kódsorból hívva működik. Végül a fenti kódban is látható megközelítést választottuk: az egyes kategóriákat (dept a color attribútumon keresztül szeparáljuk. Eredmény Az elkészült alkalmazást a shinyapps.io weboldalon keresztül publikáltuk a https://rach.shinyapps.io/shopper címen. 7