Neden numpy?
Anadolu Market'in 3 şubesi 7 gün boyunca saatlik satış kaydı tutuyor. Her şube için günlük ortalama, her gün için şube toplamı, 1000 TL üstü saatlerin sayısı... Bunları saf Python listeleriyle de yazabilirsin ama iç içe döngüler hem yavaş hem de okunması zor olur. numpy, bu tür sayısal işleri tek satırda ve C hızında yapan kütüphanedir; pandas da altında numpy dizileri kullanır. Önce numpy'ı anlayan, pandas'ı çok daha rahat öğrenir.
ndarray: tek tipli, sabit şekilli dizi
Python listesi her türden nesne tutabilir; numpy dizisi (ndarray) ise tek bir veri türü (dtype) tutar ve belleğe yan yana yerleşir. Bu yüzden hızlıdır. Dizinin boyutlarını shape verir.
import numpy as np
fiyatlar = np.array([129.9, 45.5, 899.0, 12.75])
print(fiyatlar.dtype, fiyatlar.shape, fiyatlar.ndim)
adetler = np.array([3, 10, 1, 24]) # tam sayı dizisi
print(adetler.dtype)
# 3 şube x 7 gün (satır = şube, sütun = gün)
satis = np.array([
[120, 135, 150, 110, 180, 240, 260],
[ 80, 95, 70, 90, 130, 200, 210],
[200, 210, 190, 220, 260, 320, 350],
])
print(satis.shape) # (3, 7)
print(np.zeros(3), np.arange(0, 10, 2), np.linspace(0, 1, 5))
print(satis.reshape(7, 3).shape) # aynı veri, farklı şekil
dtype=np.float32 verebilirsin ama hassasiyet düşer. Bir diziye farklı türde değer karıştırırsan numpy hepsini "en geniş" türe yükseltir: tam sayı + ondalık → float.Vektörleştirme: döngüyü numpy'a devret
Liste ile [f * a for f, a in zip(fiyatlar, adetler)] yazarsın; numpy ile doğrudan fiyatlar * adetler. Aritmetik, karşılaştırma ve matematik fonksiyonları (np.sqrt, np.log, np.round) eleman eleman çalışır. Buna vektörleştirme (vectorization) denir.
import numpy as np fiyatlar = np.array([129.9, 45.5, 899.0, 12.75]) adetler = np.array([3, 10, 1, 24]) tutar = fiyatlar * adetler print(tutar) print(np.round(tutar * 1.20, 2)) # KDV dahil, 2 basamak print(tutar.sum(), tutar.mean().round(2), tutar.max()) print(tutar.argmax()) # en büyük tutarın indeksi
Broadcasting: şekiller uyuşmasa da hesap
Skaler ile diziyi çarpmak broadcasting'in en basit hali: 1.20 sayısı dizinin her elemanına "yayılır". Kural şudur: iki dizinin şekilleri sağdan sola karşılaştırılır; her boyutta ya eşit olmalı ya da biri 1 olmalı. (3, 7) ile (7,) uyar (her satıra aynı 7'li vektör), (3, 7) ile (3, 1) uyar (her satıra kendi katsayısı).
import numpy as np
satis = np.array([
[120, 135, 150, 110, 180, 240, 260],
[ 80, 95, 70, 90, 130, 200, 210],
[200, 210, 190, 220, 260, 320, 350],
])
hedef = np.array([150, 150, 150, 150, 200, 250, 250]) # (7,) gün hedefi
print((satis >= hedef).astype(int)) # her satıra yayıldı
katsayi = np.array([[1.0], [1.5], [0.8]]) # (3, 1) şube katsayısı
print(np.round(satis * katsayi)[0, :3]) # her satır kendi katsayısıyla
try:
satis + np.array([1, 2, 3]) # (3,) ile (3, 7) uymaz
except ValueError as e:
print("Hata:", e)
İndeksleme ve maske
Listedeki gibi d[0], d[1:3] çalışır; iki boyutta satis[1, 2] (2. şube, 3. gün) ve satis[:, 0] (tüm şubelerin ilk günü). En güçlü araç boolean maske: bir karşılaştırma True/False dizisi üretir, bunu köşeli paranteze verince yalnızca True olanlar seçilir.
import numpy as np tutar = np.array([389.7, 455.0, 899.0, 306.0, 1250.0, 75.5]) maske = tutar > 400 print(maske) print(tutar[maske]) # 400 üstü tutarlar print(maske.sum()) # kaç tane? (True = 1) print(np.where(tutar > 400, "büyük", "küçük")) print(tutar[[0, 2, 4]]) # fancy indeksleme: indeks listesi print(tutar[(tutar > 300) & (tutar < 1000)]) # ve: &, veya: |, değil: ~
and/or'u çalışmaz ("truth value of an array is ambiguous"). &, |, ~ kullan ve her karşılaştırmayı paranteze al; çünkü & işleminin önceliği >'den yüksektir.Eksen (axis) ile özet
İki boyutlu dizide sum() her şeyi toplar. axis=0 satırlar boyunca yürür, yani her sütun için bir sonuç verir; axis=1 sütunlar boyunca yürür, her satır için sonuç verir. Ezber yolu: axis, "yok olacak" boyuttur.
| İfade | Anlamı (satır = şube, sütun = gün) | Sonuç şekli |
|---|---|---|
satis.sum() | Tüm satış | skaler |
satis.sum(axis=0) | Her günün şube toplamı | (7,) |
satis.sum(axis=1) | Her şubenin haftalık toplamı | (3,) |
satis.mean(axis=1) | Her şubenin günlük ortalaması | (3,) |
satis.argmax(axis=1) | Her şubenin en iyi günü (indeks) | (3,) |
Rastgele veri: default_rng
Test verisi, simülasyon ve örnekleme için numpy'ın modern üreteci (generator) np.random.default_rng(seed). Eski np.random.seed yerine bunu kullan; aynı seed aynı diziyi verir, bu da testleri tekrarlanabilir yapar.
import numpy as np rng = np.random.default_rng(42) gunluk = rng.integers(100, 500, size=7) # 100-499 arası tam sayı print(gunluk) sure = rng.normal(loc=3.2, scale=0.5, size=5) # ortalama 3.2, sapma 0.5 print(np.round(sure, 2)) print(rng.choice(["Ankara", "İzmir", "Bursa"], size=4)) print(rng.random(3).round(3)) # 0-1 arası
Hız farkı nereden geliyor?
Python listesinde her sayı ayrı bir nesnedir; toplarken yorumlayıcı her adımda tür kontrolü yapar. numpy dizisi tek bloktur ve döngü C'de döner. Fark küçük veride göze batmaz, bir milyon elemanda onlarca kata çıkar.
import numpy as np, timeit
n = 200_000
liste = list(range(n))
dizi = np.arange(n)
t_liste = timeit.timeit(lambda: sum(x * 2 for x in liste), number=5)
t_numpy = timeit.timeit(lambda: (dizi * 2).sum(), number=5)
print(f"liste: {t_liste:.4f} s numpy: {t_numpy:.4f} s oran: {t_liste / t_numpy:.0f}x")
for döngüsü görürsen dur ve sor: bu işlem bir dizi ifadesiyle yazılabilir mi? Neredeyse her zaman evet.Derinleş: kurulum, numpy 2 ve eksik kalan araçlar
Temel kavramlar (ndarray, vektörleştirme, broadcasting, maske, axis) hazır. Bu bölümde iş hayatında her gün karşına çıkan ama giriş derslerinde atlanan parçaları tamamlıyoruz: kurulum ve sürüm sorunları, dizi kurma ve birleştirme araçları, sıralama ve tekilleştirme, çok koşullu dönüşüm, birikimli hesaplar ve eksik değerli istatistik.
Kurulum ve sürüm kontrolü
Tarayıcıdaki motorda numpy hazırdır. Kendi bilgisayarında sanal ortamı (virtual environment) etkinleştirip pip ile kurarsın. pip yerine python -m pip yazmak, paketin doğru Python yorumlayıcısına kurulmasını garanti eder.
# Windows (PowerShell) py -m venv .venv .venv\Scripts\Activate.ps1 python -m pip install --upgrade pip python -m pip install numpy # macOS / Linux python3 -m venv .venv source .venv/bin/activate python -m pip install numpy # Sürümü sabitlemek (requirements.txt) numpy>=2.0,<3
Kurulumdan sonra ilk iş sürümü ve hangi yorumlayıcının kullanıldığını kontrol etmektir:
import sys
import numpy as np
print("Python:", sys.version_info[:2])
print("numpy :", np.__version__)
ana = int(np.__version__.split(".")[0])
print("numpy 2 serisi mi?", ana >= 2)
| Belirti | Neden | Çözüm |
|---|---|---|
ModuleNotFoundError: No module named 'numpy' | Paket başka bir Python'a kurulmuş ya da sanal ortam etkin değil | Ortamı etkinleştir, python -m pip install numpy |
A module that was compiled using NumPy 1.x cannot be run in NumPy 2.x | Eski derlenmiş bir paket (ör. eski pandas, scipy) numpy 2 ile uyumsuz | O paketi güncelle (pip install -U pandas) ya da geçici olarak numpy<2 sabitle |
AttributeError: module 'numpy' has no attribute 'float' | np.float, np.int, np.object takma adları kaldırıldı | Python'un float/int'ini ya da np.float64/np.int64 kullan |
UFuncTypeError: Cannot cast ufunc 'multiply' output | Tam sayı diziye yerinde (*=) ondalık sonuç yazılıyor | Diziyi baştan dtype=float kur ya da d = d * 1.2 yaz |
Dizi kurma ve şekil değiştirme
np.zeros, np.ones, np.full sabit dizi; np.eye birim matris kurar. arange(baş, bit, adım) adımı, linspace(baş, bit, adet) eleman sayısını sabitler; ondalık adımda arange yuvarlama yüzünden bir eleman fazla ya da eksik üretebilir, bu yüzden ondalık aralıklarda linspace güvenlidir. reshape(-1, 7) içindeki -1 "bu boyutu sen hesapla" demektir; ravel() diziyi tek boyuta indirir, .T devrik (transpose) verir.
import numpy as np saatlik = np.arange(1, 15) # 14 günlük satış (örnek) hafta = saatlik.reshape(-1, 7) # 2 hafta x 7 gün print(hafta.shape) print(hafta.T.shape) # 7 x 2 print(np.full(3, 99.9), np.eye(2).tolist()) # Birleştirme h1 = np.array([10, 12, 9]) h2 = np.array([11, 14, 8]) print(np.concatenate([h1, h2])) # uç uca: (6,) print(np.vstack([h1, h2]).shape) # alt alta: (2, 3) print(np.column_stack([h1, h2]).shape) # yan yana: (3, 2)
Sıralama, sıra indeksi ve tekilleştirme
np.sort sıralı kopya verir; argsort ise "sıralamak için hangi indeksleri hangi sırayla almalıyım?" sorusunu yanıtlar. Bu sayede bir diziye göre başka bir diziyi (ürün adlarını adetlere göre) sıralayabilirsin. np.unique(..., return_counts=True) tekil değerleri ve kaç kez geçtiklerini birlikte döndürür; küçük bir value_counts gibidir.
import numpy as np urunler = np.array(["kahve", "çay", "şeker", "un"]) adet = np.array([120, 340, 15, 80]) sira = np.argsort(adet)[::-1] # büyükten küçüğe indeksler print(sira, urunler[sira][:2]) # en çok satan iki ürün sehirler = np.array(["Ankara", "İzmir", "Ankara", "Bursa", "Ankara"]) deger, sayi = np.unique(sehirler, return_counts=True) print(dict(zip(deger.tolist(), sayi.tolist()))) print(np.isin(sehirler, ["İzmir", "Bursa"]))
Çok koşullu dönüşüm: np.select ve np.clip
np.where iki dallı bir if gibidir. Üç ve daha fazla dal için np.select(kosullar, secenekler, default=...) kullanılır; koşullar sırayla denenir ve ilk doğru olan kazanır, tıpkı if/elif zinciri gibi. np.clip(d, alt, üst) değerleri bir aralığa kırpar (ör. indirim oranı %0 ile %50 arasında kalsın).
import numpy as np tutar = np.array([250.0, 640.0, 1200.0, 90.0]) kosullar = [tutar >= 1000, tutar >= 500] oranlar = [0.15, 0.10] indirim = np.select(kosullar, oranlar, default=0.0) print(indirim) print(np.round(tutar * (1 - indirim), 2)) print(np.clip(np.array([-3, 20, 75]), 0, 50))
Birikimli hesaplar ve eksik değerli istatistik
cumsum yürüyen toplam (kümülatif ciro), diff ardışık fark (günlük değişim) verir. np.percentile ve np.median aykırı değere dayanıklı özetlerdir. Dizide np.nan varsa mean sonucu da nan olur; np.nanmean, np.nansum, np.nanmax eksikleri yok sayar.
import numpy as np gunluk = np.array([120.0, 135.0, np.nan, 110.0, 180.0]) print(np.cumsum(np.nan_to_num(gunluk))) # nan yerine 0 print(np.diff([120, 135, 150, 110])) # günlük değişim print(gunluk.mean(), np.nanmean(gunluk)) # nan vs 136.25 print(np.isnan(gunluk).sum(), "eksik gün") print(np.percentile([10, 20, 30, 40, 1000], 50)) # Matris çarpımı: adet (2 şube x 3 ürün) @ fiyat (3,) adet = np.array([[2, 1, 0], [5, 0, 3]]) fiyat = np.array([45.5, 899.0, 12.75]) print(adet @ fiyat) # şube başına ciro
0.1 + 0.2 == 0.3 numpy'da da False verir. Testlerde ve kontrol kodunda np.isclose(a, b) (eleman eleman) ya da np.allclose(a, b) (tek bool) kullan.d *= 1.2 yerinde çalışır ve dizinin türünü değiştiremez: tam sayı dizide hata verir; tek tek d[i] = d[i] * 1.2 atamak ise sessizce küsuratı keser. Para hesaplarında diziyi baştan float kur.np.select ile kademeli kural uygulamak (prim, indirim, risk sınıfı), np.percentile ile eşik belirlemek ve simülasyon için default_rng ile tekrarlanabilir veri üretmek. Projede numpy sürümünü requirements.txt içinde aralıkla sabitlemek, "benim bilgisayarımda çalışıyordu" sorunlarının çoğunu önler.Görünüm (view) mü kopya mı?
Dilimleme (d[2:5]) yeni bellek ayırmaz, aynı veriye bakan bir görünüm döndürür; görünümü değiştirirsen asıl dizi de değişir. Boolean maske ve fancy indeksleme ise kopya üretir. Bağımsız bir parça istiyorsan .copy() çağır. Aynı mantık reshape için de geçerlidir.
Bellek ve dtype
Bir milyon float64 yaklaşık 8 MB, float32 4 MB tutar. d.nbytes ile ölç. Tam sayı taşması (overflow) numpy'da sessizdir: int8 dizisine 200 yazarsan değer "döner". Kurumsal veride para tutarları için float64 ya da pandas tarafında Decimal benzeri çözümler tercih edilir.
İş hayatında
numpy'ı çoğu zaman doğrudan değil, pandas sütunlarının altındaki motor olarak kullanırsın: np.where ile koşullu sütun, np.percentile ile eşik, np.allclose ile kayan nokta karşılaştırması. Lineer cebir (np.linalg) ve makine öğrenmesi kütüphaneleri de ndarray bekler.