İçeriğe geç
Educora
Üniversite25 dk32 / 42

pandas ile veri analizi

Gerçek veriyle çalışmanın temel araçları: eksik değerler, `groupby` ve `agg`, tabloları `merge` ile birleştirme, `pivot_table`, `map` ve `apply`, tarihler ve bir kafe zincirinin satışlarının mini analizi.

Kendini test et
Bu derste öğreneceklerin
  • Eksik değerleri bulmak ve uygun bir stratejiyle (silme ya da doldurma) ele almak
  • groupby, adlandırılmış özetleme, transform ve pivot_table ile özet tablolar oluşturmak
  • Tabloları merge ile doğru birleştirmek ve sonuçtaki satır sayısını önceden bilmek
  • Tarihlerle çalışıp aylık rapor ve büyüme oranları hesaplamak

Gerçek veri hiçbir zaman ders kitabındaki kadar düzenli değildir: bazı hücreler boştur, bilgi birkaç tabloya dağılmıştır, tarihler metin olarak yazılmıştır. Bir analistin işi dört adımdan oluşur: temizle → birleştir → grupla → yanıtla. Bu derste her adım için pandas araçlarını öğrenecek, sonunda da iki şubesi olan bir kafe zincirinin çeyreklik satışlarını analiz edeceksin.

Eksik değerler

pandas eksik bir değeri NaN (Not a Number) olarak gösterir. isna().sum() her sütundaki boşlukları sayar. count() yalnızca dolu değerleri, size ise tüm satırları sayar. mean, sum, std gibi fonksiyonlar NaN değerlerini varsayılan olarak atlar. Sonra seçim yaparız: dropna() boşluk içeren satırları siler, fillna() ise onları doldurur: bir sabitle, medyanla ya da ortalamayla.

Python
import numpy as np
import pandas as pd

df = pd.DataFrame({
    'student': ['Aysel', 'Murad', 'Leyla', 'Elvin', 'Nigar'],
    'group': ['A', 'B', 'A', 'B', 'A'],
    'math': [91, np.nan, 85, 64, np.nan],
    'physics': [88, 72, np.nan, 70, 81],
})
print(df.isna().sum())
print(df['math'].mean(), df['math'].count())
print(df.dropna().shape)
filled = df.fillna({'math': df['math'].median(), 'physics': df['physics'].mean()})
print(filled)
print(np.nan == np.nan)
▸ Beklenen çıktı
student    0
group      0
math       2
physics    1
dtype: int64
80.0 3
(2, 4)
  student group  math  physics
0   Aysel     A  91.0    88.00
1   Murad     B  85.0    72.00
2   Leyla     A  85.0    77.75
3   Elvin     B  64.0    70.00
4   Nigar     A  85.0    81.00
False
Matematik ortalaması 80'dir, çünkü yalnızca 3 dolu değer dikkate alınır. dropna() 5 satırdan sadece 2'sini bıraktı; bilginin çoğu kayboldu.

Gruplama: böl, uygula, birleştir

Tanım
Böl–uygula–birleştir (split–apply–combine)

groupby, tabloyu bir anahtar sütunun değerlerine göre gruplara böler, her gruba bir fonksiyon (toplam, ortalama, sayı) uygular ve sonuçları yeni bir tabloda birleştirir. SQL'deki GROUP BY ile aynı fikirdir.

agg ile aynı anda birkaç göstergeyi hesaplarız; adlandırılmış özetleme ad=('sütun', 'fonksiyon') sonuç sütunlarına anlaşılır adlar verir. İki anahtarla gruplama çok düzeyli bir indeks (MultiIndex) oluşturur. transform ise sonucu asıl tablonun her satırına geri döndürür; örneğin her satışın kendi şubesinin gelirindeki payını bulmak için.

Python
import pandas as pd

df = pd.DataFrame({
    'store': ['Baku', 'Baku', 'Ganja', 'Baku', 'Ganja', 'Ganja', 'Baku'],
    'category': ['Food', 'Drinks', 'Food', 'Food', 'Drinks', 'Food', 'Drinks'],
    'amount': [120.0, 45.5, 80.0, 99.5, 30.0, 64.0, 52.0],
})
print(df.groupby('store')['amount'].sum())
summary = df.groupby('store').agg(
    total=('amount', 'sum'),
    average=('amount', 'mean'),
    orders=('amount', 'size'),
)
print(summary.round(2))
print(df.groupby(['store', 'category'])['amount'].sum())
df['share'] = (df['amount'] / df.groupby('store')['amount'].transform('sum')).round(3)
print(df.head(3))
▸ Beklenen çıktı
store
Baku     317.0
Ganja    174.0
Name: amount, dtype: float64
       total  average  orders
store                        
Baku   317.0    79.25       4
Ganja  174.0    58.00       3
store  category
Baku   Drinks       97.5
       Food        219.5
Ganja  Drinks       30.0
       Food        144.0
Name: amount, dtype: float64
   store category  amount  share
0   Baku     Food   120.0  0.379
1   Baku   Drinks    45.5  0.144
2  Ganja     Food    80.0  0.460
x̄_w = ∑ wᵢ · xᵢ / ∑ wᵢx̄_w = ∑ wᵢ · xᵢ / ∑ wᵢ
burada:
  • xᵢi. grubun ortalaması (örneğin ortalama fiş tutarı)
  • wᵢgrubun ağırlığı: genellikle gözlem sayısı

Ağırlıklı ortalama. pandas'ta: np.average(x, weights=w) ya da yalnızca genel toplamı genel sayıya bölmek.

Örnek 1: ortalamaların ortalaması tuzağı

Bakü şubesinde 300 fiş var, ortalama fiş 8 manat; Gence şubesinde 100 fiş var, ortalama fiş 12 manat. Tüm zincirin ortalama fişi nedir?

Çözümü göster
Yanlış yol: (8 + 12) / 2 = 10 manat; bu, iki şubeye eşit ağırlık verir.
Doğru yol: toplam gelir = 300 · 8 + 100 · 12 = 2400 + 1200 = 3600 manat, 400 fiş.
x̄_w = 3600 / 400 = 9 manat.
pandas'ta bir groupby(...).mean() sonucunun yeniden ortalamasını alma; ya asıl satırlar üzerinde mean() kullan ya da toplamları birbirine böl.

Tabloları birleştirmek: merge

Siparişler bir tabloda, müşteriler başka bir tabloda; onları ortak bir anahtar sütunla (customer_id) birleştiririz. how parametresi, anahtarı yalnızca bir tabloda bulunan satırlara ne olacağını belirler. indicator=True, her satırın nereden geldiğini gösteren bir _merge sütunu ekler.

howSonuçta hangi anahtarlar kalır
inneryalnızca iki tabloda da bulunanlar (varsayılan)
leftsol tablonun tüm anahtarları; sağda bulunamayanlar NaN olur
rightsağ tablonun tüm anahtarları
outeriki tablonun tüm anahtarları
Python
import pandas as pd

orders = pd.DataFrame({'order_id': [1, 2, 3, 4],
                       'customer_id': [10, 11, 10, 13],
                       'amount': [25.0, 40.0, 15.5, 60.0]})
customers = pd.DataFrame({'customer_id': [10, 11, 12],
                          'name': ['Aysel', 'Murad', 'Leyla']})
inner = orders.merge(customers, on='customer_id')
print(inner)
left = orders.merge(customers, on='customer_id', how='left')
print(left[['order_id', 'name']])
outer = orders.merge(customers, on='customer_id', how='outer', indicator=True)
print(outer[['customer_id', 'order_id', 'name', '_merge']])
▸ Beklenen çıktı
   order_id  customer_id  amount   name
0         1           10    25.0  Aysel
1         2           11    40.0  Murad
2         3           10    15.5  Aysel
   order_id   name
0         1  Aysel
1         2  Murad
2         3  Aysel
3         4    NaN
   customer_id  order_id   name      _merge
0           10       1.0  Aysel        both
1           10       3.0  Aysel        both
2           11       2.0  Murad        both
3           12       NaN  Leyla  right_only
4           13       4.0    NaN   left_only
13 numaralı müşteri müşteriler tablosunda yok, Leyla'nın (12) ise siparişi yok. outer sonucunda order_id ondalıklı sayıya dönüştü, çünkü tam sayı sütunu NaN tutamaz.
Örnek 2: sonuçta kaç satır olacak?

a) Yukarıdaki orders (4 satır) ve customers (3 satır) için inner, left ve outer birleştirmelerinde satır sayısını önceden bul. b) Bir anahtar sol tabloda 3, sağ tabloda 2 kez geçiyorsa inner birleştirme bu anahtar için kaç satır verir?

Çözümü göster
a) Anahtar 10 → 2 sipariş · 1 müşteri = 2 satır, anahtar 11 → 1 satır: inner = 3.
left tüm siparişleri korur: 4 (13 numaralı müşteri için ad NaN).
outer = 4 + Leyla (12) = 5.
b) Her sol satır her sağ satırla eşleşir: 3 · 2 = 6 satır. Bu “çoktan çoğa” patlamasıdır; tablo beklenmedik biçimde büyür ve toplamlar iki kez sayılır.

pivot_table, map, apply ve tarihler

pivot_table, Excel'deki özet tablonun aynısıdır: index satırları, columns sütunları, values ve aggfunc ise hücrelerde neyin hesaplanacağını belirler. fill_value=0 boş hücreleri doldurur, margins=True ise “All” adlı toplam satırı ve sütunu ekler.

Python
import pandas as pd

df = pd.DataFrame({
    'store': ['Baku', 'Baku', 'Ganja', 'Baku', 'Ganja', 'Ganja', 'Baku'],
    'category': ['Food', 'Drinks', 'Food', 'Food', 'Drinks', 'Food', 'Drinks'],
    'amount': [120.0, 45.5, 80.0, 99.5, 30.0, 64.0, 52.0],
})
pt = df.pivot_table(index='store', columns='category', values='amount',
                    aggfunc='sum', fill_value=0, margins=True)
print(pt)
print(df.pivot_table(index='store', columns='category', values='amount', aggfunc='count'))
▸ Beklenen çıktı
category  Drinks   Food    All
store                         
Baku        97.5  219.5  317.0
Ganja       30.0  144.0  174.0
All        127.5  363.5  491.0
category  Drinks  Food
store                 
Baku           2     2
Ganja          1     2

Bir sütunu dönüştürmenin üç yolu vardır: map değerleri bir sözlüğe göre değiştirir, apply her elemana istenen bir fonksiyonu uygular, pd.cut ise sayıları aralıklara (kutulara) ayırır.

Python
import numpy as np
import pandas as pd

df = pd.DataFrame({'code': ['BAK', 'GNJ', 'SMQ', 'BAK'], 'amount': [120.0, 45.5, 80.0, 30.0]})
df['city'] = df['code'].map({'BAK': 'Baku', 'GNJ': 'Ganja', 'SMQ': 'Sumgait'})
df['size_apply'] = df['amount'].apply(lambda x: 'large' if x >= 60 else 'small')
df['size_where'] = np.where(df['amount'] >= 60, 'large', 'small')
df['band'] = pd.cut(df['amount'], bins=[0, 50, 100, 200], labels=['low', 'mid', 'high'])
print(df)
print((df['size_apply'] == df['size_where']).all())
▸ Beklenen çıktı
  code  amount     city size_apply size_where  band
0  BAK   120.0     Baku      large      large  high
1  GNJ    45.5    Ganja      small      small   low
2  SMQ    80.0  Sumgait      large      large   mid
3  BAK    30.0     Baku      small      small   low
True

Metin olarak saklanan tarihleri pd.to_datetime ile (ya da read_csv(..., parse_dates=['date']) ile) gerçek tarihlere çeviririz. Sonra .dt üzerinden yıl, ay ve haftanın günü alınır; to_period('M') ise tarihi ayına “yuvarlar”. İki tarihin farkı bir Timedelta'dır.

Python
import pandas as pd

sales = pd.DataFrame({
    'date': ['2026-01-15', '2026-01-28', '2026-02-03', '2026-02-20', '2026-03-05', '2026-03-18'],
    'amount': [310.0, 275.5, 402.0, 350.0, 390.0, 441.5],
})
sales['date'] = pd.to_datetime(sales['date'])
sales['month'] = sales['date'].dt.to_period('M')
sales['weekday'] = sales['date'].dt.day_name()
print(sales)
print(sales.groupby('month')['amount'].sum())
print((sales['date'].max() - sales['date'].min()).days, 'days')
▸ Beklenen çıktı
        date  amount    month    weekday
0 2026-01-15   310.0  2026-01   Thursday
1 2026-01-28   275.5  2026-01  Wednesday
2 2026-02-03   402.0  2026-02    Tuesday
3 2026-02-20   350.0  2026-02     Friday
4 2026-03-05   390.0  2026-03   Thursday
5 2026-03-18   441.5  2026-03  Wednesday
month
2026-01    585.5
2026-02    752.0
2026-03    831.5
Freq: M, Name: amount, dtype: float64
62 days

Mini analiz: bir kafe zincirinin satışları

Bir kafe zincirinin Bakü ve Gence'de şubeleri var. Satış kaydında tarih, şube, ürün kodu ve adet; ayrı bir tabloda ise ürün adları ve manat cinsinden fiyatlar tutuluyor (veriler uydurmadır). Yönetim soruyor: her şube her ay ne kadar gelir getirdi, toplam gelir nasıl değişti ve hangi ürün önde?

  1. 1
    Oku

    Kaydı read_csv ile oku ve tarihleri parse_dates ile hemen dönüştür.

  2. 2
    Birleştir

    Ürünler tablosunu how='left' ve validate='many_to_one' ile ekle.

  3. 3
    Hesapla

    Gelir = adet · fiyat; tarihten ayı çıkar.

  4. 4
    Özetle

    pivot_table ile ay × şube tablosu oluştur, toplamı ve büyüme oranını ekle, ürünleri gelire göre sırala.

Δ% = (xₜ − xₜ₋₁) / xₜ₋₁ · 100%Δ% = (xₜ − xₜ₋₁) / xₜ₋₁ · 100%
burada:
  • xₜmevcut dönemin değeri
  • xₜ₋₁önceki dönemin değeri

Büyüme oranı; pandas'ta pct_change() bunu hesaplar (100 ile çarpmadan). İlk dönemin önceki değeri olmadığı için NaN çıkar.

Python
import io
import pandas as pd

sales_csv = '''date,store,product_id,qty
2026-01-05,Baku,1,30
2026-01-12,Ganja,2,25
2026-01-20,Baku,3,12
2026-02-02,Baku,1,34
2026-02-14,Ganja,3,18
2026-02-25,Ganja,1,20
2026-03-03,Baku,2,41
2026-03-15,Ganja,2,28
2026-03-28,Baku,3,15'''
products = pd.DataFrame({'product_id': [1, 2, 3], 'product': ['Tea', 'Coffee', 'Cake'],
                         'price': [4.5, 6.0, 12.0]})
sales = pd.read_csv(io.StringIO(sales_csv), parse_dates=['date'])
df = sales.merge(products, on='product_id', how='left', validate='many_to_one')
df['revenue'] = df['qty'] * df['price']
df['month'] = df['date'].dt.month
report = df.pivot_table(index='month', columns='store', values='revenue',
                        aggfunc='sum', fill_value=0)
report['total'] = report.sum(axis=1)
report['growth_%'] = report['total'].pct_change().mul(100).round(1)
print(report)
print(df.groupby('product')['revenue'].sum().sort_values(ascending=False))
▸ Beklenen çıktı
store   Baku  Ganja  total  growth_%
month                               
1      279.0  150.0  429.0       NaN
2      153.0  306.0  459.0       7.0
3      426.0  168.0  594.0      29.4
product
Coffee    564.0
Cake      540.0
Tea       378.0
Name: revenue, dtype: float64

Bulgular: çeyrek boyunca toplam gelir 429 manattan 594 manata çıktı, martta büyüme %29,4 oldu; bu, esas olarak Bakü şubesindeki kahve ve pasta satışları sayesinde. Şubatta Gence, Bakü'yü geçti çünkü orada pastalar iyi sattı. Çeyreğin lider ürünü kahve (564 manat). Çay gelirde sonuncu (378 manat): 84 bardak satıldı ama fiyatı düşük; oysa yalnızca 45 pasta 540 manat getirdi. Tek bir tablo ve birkaç satır kod yönetime somut kararlar için bir temel sağladı.

Alıştırma

Harcamalar tablosuna göre ayrı satırlarda yazdır: 1) her kişinin toplam harcamasını sözlük olarak (to_dict()); 2) toplamı en büyük olan kategoriyi; 3) her kişinin harcama sayısını sözlük olarak.

Alıştırma · Python
import pandas as pd

expenses = pd.DataFrame({
    'person': ['Aysel', 'Murad', 'Aysel', 'Leyla', 'Murad', 'Aysel', 'Leyla'],
    'category': ['food', 'transport', 'books', 'food', 'food', 'food', 'books'],
    'amount': [12.5, 3.0, 25.0, 18.0, 9.5, 7.0, 14.0],
})
# 1) total per person (dict)
# 2) category with the largest total
# 3) number of expenses per person (dict)
▸ Beklenen çıktı
{'Aysel': 44.5, 'Leyla': 32.0, 'Murad': 12.5}
food
{'Aysel': 3, 'Leyla': 2, 'Murad': 2}
Alıştırma

Siparişleri müşterilerle how='left' kullanarak birleştir ve bulunamayan şehirleri 'Unknown' ile doldur. Sonra yazdır: 1) şehirlere göre sipariş tutarlarının toplamını sözlük olarak; 2) Bakü'nün toplam tutardaki payını yüzde olarak, 1 ondalık basamağa yuvarlanmış.

Alıştırma · Python
import pandas as pd

orders = pd.DataFrame({'order_id': [1, 2, 3, 4, 5], 'customer_id': [1, 2, 1, 3, 4],
                       'amount': [30.0, 45.0, 20.0, 15.0, 50.0]})
customers = pd.DataFrame({'customer_id': [1, 2, 3], 'city': ['Baku', 'Ganja', 'Baku']})
# merge, fill missing cities, then print the two results
▸ Beklenen çıktı
{'Baku': 65.0, 'Ganja': 45.0, 'Unknown': 50.0}
40.6

Önemli noktalar

  • isna().sum() boşlukları sayar; dropna siler, fillna doldurur; NaN == NaN False verir.
  • groupby + agg böl–uygula–birleştir şemasını uygular; transform sonucu her satıra geri verir.
  • Grup ortalamalarını birleştirirken ağırlıklı ortalamayı kullan: ∑ wᵢxᵢ / ∑ wᵢ.
  • merge için how (inner, left, right, outer) sonucu belirler; tekrarlanan anahtarlar satırları çoğaltır, validate ile kontrol et.
  • pivot_table, Excel'in özet tablosudur; mümkün olduğunda apply yerine vektörel işlemleri seç.
  • Tarihleri to_datetime ya da parse_dates ile dönüştür, sonra dönemleri .dt ve pct_change ile analiz et.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
s = pd.Series([4, np.nan, 8]) için s.mean() ve s.count() nedir?