Ana içeriğe geç

Giriş12 dk

NumPy: dizi düşüncesi

Önkoşul:Veri bilimi süreci: soru → veri → model → karar

Kanca

8 ürünün fiyatına %20 KDV eklemek istiyorsun. Python listesiyle bunu yapmanın tek yolu her fiyatı tek tek gezmek. Ama fiyatlar * 1.20 yazınca tüm liste bir anda güncellenseydi? NumPy dizileri tam olarak bunu yapıyor — ve bu, sadece bir kısayol değil, veriyle düşünme biçiminde bir değişim.

Sezgi

Python listesi, “kutucukların art arda dizilmesi” gibi düşünülür — her kutucuğa tek tek erişirsin, tek tek işlem yaparsın. NumPy dizisi ise tek bir bütün gibi düşünülür: ona uyguladığın işlem, otomatik olarak her elemana aynı anda uygulanır. Bu fark küçük görünüyor ama zihniyeti tersine çeviriyor: “her elemanı nasıl işlerim” yerine “tüm diziye ne yapmak istiyorum” diye düşünmeye başlıyorsun.

Bu düşünme biçimine “vektörleştirilmiş düşünce” denir — döngüyü yazmak yerine, işlemi doğrudan diziye tarif edersin. 4. derste bunun hız kazancını da göreceğiz.

Mekanizma

Aynı hesabı iki şekilde yapalım: liste + döngü, sonra NumPy dizisi.

Liste + döngü vs NumPy dizisi

# 8 ürünün fiyatına %20 KDV eklemek istiyoruz. Önce Python listesiyle (döngüyle),
# sonra NumPy diziyle (döngüsüz) yapalım — SONUÇ AYNI, ama düşünme biçimi farklı.
fiyatlar_liste = [120, 45, 300, 78, 15, 220, 60, 99]

# Liste yaklaşımı: "her elemanı TEK TEK gez"
kdvli_liste = []
for fiyat in fiyatlar_liste:
    kdvli_liste.append(fiyat * 1.20)
print("Liste + döngü:", kdvli_liste)

# Dizi yaklaşımı: "TÜM diziye birden işlem uygula"
fiyatlar = np.array(fiyatlar_liste)
kdvli_dizi = fiyatlar * 1.20
print("NumPy dizi:   ", kdvli_dizi)
Liste + döngü: [144.0, 54.0, 360.0, 93.6, 18.0, 264.0, 72.0, 118.8]
NumPy dizi:    [144.   54.  360.   93.6  18.  264.   72.  118.8]

Sonuç birebir aynı, ama NumPy versiyonunda hiç döngü yazmadık — fiyatlar * 1.20 tek başına yeterliydi. Şimdi bir NumPy dizisinin üç temel özelliğine bakalım:

shape, dtype, ndim

# Bir NumPy dizisinin üç temel özelliği: shape (boyut), dtype (veri tipi), ndim (kaç boyutlu).
print(f"\nshape: {fiyatlar.shape}   (8 elemanlı, tek boyutlu)")
print(f"dtype: {fiyatlar.dtype}   (tam sayı)")
print(f"ndim:  {fiyatlar.ndim}")

# Listeyle bunu yapmaya çalışırsan: fiyatlar_liste * 1.20 HATA VERİR (listeyi tekrarlar, çarpmaz!)
print(f"\nListe * 2 ne yapar: {fiyatlar_liste[:3] * 2}  (tekrar eder, matematik yapmaz!)")

shape: (8,)   (8 elemanlı, tek boyutlu)
dtype: int64   (tam sayı)
ndim:  1

Liste * 2 ne yapar: [120, 45, 300, 120, 45, 300]  (tekrar eder, matematik yapmaz!)

Dikkat: fiyatlar_liste[:3] * 2, Python listesinde matematik yapmaz — listeyi olduğu gibi tekrarlar: [120, 45, 300, 120, 45, 300]. Çoğu yeni başlayan “NumPy’a gerek yok, liste de matematik yapar” sanır. Değil, çünkü Python listeleri genel amaçlı kutular olarak tasarlanmıştır, sayısal işlem için değil — * operatörü listede “tekrarla” anlamına gelir, dizide ise “her elemanı çarp” anlamına gelir.

Matematik

Dizi özellikleri ve eleman bazlı işlem
(a1,a2,,an)×k=(a1k, a2k, , ank)(a_1, a_2, \ldots, a_n) \times k = (a_1 k,\ a_2 k,\ \ldots,\ a_n k)
ÖzellikAnlamı
shapeDizinin boyutları — (8,) sekiz elemanlı tek boyutlu, (3, 4) 3×4’lük iki boyutlu bir dizi olurdu
dtypeDizideki tüm elemanların ortak veri tipi (int64, float64 gibi) — bir dizide karışık tip olmaz
ndimBoyut sayısı — vektör 1, matris 2, daha yükseği “tensör”
Eleman bazlı işlemBir skaler veya aynı boyuttaki başka bir diziyle yapılan işlem, karşılıklı elemanlara uygulanır

dtype’ın “tüm elemanlar aynı tip olmalı” kısıtlaması, NumPy’ın hızının sırrıdır — Python listesi her eleman için ayrı bir nesne tutarken, NumPy dizisi bellekte bitişik, tek tip bir blok tutar. Bu, 4. derste göreceğimiz hız farkının temel nedenidir.

Matematik tazelemeVektör nedir, veri neden vektördür

Kod

Karşılaştırma operatörleri de eleman bazlı çalışır — bu, veri filtrelemenin temelidir:

Karşılaştırma ve filtreleme

# NumPy'da karşılaştırmalar bile eleman eleman çalışır — tek satırda "filtreleme mantığı" kurulur.
pahali_mi = fiyatlar > 100
print(f"\n100 TL üstü mü: {pahali_mi}")
print(f"100 TL üstü ürünler: {fiyatlar[pahali_mi]}")
print(f"Ortalama fiyat: {fiyatlar.mean():.2f} TL")
print(f"En pahalı - en ucuz: {fiyatlar.max() - fiyatlar.min()} TL")

100 TL üstü mü: [ True False  True False False  True False False]
100 TL üstü ürünler: [120 300 220]
Ortalama fiyat: 117.12 TL
En pahalı - en ucuz: 285 TL

fiyatlar > 100 tek başına True/False değerlerinden oluşan bir dizi üretiyor, fiyatlar[pahali_mi] ise bu diziyi bir “filtre” gibi kullanıp sadece True olan elemanları döndürüyor — döngü, if bloğu, hiçbiri gerekmedi.

8 ürünün KDV'siz ve KDV'li fiyatlarını yan yana gösteren çubuk grafik.
Tek satırlık dizi çarpımı (fiyatlar * 1.20), 8 ürünün fiyatını aynı anda güncelledi.

Nerede işe yarar

NumPy dizileri, veri bilimindeki hemen her aracın (pandas, scikit-learn, PyTorch) altında çalışan temel yapı taşıdır:

  • Toplu hesaplamalar. Bir ürün kataloğuna zam uygulamak, bir sensör verisini birim dönüştürmek, bir görüntünün her pikselini işlemek.
  • Filtreleme. “Şu eşiği geçen kayıtları bul” — boolean indeksleme, SQL’deki WHERE cümlesinin NumPy karşılığıdır.
  • İleri seviye kütüphanelerin temeli. pandas’ın Series’i, scikit-learn’ün beklediği veri formatı, PyTorch’un tensörleri — hepsinin kökeninde NumPy dizisi mantığı var.

Bu 3 hatayı yaparsın:

  1. NumPy dizisinde döngü yazmaya devam etmek — mümkün ama yavaş; “diziye ne yapmak istiyorum” diye düşünmeyi alışkanlık hâline getirmek gerekir.
  2. Farklı dtype’lardaki dizileri karıştırıp beklenmedik sonuçlar almak (örn. tam sayı dizisinde ondalık bölme kırpılması).
  3. Python listesi ile NumPy dizisini aynı şeymiş gibi kullanmaya çalışmak — + bir listede birleştirme, bir dizide toplama demektir.

Kendini test et

1. `fiyatlar_liste * 2` (Python listesi) ile `fiyatlar * 2` (NumPy dizisi) arasındaki fark nedir?
  1. İkisi de aynı sonucu verir
  2. Liste listeyi tekrarlar, dizi her elemanı 2 ile çarpar (doğru cevap)
  3. Liste hata verir, dizi çalışır
  4. Dizi listeyi tekrarlar, liste çarpar

Neden: Python'da listede `*` tekrar operatörüdür; NumPy dizisinde ise eleman bazlı çarpma operatörüdür — aynı sembol, farklı veri yapısında farklı anlam taşır.

2. Bir NumPy dizisinin `dtype`'ı neden tüm elemanlar için aynı olmak zorundadır?
  1. Rastgele bir tasarım tercihi
  2. Bellekte bitişik, tek tip bir blok tutulması hız kazandırır (doğru cevap)
  3. Python dilinin bir kısıtlaması olduğu için
  4. Aslında zorunlu değildir

Neden: Tek tip veri, NumPy'ın veriyi bellekte bitişik ve öngörülebilir şekilde tutmasını sağlar — bu da hızlı, vektörleştirilmiş işlemlerin temelidir.

3. `fiyatlar[fiyatlar > 100]` ifadesi ne yapar?
  1. 100'den büyük indeksleri döndürür
  2. 100'den büyük DEĞERLERE sahip elemanları döndürür (doğru cevap)
  3. Hata verir
  4. Diziyi 100 ile çarpar

Neden: fiyatlar > 100 önce bir boolean dizi üretir (True/False), bu dizi köşeli parantez içinde kullanıldığında sadece True olan konumlardaki değerleri filtreler.

Özet

Özet

  • NumPy dizisi, bir işlemi tüm elemanlara aynı anda uygulayan (vektörleştirilmiş) bir veri yapısıdır.
  • shape, dtype ve ndim, bir dizinin boyutunu, tipini ve kaç boyutlu olduğunu anlatır.
  • Python listesi ile NumPy dizisi aynı operatörlere (+, *) farklı anlamlar yükler.
  • Karşılaştırma operatörleri de eleman bazlı çalışır, bu da boolean indekslemeyle filtrelemeyi mümkün kılar.
  • Bu "dizi düşüncesi", pandas ve scikit-learn dahil neredeyse tüm veri bilimi araçlarının temelidir.
Sonraki adım: Broadcasting →