Ana içeriğe geç
Kesit Python
Orta · Veri analizine giriş

numpy: sayısal diziler ve vektörleştirme

ndarray, dtype ve şekil; döngüsüz (vektörleştirilmiş) hesap, broadcasting, maske ile seçim, eksen bazlı özet ve default_rng ile rastgele veri.

Uygulamada aç, 19 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

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 seçimiVarsayılan tam sayı türü 64 bit (int64), kayan nokta float64. Bellek kritikse 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 değil & ve |Maske birleştirirken Python'ın 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.

İfadeAnlamı (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")
Altın kuralnumpy dizisinin üstünde for döngüsü görürsen dur ve sor: bu işlem bir dizi ifadesiyle yazılabilir mi? Neredeyse her zaman evet.
Derinleş

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)
BelirtiNedenÇözüm
ModuleNotFoundError: No module named 'numpy'Paket başka bir Python'a kurulmuş ya da sanal ortam etkin değilOrtamı etkinleştir, python -m pip install numpy
A module that was compiled using NumPy 1.x cannot be run in NumPy 2.xEski derlenmiş bir paket (ör. eski pandas, scipy) numpy 2 ile uyumsuzO 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' outputTam sayı diziye yerinde (*=) ondalık sonuç yazılıyorDiziyi 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
Kayan nokta karşılaştırması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.
Yerinde işlem ve türd *= 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.
Gerçek işteAnalist ekiplerinde numpy çoğunlukla üç yerde görünür: pandas sütunlarına 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.
Uzmanlaş

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.

Alıştırmalara geç (19)