Python listesi ile NumPy dizisi dışarıdan bakıldığında birbirine benzer: ikisi de sıralı biçimde değer tutar, ikisinden de köşeli parantezle eleman okursunuz. Ama veri analizinde bu ikisi farklı iki araçtır. Fark, verini…
Python listesi ile NumPy dizisi dışarıdan bakıldığında birbirine benzer: ikisi de sıralı biçimde değer tutar, ikisinden de köşeli parantezle eleman okursunuz. Ama veri analizinde bu ikisi farklı iki araçtır. Fark, verinin bellekte nasıl durduğundan ve üzerinde işlem yaparken ne olduğundan gelir.
Bellekte nasıl duruyorlar?
Python listesi genel amaçlı bir kaptır. İçinde sayı, metin, başka bir liste, hepsi bir arada bulunabilir. Bunu başarmak için liste, değerlerin kendisini değil onlara giden işaretçileri tutar. NumPy dizisi ise tek tipli ve bitişik bir bloktur: bütün elemanlar aynı veri tipindedir ve bellekte yan yana durur. Bu kısıt, NumPy'ın işlemleri C düzeyinde toplu hâlde yapabilmesini sağlar.
- Liste: esnek, karışık tip alır, eleman ekleme çıkarma ucuzdur.
- NumPy dizisi: tek tipli, sabit boyutlu, sayısal hesapta çok daha hızlıdır.
- Dizinin ek bilgisi:
shape (boyut) ve dtype (veri tipi) alanları vardır.
Aynı işlem, farklı sonuç
En görünür fark aritmetikte ortaya çıkar. Listede * operatörü elemanları çoğaltır; dizide her elemanı tek tek çarpar. Buna vektörel işlem denir:
import numpy as np
adet_liste = [12, 8, 15, 3]
adet_dizi = np.array([12, 8, 15, 3])
print(adet_liste * 2) # [12, 8, 15, 3, 12, 8, 15, 3] -> liste uzadı
print(adet_dizi * 2) # [24 16 30 6] -> her eleman çarpıldı
birim_fiyat = np.array([45.0, 120.0, 30.0, 250.0])
tutar = adet_dizi * birim_fiyat # eleman eleman çarpım
print(tutar) # [ 540. 960. 450. 750.]
print(tutar.sum()) # 2700.0
print(tutar.mean()) # 675.0
Aynı hesabı listeyle yapmak için döngü ya da liste üreteci yazmanız gerekir: [a * f for a, f in zip(adet_liste, [45.0, 120.0, 30.0, 250.0])]. Dört elemanda fark hissedilmez, birkaç yüz bin satırlık bir satış tablosunda ise fark katlarla ölçülür.
Tip birliği bir kural, öneri değil
Bir diziye farklı tipte değerler verirseniz NumPy sessizce hepsini ortak bir tipe yükseltir. Bu, fark edilmeden bozulan hesapların klasik kaynağıdır:
print(adet_dizi.dtype) # int64
karisik = np.array([12, 8, "15"])
print(karisik.dtype) # <U21 -> hepsi metne dönüştü
print(karisik.sum()) # TypeError
sayisal = karisik.astype(float) # bilinçli dönüşüm
print(sayisal.sum()) # 35.0
Sık yapılan hata: döngü içinde np.append ile diziyi büyütmek. Dizi sabit boyutlu olduğu için her çağrıda bellekte yeni bir kopya oluşur ve kod, listeyle yazılmış hâlinden bile yavaşlar. Doğru yaklaşım, önce sonuçları normal bir listede toplayıp döngü bittiğinde tek seferde np.array(sonuclar) demek ya da boyutu biliniyorsa diziyi np.zeros(n) ile baştan ayırmaktır. Veriyi topla-biriktir aşamasında liste, hesap aşamasında dizi kullanmak pratikte en sağlıklı iş bölümüdür.