Tuesday, July 31, 2012

Pur PDI ile Takvimleme

Genellikle takvimleme (scheduling) islemleri cron veya benzeri bir dis program tarafindan yapilir. Ama bir teknoloji egzersizi olarak takvimlemenin tamamini da Pentaho Kettle, PDI ile yapsak olur muydu?

Ustteki gibi bir islem (job) yapisi bunu halledebilir. Basladiktan sonra bir Shell isletiyoruz, icinde Unix cagrisi "date +%H%M > /tmp/sysdate" yapiliyor. Bu cagri mesela saat 14:20 icin 1420 sayilarini /tmp/sysdate icine yazacak. Bir dosya daha var, bu dosya takvimleme dosyamiz, hangi saat ve dakikayi bekledigimizi buraya yazacagiz. Diyelim ki 15:00 icin 1500 ve bu dosya ismi /var/takvim. Bu dosyayi onceden hazir ediyoruz.

O zaman beklenen ana gelip gelmedigimiz basit bir dosya karsilastirmasindan ibaret olacak. Bunun icin de bir Kettle ogesi var, File Compare. Iki dosya ismi veriliyor, bu ornekte /tmp/sysdate ve /var/takvim ve eger dosyalar esit ise yesil ok takip ediliyor, degil ise kirmizi cizgi. Bizim takvimleme islemi icin kirmizi cizgi "beklemeye devam et" anlamina gelir, beklemek icin de bir oge var zaten, Wait For. Bu kutuya ne kadar beklenecegini soyleyebiliriz, mesela 5 saniye, ve ardindan islemi tekrar basa postalariz (yani Shell cagrisina) boylece en son tarihi tekrar aliriz, sonra onu kontrol ederiz... boyle devam ederiz. Islemin sonunda gorulen Success noktasi yerine takvim aninda calistirmak istedigimiz esas islemler olabilir, mesela bir yerlere mail gondermek, bir tablo okumak, vs gibi.

Pentaho ile Tablolari Senkronize Etmek

Veri ambarlarini olusturan ETL isleminin onemli bir kismi ham verinin bir noktadan bir noktaya aktarilmasidir. Cogunlukla kaynak veri ambara "yakin" bir taban uzerinde bir ara bolgeye (staging) alinir, ve bu ara bolgedeki veri, kaynagin birebir kopyasi olmalidir.

Peki bu ara bolgeye sadece "degisen" verilerin alinmasi daha iyi olmaz miydi? Boylece tum kaynak verisinin tamamini, surekli almak zorunda kalmazdik.

Pentaho Data Entegrasyon (PDI) araci boyle bir ozelligi sagliyor.

Once ornek veri

create table x_target( id text,  value text );

create table x_source ( id text, value text);

truncate table x_source;
insert into x_source values('1','111');
insert into x_source values('2','222');
insert into x_source values('3','333');
insert into x_source values('4','444');
insert into x_source values('5','555');


Iki tane tablo yarattik, biri kaynak (source), digeri hedef (target). Amacimiz hedefi kaynak ile surekli birebir ayni tutmak ve guncelleme isleminin sadece farklari almasi. Farklar ne olabilir? ETL genellikle geceleri isletilen islemlerdir, diyelim ki onceki gece ETL isleyip bittikten sonra kaynaga eklenen yeni verileri alacagiz. Ya da mevcut kayitlarin bazilari guncellenmis olabilir onlari alacagiz, ya da son olarak kaynakta bazi silme islemleri yapilmis olabilir, bu silme islemlerini aynen biz de ara bolgede yapmaliyiz.

PDI ile bu islemi soyle yapariz.


Dort tane gorsel ogeye ihtiyacimiz var. Oncelikle iki tabloyu kaynak olarak tanimlamayi saglayan Input | Table input ogesi. Bu ogeyi iki kere ekliyoruz, biri kaynak digeri hedef icin. Kaynak icin mesela "Table input source" tanim olarak veriyoruz, hangi veri tabanina baglanilmasi gerektigini "Connection" ile tanimliyoruz, ve verinin hangi SQL komutu ile alinacagini belirtiyoruz. En basiti SELECT * FROM tanimi kullanmak.



Simdi bu iki tablonun farkinin bulunmasina / yaratilmasina / alinmasina gelelim. Joins | Merge Rows (diff) ogesini kullanalim.



Bu ogeye hedef tablosu (Table input ogesi olarak) "Reference rows origin" olarak, kaynak ise "Compare rows origin" olarak veriliyor, hemen bir liste uzerinden secilebilir haldeler, cunku Kettle GUI'si icinde oklarla baglantiyi yapmistik. Flag fieldname ise bu karsilastirma, birlestirme (merge) isleminin ek olarak kendi yarattigi yeni bir kolon. Bir hayali kolon denebilir. Bu kolona ismini biz veriyoruz, basit olarak "flagfield" ismini verdik. Bu kolon, karsilastirma islemi sonrasi her farkli satir icin yeni (new), degisti (changed), ve silindi (deleted) bilgisini ekleyecektir, boylece bir sonraki basamagin bu bilgi ile gerekli islemi yapmasini saglayacaktir.

Bu karsilastirma tabii ki her iki tarafta birbirine uyan satirlar uzerinde yapilacaktir, yani mesela guncellenme kontrolu icin ID'si iki tarafta ayni olan satirlara bakilacaktir. Bu sebeple Merge islemine bu ID kolonunun hangisi oldugu soylenmeli. "Keys to match" ile bu tanimlanir. Sonra, bu ayni ID'ye sahip satirlarda hangi degerlerin degisip degismediginin kontrolunu de "Values to compare" ile tanimlariz. Bu kolon isimlerini elle haldur huldur yazmaya gerek yok bu arada, en alttaki "Get key fields" ve "Get value fields" dugmeleri otomatik olarak tum listeyi alir, sonra arasindan istenmeyen kolonlar silinebilir.

Ozet olarak Merge islemi sadece farkli olan satirlari bulur, ve hayali bir kolon da ekleyerek bir sonraki basamaga aktarir.

En son islem "birlestirme sonrasi senkronize (syncronize after merge)". Bu oge birlestirmeden gelen veriyi aliyor, ve eklenen hayali kolon uzerinden farkli satirlarla gerekeni yapiyor.


Senkronizasyon icin hedef tablosu tanimlaniyor, taban baglantisi veriliyor. Yine ID tanimi var, ama bu sefer birlestirme isleminden gelen veri akintisinin (stream) icindeki ID ismi. Gerci farketmiyor cunku hep ayni isim kullandik, "id" kolonu. Bu kolon, hedef tablosundaki yine "id" isimli kolona uyduruluyor. Hangi kolonlarin guncellenecegi Update fields ile tanimli.

Advanced tab'i uzerinde birlestirmeden gelen hayali kolonun ismi tanimlanir, flagfield demistik onu veriyoruz, ve hangi degerlerin hangi isleme sebep olacagi da burada tanimli. 



Bu kadar. Bu arada, bu dort oge bir transformasyon olarak tanimli, yani Kettle ile New | Transformation secerek bunlari tanimladik. Bu transformasyonu kaydedin, ve sonra bir islem (job) yaratin, New | Job ile. Pentaho bu iki kavrami birbirinden farkli tutuyor, transformasyon sofistike veri eslemeleri (ustte gordugumuz gibi), arayuzler, karar mekanizmalari, vs. gibi isler icin dusunulmus, islemler ise adi ustunde, daha "islemeye" yakin ogeler. Baslama, bitirme, bekleme, mail gonderme, dosya idaresi gibi daha alt seviye islemler. Bir islem, bir transformasyonun cagirabiliyor tabii ki. Herhalde modularizasyon acisindan da bu ayrim isabetli olmus (ayni transformasyon farkli islemler tarafindan cagirilabilir boylece).


Islem cok basit, baslayacak, ve transformasyonu cagiracak. Hangi transformasyonunun cagirilacagini Transformasyon ogesinin uzerinde tiklanarak belirlenir, Kettle ile transformasyonu hangi ktr dosyasinda kaydettiysek, o dosyayi seceriz.

Simdi tum islemi mesela etl olarak kaydedelim (ve Kettle etl.kjb dosyasi yaratsin) ve soyle baslatalim,

sh [PDI DIZINI]/kitchen.sh -file=etl.kjb -norep=Y -log=/tmp/etl.log

Simdi veriyle oynayin, mesela insert ifadelerindeki tanimlari degistirin, veriyi tekrar yukleyin, ve ustteki ETL'i isletin. Ya da silme islemi yapin. ETL isletin. Hedef ve kaynagin birbirine surekli uyumlu tutuldugunu goreceksiniz.

Not: Ornek icin hedef ve kaynak ayni taban icinde yaratildi, fakat kaynak uzaktaki bir makina olabilir, Japonya'daki bir taban ile Istanbul'daki bir yerel tabloyu senkronize ediyor olabilirsiniz!

Ayrica hem kaynak hem hedef icin Table input, yani tablo girdisi kullandik ama kaynak bir CSV dosyasi da olabilirdi mesela, yani CSV ile tablo senkronize ediyor olabilirdik. Bu durumda Kettle ogelerinden sadece tek girdi degisirdi, transformasyonun geri kalani ayni kalirdi.

Kaynak

Sunday, July 22, 2012

Dongu Yazmamak, Fonksiyonel Diller, Python

Buyuk miktarda veri islerken "for" komutunu kullanan dongulerden kacinmak iyi olur. Python'un fonksiyonel formati ve kutuphanelerin sagladigi hizmet zaten daha kisa / temiz sekilde listeler, numpy vektorleri, matrisleri uzerinde islem yapilmasini saglar. Ama daha onemlisi, bu tek cagrilik fonksiyonel kullanimlarin arka planda C ile isleyen kodlara direk gitmesidir. Yani hizli isleyeceklerdir.

Mesela elimizde bir kelime listesi olsun, bu listenin her elemaninin her diger elemanina olan Levenshtein uzakligini hesaplayip bir matrise yazacagiz. Ilk yaklasim hemen

for w1 in words:
   for  w2 in words:

diye bir dongu yazar. Bu cok yavas isler cunku dongulerin kendisi Python icindedir. Daha iyisi itertools.product(words, words) ile kelimelerin tum kombinasyonunu hesaplatmaktir. Avantaj bir, itertools.product C ile kodlanmis ve hizli. Iki, geriye dondurulen bir oge gezici (iterator) ve her istek icin tek eleman uretiyor, tum hafizayi tum sonuc ile bir anda doldurmuyor.

Simdi bu kombinasyon uzerinde uzaklik hesabini itertools.imap(f, ...) ile isletiriz. f fonksiyonu

f = lambda (x,y): leven.distance(x,y)

olarak tanimlanir, distance daha once bahsettigimiz mesafe hesabi, o da C ile isleyen bir kodda. Bakis acisindaki degisime dikkat: dongu her seyi kontrol etmiyor, imap fonksiyonu, dongu ve veriyi "eslestiriyor", birini alip otekine uyguluyor. Bu isleyince elimizde mesafeler var, ve oge gezici uzerinden bu hesaplar isteyene veriliyor. Peki geziciden Numpy matrisi nasil olustururuz? np.fromiter ile.

words = np.array(['filan', 'fisman', 'sisman', 'paspas'])
(dim,) = words.shape
f = lambda (x,y): leven.distance(x,y)
res=np.fromiter(itertools.imap(f, itertools.product(words, words)),
                dtype=np.uint8)
A = np.reshape(res,(dim,dim))

Koda tekrar bakarsak, product, imap, fromiter ve leven.distance cagrilarinin hepsi C icinde isliyor. Yani hesap oldukca hizli olacak. Genel olarak kodlama felsefesi de soyle (degismeye basladi). Python, Ruby gibi diller fonksiyonel kodlamaya izin veren, onu ozendiren diller, dongunun veri isledigi degil, fonksiyonlarin dongulere parametre olarak verildigi bir yaklasim bu. Aslina bakilirsa modularite acisindan mantikli. Donguler de cogunlukla birbirine benzeyen ve bir kere kodlayip bir daha kodlamak istemeyecegiz seyler.

Ayrica performans acisindan da veri analiz kodlarini hep "C icinde tutmak" icin, ustte gordugumuz gibi, fonksiyonel tarz daha one cikiyor.

Not: Gezicilere bir daha deginelim. Ustteki cagri zincirinde surekli gezicilerin dondurdugu tek eleman uzerinde islem yapiliyor, ve gezicilerin kodlari C icinde. Eger tek bir gezici, ufak veri olsaydi gezici kullanimi pek bir fark yaratmayabilirdi. Ama onbinlerce ogeli bir vektor uzerinde arka akraya bir suru islem uyguluyor olsaydik, gezici olmadigi durumda her basamakta 10,000 uyeli bir vektor daha yaratiyor olurduk. Bu hem hafiza hem CPU israfi demek olurdu.

Thursday, July 19, 2012

Kelime Benzerligi - Levenshtein Mesafesi

Kelime benzerligini bulmak harfleme (spelling) hatalari duzeltmekten, benzer kelimeleri gruplamaya kadar pek cok alanda kullanisli olabilir. Gruplama algoritmalari cogunlukla grupladiklari "seyler" arasindaki mesafeyi bir sekilde hesaplayan bir fonksiyona ihtiyac duyarlar. Levenshtein mesafesi kelimeler icin bu olcutlerden biridir. Aslinda fikir basit, "en az kac degistirme operasyonu ile birinci kelimeden ikinciye ulasabiliriz". Kac tane operasyon kullanildiysa, mesafe o'dur. Mesela pizza kelimesinden piazzo kelimesine 2 basamakta gelinebilir. Once 'a' silinir sonra 'o' harfi 'a' yapilir.

Levenshtein icin en hizli kodlardan (arka planda C ile kodlanmis) surasi var

https://code.google.com/p/pylevenshtein/

Kurulur, test icin

from Levenshtein import *
print distance("pizza", "piazzo")


Pur Python ile

''' Calculates the Levenshtein distance of 2 strings'''
def printMatrix(m):
    print ' '
    for line in m:
        spTupel = ()
        breite = len(line)
        for column in line:
            spTupel = spTupel + (column, )
        print "%3i"*breite % spTupel

def levenshtein(s1, s2):
  l1 = len(s1)
  l2 = len(s2)

  matrix = [range(l1 + 1)] * (l2 + 1)
  for zz in range(l2 + 1):
    matrix[zz] = range(zz,zz + l1 + 1)
  for zz in range(0,l2):
    for sz in range(0,l1):
      if s1[sz] == s2[zz]:
        matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz])
      else:
        matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz] + 1)
  print "That's the Levenshtein-Matrix:"
  printMatrix(matrix)
  return matrix[l2][l1]

if __name__ == "__main__":

    s1 = "pizza"
    s2 = "pioazza"   
    distance = levenshtein(s1, s2)       
    print 'The Levenshtein-Distance of ',s1, ' and ', s2, ' is ', distance

    s1 = "hamburger"
    s2 = "haemmurger"   
    distance = levenshtein(s1, s2)       
    print 'The Levenshtein-Distance of ',s1, ' and ', s2, ' is ', distance
   

Veri Transferi Tasarimi, Pentaho, Spoon, ETL

Verileri network uzerinden baska makinadan, belki dosya formatinda alip, onu tabana yuklemek, oradan alip baska bir tabana aktarmak, vs gibi isler ETL adi ile anilir. ETL'in her parcasini olusturan parcalari ayri ayri elle kodlayabilirsiniz, fakat bu parcalarin bir araya konulmasi icin bir aracin kullanilmasi iyi olur. Pentaho Spoon ve Kitchen bu is icin uygun.

http://sourceforge.net/projects/pentaho/files/Data%20Integration/

adresinden indirilir. Acinca data-integration adinda bir dizin goreceksiniz. Dizine girip

sh spoon.sh

ile gorsel araci baslatabilirsiniz. New | Job ile bir ETL tanimlamaya hemen baslayabilirsiniz.



Solda gorulen ogeler surukle-birak ile sag tarafa aktarilabilir. Ogelerin uzerine mouse getirilince sola, saga baglanti yapmanizi saglayacak secimler gorulecek.


Yesil secim o basamagin basarisi durumunda ne yapilacagini tanimlar. O oktan sonra ikinci kez ok secimi yaparsaniz, bu sefer basarisizlik halinde nereye gidilecegi tanimlanir, bu iki ok sirasiyla yesil, kirmizi olarak gozukecekler.

Bizim en cok kullandigimiz oge Scripting altindaki Shell ogesi. Cunku python bazli SQL yukleyip cagiran (arada onu degistiren) bir suru script bazli kodumuz vardi, ve bunlari en rahat entegre etmenin yolu ustte gorulen Shell ogesi ile oldu.

ETL tanimlaninca onu bir isimle kaydedersiniz, bir dosya.kjb dosyasi olacak. Bu dosyayi alip

sh kitchen.sh -file=dosya.kjb -norep=Y  -log=/tmp/etl.log

ile isletebilirsiniz. Yani spoon tasarlar, kitchen isletir.

Simdi daha cetrefil tasarimlara gelelim. Eger komut satirindan (kitchen) ile ETL'e parametre gecmek istiyorsaniz, Edit | Settings ile Parameters tab'ina gidin, ve burada parametre ismini tanimlayin. Bu parametre neler yapabilirsiniz? Mesela script ogesinde General tab'inde "Insert script" secin, ve Script tab'inde

sh /falan/filan/dosya-${param1}.sh

gibi bir kullanim olabilir, yani param1 ne ise ${param1}'in yerine o gececektir.

Ya da, Simple evaluation ogesini secersiniz,


ve degisken X degerine sahip ise ETL bir yone gitsin seklinde akis yonunu degistirebilen karar noktalari tanimlayabilirsiniz. Yani programlamadaki "if" komutunun benzerini gorsel ETL'de kullanabilirsiniz.

Disaridan parametreyi gecmek icin kettle cagrisi soyle olur

sh kitchen.sh -file=dosya.kjb -norep=Y -param:param1=patates  -log=/tmp/etl.log

Birden fazla parametre icin birden fazla -param ifadesi eklenebilir.

Not: Baslangic noktasini tanimlamak icin solda General altinda START ogesini en basa koymayi unutmayin.


supervisord

Unix'te surec (process) isletimi, onlarin takibi icin faydali bir program, supervisord. Bu tur programlarin en cok aranan ozelliklerinden biri eger takip ettikleri program cokmus ise onu tekrar baslatabilmeleri. Cok uzun suren bir toptan isleyici (batch) programi mesela tekrar baslatilabilir (restartable) yapabilirsiniz, ve eger bir sekilde cokerse programi baslatinca kaldigi yerden devam eder, ve coken programi tekrar baslatacak olan sey supervisord gibi bir program olacaktir.

http://supervisord.org/index.html

Kurmak icin en rahat yol

sudo easy_install supervisor

Alttaki

echo_supervisord_conf

komutu ile ornek bir ayar dosyasi urettirebilirsiniz.

Bir ornek

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false

[program:mytest]
command=python -u /home/burak/data/scripts/test_supervisord.py
autorestart=true
user=postgres
redirect_stderr=true
stdout_logfile=/tmp/test_supervisord.log


Bir mytest adinda programi baslatip kontrol ettiriyoruz, log dosyasini tanimliyoruz, autorestart=true ile hatali sekilde surec cokerse tekrar baslat diyoruz, ve "hangi kullanici uzerinden" alt programin baslatilmasini gerektigini bile tanimlayabiliyoruz. Bu durumda supervisord root olarak baslatilmali tabii, yoksa istedigi diger kullaniciya gecis yapamaz.

Baslatmak icin

sudo supervisord

komutu yeterli.

Kontrol edilen tum programlari (supervisord ile beraber) oldurmek icin supervisord'nin PID'ini ps -eaf | grep supervisord ile bulun, ve

sudo kill -SIGTERM [PID]

Not: Yukaridaki program taniminda python'u -u secenegi ile kullandik, bu secenek ile "print" gibi stdout'a cikan ifadeler derhal "disari atiliyor (flushed)". Bu yapilmazsa cocuk surec isliyor ve ekrana mesaj basiyor olsa bile log'da hic bir sey gozukmeyecektir.