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

groupby, pivot ve merge

groupby/agg/transform ile özet, pivot_table ile çapraz tablo, merge/concat ile tablo birleştirme, resample ile zaman serisi ve apply tuzağı; Anadolu Market satış analizi.

Uygulamada aç, 30 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ç.

Soru: "Hangi şehir, hangi kategoride ne kadar satıyor?"

Anadolu Market yönetimi her ay aynı soruları sorar: şehir bazında ciro, kategori bazında ortalama sepet, müşteri başına sipariş sayısı, haftalık trend. Bu soruların hepsi "böl → hesapla → birleştir" (split-apply-combine) kalıbıdır ve pandas'ta groupby ile yazılır. Derste tek bir sipariş tablosu üzerinden tüm araçları sırayla göreceğiz.

groupby ve agg

df.groupby("sehir") tabloyu şehir gruplarına böler; ardından ["tutar"].sum() gibi bir özet uygulanır. Birden çok özet için agg: sözlükle sütun başına fonksiyon, ya da adlandırılmış agg ile çıktı sütun adlarını kendin ver.

import pandas as pd, io

CSV = """siparis_id,musteri,sehir,kategori,adet,fiyat,tarih
1001,Ayşe,İstanbul,Gıda,3,45.5,2024-03-01
1002,Mehmet,Ankara,Elektronik,1,899.0,2024-03-01
1003,Ayşe,İstanbul,Gıda,10,12.75,2024-03-04
1004,Zeynep,İzmir,Kozmetik,2,129.9,2024-03-05
1005,Ali,Ankara,Gıda,6,45.5,2024-03-09
1006,Zeynep,İzmir,Elektronik,1,1250.0,2024-03-12
1007,Mehmet,Ankara,Gıda,4,12.75,2024-03-15
1008,Ayşe,İstanbul,Kozmetik,1,129.9,2024-03-20
"""
df = pd.read_csv(io.StringIO(CSV), parse_dates=["tarih"])
df["tutar"] = df["adet"] * df["fiyat"]

print(df.groupby("sehir")["tutar"].sum())
print(df.groupby("sehir").agg(
    ciro=("tutar", "sum"),
    siparis=("siparis_id", "count"),
    ort_sepet=("tutar", "mean"),
).round(1))
print(df.groupby(["sehir", "kategori"])["tutar"].sum().reset_index().head(4))
reset_indexgroupby sonucu grup anahtarlarını index yapar. Raporda düz tablo istiyorsan reset_index() ya da baştan groupby(..., as_index=False).

transform: grubun özetini satıra geri yaz

agg grup başına bir satır verir; transform ise her satıra kendi grubunun değerini yazar, uzunluk değişmez. "Her siparişin, şehrinin toplam cirosundaki payı" sorusu bunu ister.

import pandas as pd

df = pd.DataFrame({
    "sehir": ["Ankara", "Ankara", "İzmir", "İzmir", "İzmir"],
    "tutar": [899.0, 273.0, 259.8, 1250.0, 51.0],
})
df["sehir_toplam"] = df.groupby("sehir")["tutar"].transform("sum")
df["pay"] = (df["tutar"] / df["sehir_toplam"] * 100).round(1)
df["sehir_sira"] = df.groupby("sehir")["tutar"].rank(ascending=False).astype(int)
print(df)
print(df[df["sehir_sira"] == 1])      # her şehrin en büyük siparişi

pivot_table: çapraz tablo

Satırlarda şehir, sütunlarda kategori, hücrede ciro: Excel'in pivotu. fill_value boş kombinasyonu doldurur, margins=True toplam satır/sütun ekler.

import pandas as pd

df = pd.DataFrame({
    "sehir": ["İstanbul", "Ankara", "İstanbul", "İzmir", "Ankara", "İzmir"],
    "kategori": ["Gıda", "Elektronik", "Gıda", "Kozmetik", "Gıda", "Elektronik"],
    "tutar": [136.5, 899.0, 127.5, 259.8, 273.0, 1250.0],
})
pv = pd.pivot_table(df, index="sehir", columns="kategori", values="tutar",
                    aggfunc="sum", fill_value=0, margins=True, margins_name="Toplam")
print(pv)
print(pd.crosstab(df["sehir"], df["kategori"]))   # yalnızca sayım

merge, join, concat

Siparişler bir tabloda, müşteri bilgileri (segment, kayıt tarihi) başka tabloda. SQL JOIN'in karşılığı pd.merge. how parametresi hangi satırların kalacağını belirler:

howSonuçTipik kullanım
innerİki tarafta da eşleşenlerYalnızca bilinen müşterilerin siparişleri
leftSol tablonun tümü, sağdan eşleşenSiparişleri koru, müşteri bilgisi yoksa NaN
outerHer iki tablonun tümüUyuşmazlık denetimi
concatAlt alta (ya da yan yana) eklemeAylık dosyaları birleştirme
import pandas as pd

siparis = pd.DataFrame({"siparis_id": [1, 2, 3, 4], "musteri_id": [10, 20, 10, 99], "tutar": [136.5, 899.0, 127.5, 50.0]})
musteri = pd.DataFrame({"musteri_id": [10, 20, 30], "ad": ["Ayşe", "Mehmet", "Zeynep"], "segment": ["Altın", "Gümüş", "Gümüş"]})

print(pd.merge(siparis, musteri, on="musteri_id", how="inner"))
sol = pd.merge(siparis, musteri, on="musteri_id", how="left", indicator=True)
print(sol[["siparis_id", "ad", "_merge"]])
print(sol[sol["ad"].isna()]["siparis_id"].tolist(), "-> müşterisi bilinmeyen sipariş")

mart = pd.DataFrame({"ay": ["Mart"], "ciro": [1213.0]})
nisan = pd.DataFrame({"ay": ["Nisan"], "ciro": [1480.5]})
print(pd.concat([mart, nisan], ignore_index=True))
Satır patlamasıSağ tabloda anahtar tekrar ediyorsa (bir müşteri iki satır) merge sonrası sol tablo satırları çoğalır ve ciro iki kat çıkar. Birleştirmeden önce musteri["musteri_id"].is_unique kontrol et ya da validate="many_to_one" ver; pandas tekrar varsa hata fırlatır.

Zaman serisi: resample

Tarih sütununu index yapınca (set_index("tarih")) pandas zamanı anlar: resample("W") haftalık, "ME" ay sonu, "D" günlük kovalara böler ve groupby gibi özetler. .dt erişimcisi ile ay, hafta günü gibi parçalar sütun olarak çıkarılır.

import pandas as pd

df = pd.DataFrame({
    "tarih": pd.to_datetime(["2024-03-01", "2024-03-01", "2024-03-04", "2024-03-05",
                             "2024-03-09", "2024-03-12", "2024-03-15", "2024-03-20"]),
    "tutar": [136.5, 899.0, 127.5, 259.8, 273.0, 1250.0, 51.0, 129.9],
})
haftalik = df.set_index("tarih")["tutar"].resample("W").sum()
print(haftalik)
df["gun_adi"] = df["tarih"].dt.day_name()
df["hafta"] = df["tarih"].dt.isocalendar().week
print(df.groupby("hafta", observed=True)["tutar"].agg(["sum", "count"]))
print(haftalik.rolling(2).mean().round(1).dropna())    # hareketli ortalama

apply tuzağı

df.apply(func, axis=1) her satır için Python fonksiyonu çağırır: esnek ama yavaştır, çünkü vektörleştirme (vectorization) kaybolur. Çoğu "satır bazlı" iş aslında sütun aritmetiği, np.where, np.select ya da map ile yazılabilir. apply'ı son çare olarak, küçük veride ya da gerçekten karmaşık mantıkta kullan.

import pandas as pd, numpy as np

df = pd.DataFrame({"tutar": [136.5, 899.0, 127.5, 1250.0, 51.0], "segment": ["Altın", "Gümüş", "Altın", "Gümüş", "Bronz"]})

# yavaş yol
df["indirim_a"] = df.apply(lambda r: 0.10 if r["segment"] == "Altın" else 0.0, axis=1)
# hızlı yol: map ve np.select
df["indirim_b"] = df["segment"].map({"Altın": 0.10, "Gümüş": 0.05}).fillna(0.0)
df["kademe"] = np.select([df["tutar"] >= 1000, df["tutar"] >= 500], ["büyük", "orta"], default="küçük")
print(df)
Derinleş

Derinleş: groupby'ın ince ayarları ve güvenli birleştirme

groupby, pivot ve merge ile raporun iskeletini kurabiliyorsun. Bu bölüm, raporda rakamları sessizce yanlış çıkaran ayrıntılara odaklanıyor: eksik anahtarlı satırların kaybolması, count ile size farkı, çoklu özetten gelen iki katlı sütun adları, ağırlıklı ortalama ve farklı adlı anahtarlarla birleştirme. Pencere fonksiyonları (window function), MultiIndex ve geniş-uzun dönüşüm "İleri pandas" dersinde, zaman kovaları "Zaman serisi" dersinde ayrıntılı ele alınıyor.

Grubun içine bakmak: size, count, nunique, first

groupby nesnesi bir döngüde gezilebilir: her adımda grup anahtarı ve o gruba ait alt tablo gelir. Hata ayıklarken ya da her grup için ayrı dosya üretirken işe yarar. Özetlerde ise dört sayım birbirine karıştırılır:

YöntemNe sayarEksik değer
size()Gruptaki satır sayısıSatır sayılır
count()Sütunda dolu hücre sayısıNaN sayılmaz
nunique()Farklı değer sayısı (tekil müşteri)NaN sayılmaz
first() / last()Gruptaki ilk / son dolu değerNaN atlanır
import io
import pandas as pd

CSV = """siparis_id,musteri,sehir,kategori,adet,fiyat
1,Ayşe,İstanbul,Gıda,3,45.5
2,Mehmet,Ankara,Elektronik,1,899.0
3,Ayşe,İstanbul,Gıda,10,12.75
4,Zeynep,,Kozmetik,2,129.9
5,Ali,Ankara,Gıda,6,45.5
6,Zeynep,İzmir,Elektronik,1,1250.0
7,Ayşe,İstanbul,Kozmetik,1,
"""
df = pd.read_csv(io.StringIO(CSV))
df["tutar"] = df["adet"] * df["fiyat"]

for sehir, grup in df.groupby("sehir"):
    print(sehir, len(grup), grup["siparis_id"].tolist())

g = df.groupby("sehir")
print(g.size().to_dict())                 # satır sayısı
print(g["fiyat"].count().to_dict())       # dolu fiyat sayısı
print(g["musteri"].nunique().to_dict())   # tekil müşteri
print(g.get_group("Ankara")["tutar"].sum())

Çıktıya dikkat: 4 numaralı siparişin şehri boş ve hiçbir grupta görünmüyor. İstanbul'da 3 satır var ama 2 dolu fiyat var.

Eksik anahtar: dropna=False

groupby varsayılan olarak anahtarı eksik (NaN) olan satırları atar. Şehri girilmemiş siparişlerin cirosu raporda yok olur ve toplamlar tutmaz. Ya anahtarı önce doldur (fillna("Bilinmiyor")) ya da dropna=False ver. Raporun en altına "gruplar toplamı = genel toplam" kontrolü koymak bu hatayı hemen yakalar.

import io
import pandas as pd

CSV = """siparis_id,musteri,sehir,kategori,adet,fiyat
1,Ayşe,İstanbul,Gıda,3,45.5
2,Mehmet,Ankara,Elektronik,1,899.0
3,Ayşe,İstanbul,Gıda,10,12.75
4,Zeynep,,Kozmetik,2,129.9
5,Ali,Ankara,Gıda,6,45.5
6,Zeynep,İzmir,Elektronik,1,1250.0
7,Ayşe,İstanbul,Kozmetik,1,
"""
df = pd.read_csv(io.StringIO(CSV))
df["tutar"] = df["adet"] * df["fiyat"]

print(df.groupby("sehir")["tutar"].sum().sum(), "≠", df["tutar"].sum())
print(df.groupby("sehir", dropna=False)["tutar"].sum())
rapor = df.assign(sehir=df["sehir"].fillna("Bilinmiyor")).groupby("sehir")["tutar"].sum()
assert abs(rapor.sum() - df["tutar"].sum()) < 1e-9, "toplam tutmuyor"
print(rapor.round(2).to_dict())

filter: koşulu sağlayan grupları tut

filter(fonksiyon) her grubu fonksiyona verir; True dönen grupların satırlarını (özet değil) olduğu gibi döndürür. "En az 2 sipariş vermiş müşterilerin siparişleri" ya da "cirosu 500 TL üstü şehirlerin satırları" böyle yazılır. Aynı sonucu transform ile maske kurarak da alabilirsin; büyük veride transform genellikle daha hızlıdır.

import io
import pandas as pd

CSV = """siparis_id,musteri,sehir,kategori,adet,fiyat
1,Ayşe,İstanbul,Gıda,3,45.5
2,Mehmet,Ankara,Elektronik,1,899.0
3,Ayşe,İstanbul,Gıda,10,12.75
4,Zeynep,,Kozmetik,2,129.9
5,Ali,Ankara,Gıda,6,45.5
6,Zeynep,İzmir,Elektronik,1,1250.0
7,Ayşe,İstanbul,Kozmetik,1,
"""
df = pd.read_csv(io.StringIO(CSV))
df["tutar"] = df["adet"] * df["fiyat"]

sadik = df.groupby("musteri").filter(lambda g: len(g) >= 2)
print(sadik["musteri"].unique().tolist())

maske = df.groupby("musteri")["siparis_id"].transform("size") >= 2
print(df[maske]["siparis_id"].tolist())

Çoklu özet, düz sütun adları ve ağırlıklı ortalama

agg(["sum", "mean"]) iki katlı (MultiIndex) sütun adı üretir: ("tutar", "sum"). Excel'e yazmadan önce bunları "tutar_sum" gibi düzleştirmek gerekir. Adlandırılmış agg (ciro=("tutar", "sum")) baştan düz ad verdiği için çoğu zaman daha iyidir. Hazır fonksiyon yetmediğinde agg'a kendi fonksiyonunu verebilirsin; ama ağırlıklı ortalama gibi iki sütun gerektiren özetleri ayrı toplamlarla hesaplamak hem hızlı hem doğrudur: ortalama birim fiyat = toplam tutar / toplam adet.

import io
import pandas as pd

CSV = """siparis_id,musteri,sehir,kategori,adet,fiyat
1,Ayşe,İstanbul,Gıda,3,45.5
2,Mehmet,Ankara,Elektronik,1,899.0
3,Ayşe,İstanbul,Gıda,10,12.75
4,Zeynep,,Kozmetik,2,129.9
5,Ali,Ankara,Gıda,6,45.5
6,Zeynep,İzmir,Elektronik,1,1250.0
7,Ayşe,İstanbul,Kozmetik,1,
"""
df = pd.read_csv(io.StringIO(CSV))
df["tutar"] = df["adet"] * df["fiyat"]

coklu = df.groupby("kategori")["tutar"].agg(["sum", "mean", "max"])
print(coklu.columns.tolist())
iki = df.groupby("kategori").agg({"tutar": ["sum", "max"], "adet": ["sum"]})
iki.columns = ["_".join(c) for c in iki.columns]
print(iki.reset_index())

# Ağırlıklı ortalama fiyat: düz ortalama yanıltır
toplam = df.dropna(subset=["fiyat"]).groupby("kategori")[["tutar", "adet"]].sum()
print((toplam["tutar"] / toplam["adet"]).round(2).to_dict())
print(df.groupby("kategori")["fiyat"].mean().round(2).to_dict())   # düz ortalama
print(df.groupby("kategori")["fiyat"].agg(lambda s: s.max() - s.min()).to_dict())   # özel fonksiyon

Oranlar: satır yüzdeleri

"Her şehirde cironun yüzde kaçı hangi kategoriden?" sorusu bir pivotun her satırını kendi toplamına bölmektir: pv.div(pv.sum(axis=1), axis=0). Sayımlar için pd.crosstab(..., normalize="index") aynı işi tek adımda yapar.

merge ayrıntıları

DurumÇözüm
Anahtar sütunların adı farklı (musteri_id ve id)left_on="musteri_id", right_on="id", sonra drop(columns="id")
İki tabloda aynı adlı başka sütun varOtomatik _x/_y eki; anlamlı ad için suffixes=("_siparis", "_hedef")
Anahtar iki sütundan oluşuyoron=["sehir", "ay"]
Sağ tablonun anahtarı index'tedf.join(diger, on="musteri_id") ya da right_index=True
Aylık dosyaları birleştirip kaynağı korumakpd.concat([mart, nisan], keys=["Mart", "Nisan"])
import pandas as pd

hedef = pd.DataFrame({"sehir": ["Ankara", "İzmir"], "ay": [3, 3], "tutar": [1000.0, 1500.0]})
gercek = pd.DataFrame({"sehir": ["Ankara", "İzmir", "Ankara"], "ay": [3, 3, 4], "tutar": [1172.0, 1250.0, 900.0]})
k = gercek.merge(hedef, on=["sehir", "ay"], how="left", suffixes=("_gercek", "_hedef"))
k["gerceklesme"] = (k["tutar_gercek"] / k["tutar_hedef"] * 100).round(1)
print(k)

pv = pd.DataFrame({"Gıda": [300.0, 50.0], "Elektronik": [700.0, 150.0]}, index=["Ankara", "İzmir"])
print(pv.div(pv.sum(axis=1), axis=0).mul(100).round(1))
Rapor sonu kontrolleriHer özet tablonun altına iki satırlık kontrol koy: grupların toplamı genel toplama eşit mi, merge öncesi ve sonrası satır sayısı beklendiği gibi mi? assert len(sonra) == len(once) left merge'de satır patlamasını, toplam kontrolü kaybolan NaN anahtarlarını yakalar.
Gerçek işteYönetime giden rakamların en sık yanlış çıkma sebebi hatalı formül değil, sessiz veri kaybıdır: eksik şehirli siparişlerin groupby'da düşmesi, müşteri tablosundaki tekrarlı kayıt yüzünden cironun iki kat sayılması, fiyatın düz ortalamasının ağırlıklı ortalama sanılması. Deneyimli analistler her raporun sonunda "mutabakat" (reconciliation) satırı tutar: kaynak toplam, rapor toplamı, fark.
Derinleş

groupby.apply ve pd.Grouper

agg her gruptan tek değer üretir. Grup başına birkaç satır gerekiyorsa, örneğin her şehrin en büyük üç siparişi, apply kullanırsın; daha yavaştır, önce agg ya da transform dene. include_groups=False grup sütununu fonksiyona vermez; pandas 2.2 ve 3 için aynı sonucu verir. Zamanı dönemlere bölüp başka sütunla birlikte gruplamak için pd.Grouper(key="tarih", freq="MS") kullanılır; MS ay başı demektir.

Derinleş

Derinleş: gerçek sipariş satırlarıyla groupby

Derste groupby, merge ve pivot'u küçük tablolarla öğrendin. Gerçek e-ticaret verisinde işi zorlaştıran şey fonksiyonlar değil, tablonun tanecik düzeyi (grain): her satır neyi temsil ediyor? Anadolu Market'te orders tablosunda her satır bir sipariştir (910 satır), order_items tablosunda ise her satır bir sipariş kalemidir (1.620 satır). Bir siparişte 1 ile 4 arasında kalem olabilir. Tanecik düzeyini karıştırmak, analistlerin en sık yaptığı ve en zor fark edilen hatadır.

TabloBir satır =AnahtarCiro hesabı
ordersbir siparişorder_idyok (tutar sütunu yok)
order_itemsbir sipariş kalemiitem_idquantity * unit_price * (1 - discount)
productsbir ürünproduct_idgüncel price (satış anındaki fiyat değil!)
customersbir müştericustomer_idyok

Güvenli birleştirme: validate ile tanecik kontrolü

Kalemleri siparişlerle birleştirince satır sayısı 1.620 kalmalı: her kalemin tam bir siparişi vardır. Bunu umut etmek yerine validate="many_to_one" ile garanti altına al: sağ tabloda anahtar tekrar ederse pandas hata verir. indicator=True ise hangi satırın iki tarafta da bulunduğunu gösterir.

import veri

o = veri.oku("anadolu_market", "orders")
oi = veri.oku("anadolu_market", "order_items")
oi["ciro"] = oi["quantity"] * oi["unit_price"] * (1 - oi["discount"])

d = oi.merge(o, on="order_id", how="left", validate="many_to_one", indicator=True)
print(len(oi), len(d))                       # 1620 1620
print(d["_merge"].value_counts().to_dict())  # hepsi both

İki adımlı toplama: önce siparişe, sonra segmente

"Ortalama sepet tutarı" (AOV, average order value) sipariş başına cirodur. Kalem tablosunda doğrudan ciro.mean() alırsan kalem başına ortalamayı bulursun; bu rakam AOV'nin yaklaşık yarısıdır. Doğru yol iki adımlıdır: önce order_id düzeyine topla, sonra ortala.

import veri

o = veri.oku("anadolu_market", "orders")
oi = veri.oku("anadolu_market", "order_items")
oi["ciro"] = oi["quantity"] * oi["unit_price"] * (1 - oi["discount"])
d = oi.merge(o, on="order_id")
d = d[d["status"] == "delivered"]

yanlis = d["ciro"].mean()                              # kalem ortalaması
sepet = d.groupby("order_id")["ciro"].sum()            # 1. adım: sipariş düzeyi
print("kalem başına:", round(yanlis, 2))
print("sipariş başına (AOV):", round(sepet.mean(), 2))
print("sipariş sayısı:", d["order_id"].nunique(), "| kalem sayısı:", len(d))
size() mi nunique() mi?Birleştirilmiş kalem tablosunda groupby("segment").size() sipariş değil kalem sayar. Sipariş sayısı için groupby("segment")["order_id"].nunique() kullan. Raporda "sipariş" yazan her sütunun hangi tanecikte sayıldığını kontrol et.

Gerçek pivot ve grup içi pay

Kategori × kanal çapraz tablosu, yönetimin en sık istediği görünümdür. transform("sum") ise her satıra kendi grubunun toplamını yazar; böylece "ürünün kategori içindeki payı" tek satırda hesaplanır.

import veri

o = veri.oku("anadolu_market", "orders")
oi = veri.oku("anadolu_market", "order_items")
p = veri.oku("anadolu_market", "products")
oi["ciro"] = oi["quantity"] * oi["unit_price"] * (1 - oi["discount"])
d = oi.merge(o, on="order_id").merge(p, on="product_id")
d = d[d["status"] == "delivered"]

pv = d.pivot_table(index="category", columns="channel", values="ciro",
                   aggfunc="sum", fill_value=0, margins=True, margins_name="Toplam")
print(pv.round(0))

urun = d.groupby(["category", "name"], as_index=False)["ciro"].sum()
urun["pay"] = urun["ciro"] / urun.groupby("category")["ciro"].transform("sum")
print(urun.sort_values("pay", ascending=False).head(3))

Hiç sipariş vermeyen müşteriler: left merge ve indicator

"Kaç müşterimiz hiç sipariş vermedi?" sorusu, birleştirmenin yönüyle ilgilidir. Müşteri tablosunu sola koyup how="left" ve indicator=True ile birleştirirsen, sağda karşılığı olmayan satırlar left_only olarak işaretlenir. Sayımı önce müşteri düzeyine indirmeyi unutma: bir müşteri birden çok siparişle eşleşir.

import veri

c = veri.oku("anadolu_market", "customers")
o = veri.oku("anadolu_market", "orders")
d = c.merge(o[["order_id", "customer_id"]], on="customer_id", how="left", indicator=True)
print(len(c), len(d))
siparissiz = d.loc[d["_merge"] == "left_only", "customer_id"].nunique()
print("hiç sipariş vermeyen:", siparissiz)
print("müşteri başına sipariş:", round(len(o) / c["customer_id"].nunique(), 2))
Gerçek işteBir panoda "Ortalama sepet 841 TL" ile "Ortalama sepet 1.489 TL" yan yana durabilir; ikisi de aynı veriden, farklı taneciklerden hesaplanmıştır. Kıdemli analistler her metriğin yanına tanımını yazar: "AOV = teslim edilmiş siparişlerde, sipariş başına indirimli ciro". Kesit SQL'de aynı soruyu GROUP BY order_id alt sorgusuyla çözmüştün; Kesit Power BI'da ise AVERAGEX(VALUES(Satislar[SiparisNo]), ...) aynı iki adımlı mantıktır.
Uzmanlaş

Anadolu Market aylık rapor kalıbı

Üretimde rapor üç fonksiyona ayrılır: yukle() ham veriyi getirir ve temizler; zenginlestir() müşteri ve ürün tablolarıyla merge eder, türetilmiş sütunları ekler; ozetle() groupby/pivot ile çıktı tablolarını üretir. Her birinin girdi-çıktı sözleşmesi (sütun adları ve türleri) testle korunur. Böylece "geçen ay raporu neden farklı?" sorusunun cevabı dakikada bulunur.

category ve observed

Şehir gibi sütunları category yaptıysan groupby(..., observed=True) yaz; aksi hâlde veride olmayan kategoriler sıfır satırı olarak gelir ve pandas 2.x uyarı verir (3.x'te varsayılan True oldu). Kod iki sürümde de aynı davransın diye parametreyi her zaman açıkça ver.

Pencere fonksiyonları

rolling (hareketli), expanding (kümülatif), shift (önceki dönem) ve pct_change ile "geçen haftaya göre değişim" hesaplanır. Grup içinde pencere için groupby("sehir")["tutar"].shift(1) deseni SQL'deki LAG ile aynıdır.

Alıştırmalara geç (30)