dersblog

Python Liste Kavraması (List Comprehension)

Python'un en geç anlaşılan, öğrenilen özelliklerinden biri belki de budur. Kitap yazarlarının bile hala eski usül liste oluşturmayı kullanması bunun işareti. Eski usül nasıldır? Mesela 1 ila 10 arasındaki sayıların karesini alacağım, ve bununla yeni bir liste oluşturacağım. Ana liste,

data = list(range(1,11))
print (data)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
kareler = []
for x in range(1,11):
   kareler.append(x**2)
print (kareler)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Bir boş liste tanımlandı, ona ekler yapıldı, bir for döngüsü var, vs. Fakat liste oluşturması liste kavrama ile tek bir satırda yapılabilirdi,

kareler = [x**2 for x in range(1,11)]
print (kareler)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Yani liste içeriğinin nasıl oluşturulacağını bir nevi liste tanımının parçası haline getirmiş oluyoruz.

Kavrama operasyonları çok daha kapsamlı olabiliyor, if, else, kullanmak mümkün mesela, hatta içiçe (nested) döngüler bile kullanılabiliyor.

Sadece çift sayıların karesini alalım, diğerleri olduğu gibi kalsın,

kareler2 = [x**2 if x%2==0 else x for x in range(1,11)]
print (kareler2)
[1, 4, 3, 16, 5, 36, 7, 64, 9, 100]

Liste kavrama ifadeleri biraz dil sözdizimini tersine çeviriyor sanki, mesela for döngü içine gidecek olan if ifadesi şimdi en başta.

İçiçe döngü örneği; 100 ila 105 arasındaki sayılar dış döngü, 1 ile 3 arasındakiler iç döngü olsun, ve içteki listeyi gezerken dıştakine ekleyelim. Eski usulle bunu

toplamlar = []
for x in range(100,106):
   for y in range(1,4):
      toplamlar.append(x+y)
print (toplamlar)      
[101, 102, 103, 102, 103, 104, 103, 104, 105, 104, 105, 106, 105, 106, 107, 106, 107, 108]

diye yapardık. Uzun iş.. Her şeyi tek satırda yapabilirdik,

toplamlar = [x+y for x in range(100,106) for y in range(1,4)]
print (toplamlar)
[101, 102, 103, 102, 103, 104, 103, 104, 105, 104, 105, 106, 105, 106, 107, 106, 107, 108]

Bu temel ile pek çok yöne gidilebilir. Unutmayalım, enumerate ile herhangi bir liste gezimi sırasında üzerinde olunan indis üretilebilir,

for i,x in enumerate(['ali','veli','ahmet']):
   print (i,x)
0 ali
1 veli
2 ahmet

Ve bu tür bir kullanım liste kavraması için de aynen geçerlidir,

isimler_indisler = [(i,x) for i,x in enumerate(['ali','veli','ahmet'])]
print (isimler_indisler)
[(0, 'ali'), (1, 'veli'), (2, 'ahmet')]

Böylece bir değişken grubu (tuple) listesi oluşturduk, ve tüm bunları tek bir satırda yaptık.

Şimdi sıkı durun, benzer bir sözdizim ile Python sözlüğü de (dictionary) yaratmak mümkün,

isimler_indis_dict = { i:x for i,x in enumerate(['ali','veli','ahmet']) }
print (isimler_indis_dict)
{0: 'ali', 1: 'veli', 2: 'ahmet'}

Aslında bu kullanıma sözlük kavraması (dictionary comprehension) deniyor, her neyse, benzer kullanım alanı.

Uygulama

Bu konuya nereden girdik? Veri bilimiyle alakalı bir kitap okuyordum, Introduction to Time Series Modeling with Python adında, yazar regresyon yapıyor, veriye eğri uyduruyor, ve katsayılar elde ediyor. Temel x kordinat değerleri X içinde, katsayılar coef içinde, sonra modeli tekrar oluşturmak için

X = [1,2,3,4]
coef = [10,5,-3,6]
degree = 3
curve = list()
for i in range(len(X)):
   value = coef[-1]
   for d in range(degree):
      value += X[i]**(degree-d) * coef[d]
   curve.append(value)
print (curve)   
[18, 100, 312, 714]

kullanıyor. Fakat bu kod çok karışık... Yapılmaya uğraşılan basit bir şey aslında, X içindeki her değer için coef içindeki katsayılar alınıp ona tekabül eden polinom dereceleri x ile çapılacak ve sonuçlar toplanacak, yani her x için 10 + 5x -3x^2 + 6*x^3 hesabı yapılacak. Bir dış döngü var, X için, bir de iç döngü var coef için.

Arkadaş coef gezmek için range ile indis yaratıyor, 1'inci ofsayt (çünkü o indise degree-d yapmak için ihtiyacı var). Diğer yandan range(len(X)) ile bir diğer indis yaratıyor, ona hiç gerek yok, ikinci ofsayt. İndis gerektiginde enumerate ile erişilebilirdi, geri kalan durumlarda for ile öğeleri gezmek yeterlidir. Tabii gereksiz boş liste yaratmayı bir tarafa bırakalım, o herşeyi daha da uzatmış.

Tüm bunlar çok kısa bir kodla yapılabilirdi,

curve = [np.sum([coef[-1]] + [x**(degree-d)*c for d,c \
        in enumerate(coef[:-1])]) for x in X]

print (curve)
[18, 100, 312, 714]

Toplamı nasıl yaptığımıza dikkat, liste içinde liste var, ama içeride listeyi dışarı vermeden önce numpy.sum(..) ile topluyoruz.


Yukarı