Friday, July 26, 2013

Veriyi Ayriksallastirmak (Discretization)

Pandas ile eldeki veriyi ayriksallastirmak icin herhangi bir sayisal kolon uzerinde qcut kullanilabilir. Bu cagri istatistiksel bolgeler (quartile) uzerinden hesap yapan bir cagridir, belli bir kolon icin numerik degerler belirlenen araliklara bolunur ve eslemesi geri rapor edilir.

import pandas as pd
import numpy as np

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

df = pd.DataFrame(data)

cats = pd.qcut(df['pop'], 3)
df['pop_cat'] = cats
df['pop_cat'] = "pop_" + df['pop_cat']


Bu cagridan sonra her satirin belli bir araliga eslendigini gorecegiz (cikti altta). Cagri qcuts'dan gelen objeye bakmak ilginc olabilir, icinde bir dizin (array) bir de "seviyeler (levels)" adinda 2. bir dizin var. Ana dizin icinde her nokta icin (veriyle ayni sirada) hangi araligin secildigi gosteriliyor. Siranin veri ile ayni olmasi sayesinde zaten pop_cat adli yeni bir kolona atama yaptigimiz anda Pandas o kolonu ana veriyle hemen esleyebiliyor. Bu iyi bir ozellik bu arada, elde mesela bir Numpy vektoru var ise, ve bu vektor DataFrame'imiz satir sayisi ile esdeger tane veri iceriyorsa, o zaman o vektoru alip cat diye aninda DataFrame'e yeni bir kolonmus gibi verebiliriz (ya da eskisinin uzerine yazabiliriz).

Levels ise o araliklari teker teker listeliyor, ustteki ornek icin 3 tane.

Biz ayrica ufak bir ek yaparak araligin hangi kolona ait oldugu gozuksun diye onu da veri icine ekledik. DataFrame icinde zaten belli oluyor ama duz CSV'ye versek ve mesela fpgrowth gibi bir algoritma onun uzerinde islese sonuclari rapor ederken aralik ve veri baglantisi daha rahat belli olur.


   pop   state  year             pop_cat
0  1.5    Ohio  2000    pop_[1.5, 1.933]
1  1.7    Ohio  2001    pop_[1.5, 1.933]
2  3.6    Ohio  2002    pop_(2.733, 3.6]
3  2.4  Nevada  2001  pop_(1.933, 2.733]
4  2.9  Nevada  2002    pop_(2.733, 3.6]


Istatistiksel bolgeler bu arada verinin frekansina hassastir (ki bu iyi), median ve mode arasindaki fark gibi, quartile islemi mode gibi hesaplanir, mode bilindigi gibi veri noktalarini siraya dizip ortadaki noktayi dondurur. Ortalama gibi toplama bolme yapmaz.

Thursday, July 4, 2013

Dekoratorler, Onbellek Kodlamasi, Fonksiyon Degistirmek

Bir fonksiyona ya da kod parcasina onbellek kullanimi eklemek kolaydir fakat bu tur ekler kodun okunabilirligini de azaltabilirler. Mesela kare almakla yukumlu bir fonksiyonumuz var diyelim (altta ise yaramaz bir degisken dummy ekledik, bazi puf noktalari gosterebilmek icin),

def kare(dummy, a):
    return a*a


Bu fonksiyonu

kare("filan", 3)

diye cagiriyoruz ve sonuc olarak 9 gelmesini bekliyoruz.

Onbellekleme icin, diyelim ki, eger a degeri onceden gorulmusse, kare islemi sonucunun tekrar hesaplanmasini istemiyoruz, onu onbellekten bulup hizli bir sekilde geri dondurmek tercihimiz (tabii carpim islemi de cok hizli isler, ama bu ornek icin yavas olabilecegini hayal edelim).

Bu kod uzerinde onbelleklemeyi eski usulle yapsaydik, kod suna benzerdi:

cache = {}
def kare(dummy, a):
    if not a in cache: cache[a] = a*a
    return cache[a]


Degisken cache bir Python dictionary'dir ve onbellegimiz onun uzerinde  duruyor. Goruldugu gibi kod biraz kalabaliklasti. Onbellek objesi alanen ortada, ayrica if gibi cok ciddi bir ibareyi koda sokusturmak zorunda kaldik :) Genellikle bu ifade onemli bir islem mantigi var ise kullanilir - en azindan kod okunabilirligi acisindan boyle olmasi daha iyidir.

Peki bu isi daha temiz bir sekilde yapamaz miydik?

Python dekorator fonksiyonlari iste tam burada ise yarar. Bir dekorator bir fonsiyonu "sarmalayabilir (wrap)", ve o fonksiyona giren cikan tum degerler uzerinde islem yapabilir, ve onlari istedigi gibi degistirebilir, bu sayede o fonksiyona "caktirmadan" ek ozellikler verebilir. Sozdizim acisindan da temiz dururlar, cunku dekorator fonksiyon uzerinde '@' ile tanimlanan bir seydir, baska bir eke ihtiyac yoktur. O zaman (once dekoratorun kendisi)

def cache(function):
  memo = {}
  def wrapper(*args):
    if args[1] in memo:
      print "cache hit"
      return memo[args[1]]
    else:
      print "cache miss"
      rv = function(*args)
      memo[args[1]] = rv
      return rv
  return wrapper


Ustteki kod ana kodunuzdan ayri bir yerde, bir dosyada durabilir mesela, sadece bir kere yazilir zaten, ve kullanilmasi gerektigi zaman su ibare yeterlidir,

@cache
def kare(dummy, a):
    return a*a


Goruldugu gibi gayet temiz. Onbellek kodu hic etrafta gozukmuyor, bu da kod bakimini daha rahatlastiran bir ozellik. Boylece kare fonksiyonunu normalde olmasi gerektigi gibi yaziyoruz, kod onbellek olsa da olmasa da ayni sekilde yaziliyor, sadece carpim icin gereken islem mantigini iceriyor.

Not: dummy degiskenini dekorator icinde istedigimiz herhangi fonksiyon argumani ile is yapabilecegimizi gostermek icin kullandik, args[1] ile sadece ikinci argumana baktik mesela.

Koda Ekler Enjekte Etmek

Diyelim ki mevcut bir kod parcasi var,


import random

class Foo:
    def f(self,x):
        x = random.random()
        return x

o = Foo()   


Biz bu kodun f() cagrisini "yakalayip" ona ek bir seyler yaptirtmak istiyoruz, ve mevcut koda hic dokunmadan bunu yapmak istiyoruz. Belki f() cagrisi bir baska yazilim paketi icinde, vs. Bu fonsiyonu dekore ederek bunu yapabiliriz, fakat mevcut fonksiyon koduna dokunmak istemedigimiz icin metot ustunde @birdekorator gibi bir kullanim yapamayiz. Bu durumda baska bir secenek sudur,

def decorated_f(fn):
    def new_f(*args, **kwargs):
        res = fn(*args, **kwargs)
        setattr(args[0], "was", res)
        return res
    return new_f

Foo.f = decorated_f(Foo.f)


Simdi

print "o", o.f(0)
print "was", o.was


Yeni ekleri de isletecek, bu ek Foo uzerinde yeni bir oge yaratti, ve bu ogeye o.was diye erisiyoruz.


Monday, July 1, 2013

Oracle, Pandas, HDF5

Daha onceki yazilarda SQL*Plus uzerinden CSV dosyalarina veri aktarimini gorduk. SQLplus'in bazi eksikleri var ne yazik ki, bu program metin bazli terminaller caginda ekranda birseyler gostermek icin yazilmis, hizli, verimli veri aktarimini pek beceremiyor. Disaridan parametre gecmek bile buyuk problem. Bunlarin yerine Oracle Python baglanti kutuphanesi kurulup direk Pandas icinden baglanti yapilabilir.

Diger yandan CSV ciktisi almak yerine daha az yer tutan HDF formati secilebilir.

Simdi bunlarin hepsini yanyana koyalim:

import pandas as pd
import pandas.io.sql as sql
import cx_Oracle as odb

conn = odb.connect("kullanici/sifre@DB")

query = """
select ...

from ...
"""

df = sql.read_frame(query, conn)               
df.to_hdf("dosya.h5" % i,"table")

Direk Python icinden baglandik, sorguyu islettik, Pandas DataFrame'i direk sorgu verisinden yarattik, ve ciktiyi HDF formatinda diske yazdik. Bu formati kullanmak icin PyTables adli bir paket lazim, kurmak icin

sudo apt-get install python-tables