Tablo veri dosyalarını işlemek

Önceki bir yazıda Python’la dosya okuma ve yazma yöntemlerini görmüştük. Dosyalarda düz metin içeren bir tablo halinde (satırlar ve sütünlar ile) düzenlenmiş verilere sık sık rastlanır. Bu yazıda, bu tür veri dosyalarını nasıl işleyebileceğimizi göreceğiz.

Basit tablo

Veri dosyamızın düz metin halinde aşağıdaki satırları barındırdığını varsayalım. Dosyanın adı veri.dat olsun. Her satırdaki sayılar Tab karakteriyle ayrıldı.

1.2    2.4    2.9    4.2
1.5    2.3    3.1    4.3
1.4    2.1    3.0    3.9

Amacımız bu dosyadaki verileri matematiksel işlem yapılacak şekle dönüştürmek. Tabloyu, sayısal değer barındıran bir listeler listesi haline dönüştürelim.

Önce, liste kurma yöntemi ile, her elemanı dosyanın bir satırı olan bir liste yaratalım.

>>> [s for s in open("veri.dat","r")]
['1.2\t2.4\t2.9\t4.2\n', '1.5\t2.3\t3.1\t4.3\n', '1.4\t2.1\t3.0\t3.9']

Her satırdaki sayılar ‘\t’ karakteriyle ayrıldığından, dizelere ait split() metodunu kullanarak, her satırı bir liste haline getirebiliriz.

>>> [s.split() for s in open("veri.dat","r")]
[['1.2', '2.4', '2.9', '4.2'], ['1.5', '2.3', '3.1', '4.3'], ['1.4', '2.1', '3.0', '3.9']]

Ama bu veriler matematiksel işleme uygun değil. Her elemanı, float() fonksiyonunu kullanarak sayı haline getirmeliyiz. Bu da ayrıca bir liste kurma işlemi gerektiriyor.

>>> data = [ [float(x) for x in s.split()] for s in open("veri.dat","r") ]
>>> data
[[1.2, 2.4, 2.9, 4.2], [1.5, 2.3, 3.1, 4.3], [1.4, 2.1, 3.0, 3.9]]

Bu son komut dosyadaki veriyi sayısal olarak bir tablo haline dönüştürmemizi sağladı. Artık bununla başka işlemler yapabiliriz.

Eğer bir satırda sütünlar (“alanlar”) Tab yerine boşluk (veya birkaç boşluk) ile ayrılmış olsaydı da split() metodu aynı şekilde çalışırdı. Ama alanlar başka bir karakterle, meselâ virgülle ayrılmışsa, split(',') kullanmak gerekir.

veri.dat dosyası bu sefer şöyle olsun:

1.2,2.4,2.9,4.2
1.5,2.3,3.1,4.3
1.4,2.1,3.0,3.9

O zaman kullanacağımız komut:

>>> [[float(x) for x in s.split(',')] for s in open("veri.dat","r")]
[[1.2, 2.4, 2.9, 4.2], [1.5, 2.3, 3.1, 4.3], [1.4, 2.1, 3.0, 3.9]]

Tabloya yeni sütun eklemek

Dört sütunlu olan orijinal veri dosyasına, ilk  dört sütunun toplamını barındıran beşinci bir sütun eklemek istiyoruz diyelim. Dosyayı bir tabloya dönüştürdükten sonraki hamlemiz şu olabilir:

>>> data = [[float(x) for x in s.split(',')] for s in open("veri.dat","r")]
>>> for d in data:
    d.append(sum(d))

>>> data
[[1.2, 2.4, 2.9, 4.2, 10.7], [1.5, 2.3, 3.1, 4.3, 11.2], [1.4, 2.1, 3.0, 3.9, 10.4]]

Eğer orijinal liste olan data‘yı bozmak istemiyorsak şöyle de yapabiliriz:

>>> data = [[float(x) for x in s.split(',')] for s in open("veri.dat","r")]
>>> [ d+[sum(d)] for d in data]
[[1.2, 2.4, 2.9, 4.2, 10.7], [1.5, 2.3, 3.1, 4.3, 11.2], [1.4, 2.1, 3.0, 3.9, 10.4]]

Burada d + [sum(d)] ifadesinde, d‘ye tek elemanlı bir liste olan [sum(d)]‘yi eklediğimize dikkat edin. Bunun yerine d + sum(d) yazsaydık yorumlayıcı, liste ile sayının toplanması işlemi tanımlı olmadığı için hata mesajı verirdi. Toplama işlemi yerine d.extend( [sum(d)] ) ifadesi de aynı işi yapardı.

Şimdi bu yeni tabloyu dosyaya yazalım. Yukarıdaki işlemin tersini yapacak, sayısal tabloyu bir dizeye çevireceğiz. Adım adım gidelim, önce birinci satırı alalım:

>>> [str(x) for x in data[0]]
['1.2', '2.4', '2.9', '4.2', '10.7']

Bu dizeler listesini birleştirmek için join() metodunu kullanırız.

>>> ",".join([str(x) for x in data[0]])
'1.2,2.4,2.9,4.2,10.7'

Böylece birinci satırı elde ettik. Bunu bütün satırlar için tekrarlayalım ve satırlar listesini elde edelim.

>>> [ ",".join([str(x) for x in d]) for d in data ]
['1.2,2.4,2.9,4.2,10.7', '1.5,2.3,3.1,4.3,11.2', '1.4,2.1,3.0,3.9,10.4']

Bu listenin elemanlarını da aralarında ‘\n’ (satırbaşı) karakteriyle birleştiririz.

>>> yeni = "\n".join( [ ",".join([str(x) for x in d]) for d in data] )
>>> yeni
'1.2,2.4,2.9,4.2,10.7\n1.5,2.3,3.1,4.3,11.2\n1.4,2.1,3.0,3.9,10.4'

Dosyaya yazılacak dizeyi hazırlamış olduk. (Önceki adımları sadece aşama aşama gitmek için gösterdim; işi yapmak için sadece bu son komut yeterli.) Şimdi bunu dosyaya yazalım.

>>> open("veri.dat", "w").write(yeni)

Böylece veri.dat dosyasına istediğimiz şekilde yeni bir sütun eklemiş olduk.  Eğer orijinal veri dosyasını bozmak istemiyorsak başka bir dosya ismi kullanabiliriz.

Sütunlar üzerinde işlem yapmak

Yine veri.dat dosyasını kullanalım ve sütunların toplamını bulalım.

>>> data = [[float(x) for x in s.split(',')] for s in open("veri.dat","r")]
>>> data
[[1.2, 2.4, 2.9, 4.2], [1.5, 2.3, 3.1, 4.3], [1.4, 2.1, 3.0, 3.9]]

Birinci sütunu (data listesinin her elemanının 0 indeksli elemanları) bir liste olarak ayrıştıralım

>>> [d[0] for d in data]
[1.2, 1.5, 1.4]

Bunların toplamı:

>>> sum([d[0] for d in data])
4.1

Şimdi her sütundaki sayıların toplamını veren bir liste oluşturalım

>>> [ sum([d[i] for d in data]) for i in range(len(data[0])) ]
[4.1, 6.799999999999999, 9.0, 12.4]

İkinci eleman 6.8 olacakken, bilgisayarın içindeki kayan nokta temsilindeki yuvarlama hatası sebebiyle böyle garip bir çıktı alıyoruz.

Satırları belli şartlarla okumak

Dosyayı okurken belli satırları filtreleyebiliriz. Sözgelişi, veri.dat dosyasında birinci sütundaki değeri 1.3’den büyük olan satırları alalım.

Burada liste kurma notasyonunda if kullanmak elverişsiz, çünkü filtrelemeyi sayısal değer üzerinden yapacağız. Bir döngü oluşturalım.

>>> data = []
>>> for s in open("veri.dat"):
    d = [float(x) for x in s.split(',')]
    if d[0] > 1.3:
        data.append(d)

>>> data
[[1.5, 2.3, 3.1, 4.3], [1.4, 2.1, 3.0, 3.9]]

Veya, alanlarının toplamı 10.5’i geçen satırları alalım.

>>> data=[]
>>> for s in open("veri.dat"):
    d = [float(x) for x in s.split(',')]
    if sum(d) > 10.5:
        data.append(d)

>>> data
[[1.2, 2.4, 2.9, 4.2], [1.5, 2.3, 3.1, 4.3]]

Başlık ve yorum satırlarını kaldırmak

Birçok veri dosyasında ilk birkaç satırda dosyanın içeriği hakkında birşeyler yazılmış, sütun başlıkları verilmiş olabilir. Sözgelişi veri.dat dosyasının içeriği şöyle olsun:

Boyut ölçümleri
küp1   küp2   küp3   küp4
1.2    2.4    2.9    4.2
1.5    2.3    3.1    4.3
1.4    2.1    3.0    3.9

Alanlar birbirinden Tab karakteriyle ayrılmış. Dosyada ilk iki satırın veri olmadığını görüyoruz. Önce onları atlar ve geriye kalan tabloyu yukarıdaki gibi okuruz.

>>> f = open("veri.dat","r")
>>> f.readline()
'Boyut \xc3\xb6l\xc3\xa7\xc3\xbcmleri\n'
>>> f.readline()
'k\xc3\xbcp1\tk\xc3\xbcp2\tk\xc3\xbcp3\tk\xc3\xbcp4\n'
>>> data = [[float(x) for x in s.split()] for s in f]
>>> data
[[1.2, 2.4, 2.9, 4.2], [1.5, 2.3, 3.1, 4.3], [1.4, 2.1, 3.0, 3.9]]
>>> f.close()

İki satırı okumak için iki kez readline() metodunu çağırırız. Yorumlayıcıda görülen garip şifre, yazdığımız Türkçe harflerin Unicode temsilidir. Bu komutlar bir program içinde verildiğinde bu çıktıyı görmeyeceğiz. Bu komutların ardından dosya işaretleyicisi tablonun başına geliyor ve daha önce kullandığımız yöntemle veriyi bir listeye aktarabiliyoruz.

Bazı dosyalarda ise, verilerin arasında yorum satırları bulunabilir. Bunlar genellikle satırın başında özel bir karakterle (çoğunlukla “#” ile) belirtilirler. Verileri işlemeden önce bu yorumları ayıklamak, veya işleme sırasında filtrelemek gerekir.

Veri dosyamız veri.dat bu sefer şu satırları barındırsın.

# Birinci ölçüm
1.2    2.4    2.9    4.2
# İkinci ölçüm
1.5    2.3    3.1    4.3
# Üçüncü ölçüm
1.4    2.1    3.0    3.9

Yukarıda “satırları belli şartla okumak” kısmında bahsettiğimiz gibi, burada “#” ile başlamayan satırları hesaba katacağız sadece. Bir yolu şöyle olabilir:

>>> data = []
>>> for s in open("veri.dat"):
    if s[0] != "#":
        d = [float(x) for x in s.split()]
        data.append(d)
>>> data
[[1.2, 2.4, 2.9, 4.2], [1.5, 2.3, 3.1, 4.3], [1.4, 2.1, 3.0, 3.9]]

Ancak bu durum liste kurma işlemine elverişli. Aynısını daha kısa olarak şöyle de yapabiliriz.

>>> data = [ [float(x) for x in s.split()] for s in open("veri.dat") if s[0] != "#" ]
>>> data
[[1.2, 2.4, 2.9, 4.2], [1.5, 2.3, 3.1, 4.3], [1.4, 2.1, 3.0, 3.9]]

Büyük dosyalar

Eğer çok büyük dosyalardan veri okuyorsanız, bütün dosyayı bir kerede bir listede depolamak bellek sıkıntısı yaratabilir. Bu durumda liste kurma işlemi yerine, dosya üzerinden iterasyon yapan bir döngü kullanmak daha doğru olur.

Sözgelişi, yeni bir dosya oluşturarak ilk dört sütunu kopyalayan ve beşinci sütuna ilk dördünün toplamını yazan programımız şöyle olabilir:

>>> f = open("veri2.dat", "w")
>>> for s in open("veri.dat","r"):
    d = [float(x) for x in s.split(',')]
    f.write( s.rstrip() + "," + str( sum(d) ) + '\n' )

>>> outfile.close()

Döngü içindeki son satır birçok işlem içeriyor.

f.write() metodu, parametre olarak verilen dizeyi veri2.dat dosyasına yazıyor.

s.rstrip() metodu, okunan satırın sonundaki ‘\n’ karakterini kaldırıyor. Bunu yapmazsak satırbaşı yapar ve sonradan gelen virgül ve toplam alt satırda olur.

str( sum(d) ) fonksiyonu, d listesinin toplamı olan sayıyı bir dizeye çeviriyor. Ardından bütün dizeler ‘+’ işlemiyle birleştiriliyor.

Bu kadar ayrıntıyı nasıl aklınızda tutacağınızı düşünerek üzülmeyin. Etkileşimli yorumlayıcı bu işe yarıyor: Doğru sonucu bulana kadar deneme imkânınız var. Elbette çok büyük dosyalarla çalıştığınızda önce 5-10 satırlık küçük bir parçası üzerinde deneme yapın, istediğiniz gibi çalıştığında büyük probleme uygulayın.

Yorum bırakın