Ana içeriğe geç
Kesit Python
İleri · Mülakat

Veri rolleri mülakatı: SQL, pandas, istatistik ve vaka soruları

Analist, bilimci ve veri mühendisi mülakatları için 15 SQL kalıbı, pandas canlı kodlama (merge, transform, pivot, zaman penceresi), istatistik soruları (p-değeri, GA, güç, SRM, çoklu test, bootstrap), "metrik düştü" vaka çerçevesi, ML ve veri mühendisliği soruları, take-home teslimi ve Anadolu Market verisinde zamanlı deneme seti.

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

Veri mülakatı neyi ölçer?

Mülakat hazırlığı dersinde algoritma kalıplarını ve canlı kodlamayı gördün. Veri rollerinde (analist, veri bilimci, veri mühendisi, ML mühendisi) mülakat başka bir yere ağırlık verir: gerçek bir tabloda doğru sayıyı hızlı ve savunulabilir biçimde üretmek, istatistiksel sonucu doğru yorumlamak ve belirsiz bir iş sorusunu analize çevirmek. Tipik bir süreç dört turdan oluşur:

TurSüreNe ölçülür?Tipik soru
SQL canlı30–45 dkJOIN, GROUP BY, pencere, NULL, tanecik düzeyi"Her departmanın en yüksek maaşlı çalışanı"
pandas / Python canlı30–45 dkmerge, groupby + transform, pivot, zaman penceresi"Her siparişin müşterinin toplam harcamasındaki payı"
İstatistik ve deney30 dkp-değeri, güven aralığı, güç, SRM, çoklu test"B varyantı kazandı mı, yayına alalım mı?"
Ürün / vaka45 dkProblem çerçeveleme, metrik ağacı, öncelik"Sipariş sayısı %10 düştü, nasıl araştırırsın?"

Bu derste her tur için kalıpları Anadolu Market verisinde çalışacak, alıştırmaları zamanlı deneme gibi çözeceksin: her görevin yanında hedef süre var. Süreyi tut; mülakatta doğru cevap kadar ona ulaşma hızın ve konuşma biçimin de puanlanır.

1. SQL turu: 15 kalıp

Soruların çoğu birkaç kalıbın varyasyonudur. Kalıbı tanıdığın an çözüm iskeleti hazırdır; ayrıntılar Python'dan SQL ile analiz dersinde.

#KalıpÇekirdek teknik
1n'inci en yüksek değerDENSE_RANK() OVER (ORDER BY x DESC) = n
2Grup içi en iyi / ilk kROW_NUMBER/RANK OVER (PARTITION BY g ...)
3Tekilleştirme (son kayıt)ROW_NUMBER ... DESC = 1
4Hiç ... olmayanlarNOT EXISTS / LEFT JOIN ... IS NULL
5Oran ve yüzde100.0 * SUM(kosul) / COUNT(*)
6Koşullu toplama ile pivotSUM(CASE WHEN kanal = 'web' THEN 1 ELSE 0 END)
7Kümülatif toplam / YTDSUM() OVER (PARTITION BY yıl ORDER BY ay)
8Dönem değişimiLAG(), filtre CTE dışında
9Hareketli ortalamaROWS BETWEEN 6 PRECEDING AND CURRENT ROW
10Ardışık dönemler (boşluk-ada)değer − ROW_NUMBER() ile grup
11Kohort / tutundurmaİlk tarih CTE'si + dönem farkı
12Kendine birleştirmeÇalışan–yönetici, ardışık olay
13Grup içi payx / SUM(x) OVER (PARTITION BY g)
14Medyan / yüzdelikPERCENTILE_CONT (PostgreSQL) ya da sıra ile
15Yinelenen kayıt bulmaGROUP BY anahtar HAVING COUNT(*) > 1
import veri
con = veri.baglan("anadolu_market", tablolar=["employees", "departments"])

# Kalıp 1 ve 2: ikinci en yüksek maaş ve her departmanın en yükseği
ikinci = """
WITH r AS (SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr FROM employees)
SELECT DISTINCT salary FROM r WHERE dr = 2
"""
print(con.execute(ikinci).fetchall())

lider = """
WITH r AS (
  SELECT dept_id, first_name, salary,
         RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rk
  FROM employees WHERE dept_id IS NOT NULL
)
SELECT dept_id, first_name, salary FROM r WHERE rk = 1 ORDER BY dept_id
"""
for satir in con.execute(lider):
    print(satir)
SQL turunda konuşma sırası(1) Soruyu kendi cümlenle tekrarla ve tanecik düzeyini söyle: "employees çalışan başına bir satır". (2) Kenar durumları sor: eşitlik olursa? NULL departman? İkinci en yüksek yoksa NULL mı dönmeli? (3) İskeleti CTE adımlarıyla yaz. (4) Küçük bir örnekle sonucu sözlü doğrula. Mülakatçı çoğu zaman yarıda bir koşul değiştirir ("şimdi ilk 3'ü getir"); CTE'li kod bunu bir satırla karşılar.
import sqlite3
con = sqlite3.connect(":memory:")
con.executescript("""
CREATE TABLE m (id INT); INSERT INTO m VALUES (1), (2), (3);
CREATE TABLE kara (id INT); INSERT INTO kara VALUES (2), (NULL);
""")
print("NOT IN    :", con.execute("SELECT id FROM m WHERE id NOT IN (SELECT id FROM kara)").fetchall())
print("NOT EXISTS:", con.execute("SELECT id FROM m WHERE NOT EXISTS (SELECT 1 FROM kara k WHERE k.id = m.id)").fetchall())
print("tamsayı bölme:", con.execute("SELECT 1 / 2, 100.0 * 1 / 2").fetchone())
print("COUNT(*) / COUNT(id):", con.execute("SELECT COUNT(*), COUNT(id) FROM kara").fetchone())

En sık tuzak soruları: COUNT(*) ile COUNT(sutun) farkı, LEFT JOIN + WHERE, JOIN sonrası satır çoğalması, NOT IN listesinde NULL olunca hiçbir satırın dönmemesi, WHERE ile HAVING farkı ve tamsayı bölmesi (1 / 2 = 0; 100.0 * ile başla).

2. pandas canlı kodlama

pandas turunda mülakatçı genellikle iki ya da üç tablo verir ve adım adım soru ekler. Dört kalıp soruların büyük kısmını karşılar:

import veri
import pandas as pd

o = veri.oku("anadolu_market", "orders", parse_dates=["order_date"])
i = veri.oku("anadolu_market", "order_items")
c = veri.oku("anadolu_market", "customers")
i["net"] = i["quantity"] * i["unit_price"] * (1 - i["discount"])

# 1) merge: önce sipariş düzeyine indir, sonra birleştir (çoğalmayı önle)
s = (o[o["status"].isin(["delivered", "shipped"])]
     .merge(i.groupby("order_id", as_index=False)["net"].sum(), on="order_id", validate="one_to_one"))

# 2) groupby + transform: grubun özetini satıra geri yaz
s["musteri_toplam"] = s.groupby("customer_id")["net"].transform("sum")
s["pay"] = s["net"] / s["musteri_toplam"]

# 3) pivot: yıl × ay
p = s.assign(yil=s["order_date"].dt.year, ay=s["order_date"].dt.month)
print(p.pivot_table(index="yil", columns="ay", values="net", aggfunc="sum", fill_value=0).round(0).iloc[:, :4])

# 4) zaman penceresi: son 7 günün cirosu
gunluk = s.set_index("order_date").sort_index()["net"].resample("D").sum()
print(gunluk.rolling("7D").sum().nlargest(1).round(2))

# şehir kırılımı: müşteri bilgisi ayrı tabloda, many_to_one doğrula
print(s.merge(c[["customer_id", "city"]], on="customer_id", how="left", validate="many_to_one")
       .groupby("city", dropna=False)["net"].sum().nlargest(3).round(2))
Canlı kodlamada sessiz hatalarMülakatçılar kasıtlı olarak yinelenen anahtar, boş değer ve tarih metni içeren tablolar verir. Her merge'e validate, her gruplamadan önce dropna=False sorusu, her tarih sütununa parse_dates ekle; sonucu len() ile kontrol et. "Merge'ten sonra satır sayısını kontrol ediyorum" demek puan kazandırır.

3. İstatistik ve deney soruları

SoruGüçlü cevabın özüKırmızı bayrak
p-değeri nedir?H₀ doğruysa, gözlenen kadar ya da daha uç bir sonucu görme olasılığı"H₀'ın doğru olma olasılığı"
%95 güven aralığı?Yöntem tekrarlansa aralıkların %95'i gerçek değeri kapsar"Gerçek değer %95 olasılıkla bu aralıkta"
İstatistiksel güç?Gerçek etki varken onu yakalama olasılığı (1 − β); örneklem, etki büyüklüğü ve α'ya bağlıTesti bitirdikten sonra güç hesaplamak
SRM nedir?Örnek oranı uyumsuzluğu: 50/50 planlanan trafikte gruplar beklenenden anlamlı farklı; ki-kare ile denetlenirSRM'yi kontrol etmeden sonucu yorumlamak
Çoklu test?20 metriğe bakınca birinin şans eseri anlamlı çıkması beklenir; Bonferroni / Holm, ya da önceden tek birincil metrik"Anlamlı çıkan metriği raporlarız"
Peeking?Her gün bakıp anlamlı olunca durdurmak yanlış pozitifi şişirir; süreyi önceden belirle ya da sıralı test kullan"Anlamlı olduğu an durdururuz"
import math
from statistics import NormalDist
import veri

ab = veri.oku("anadolu_market", "ab_test")
g = ab.groupby("variant")["converted"].agg(["sum", "count"])
(xa, na), (xb, nb) = g.loc["A"], g.loc["B"]

# 1) önce SRM: 50/50 planlandı mı tuttu?
beklenen = (na + nb) / 2
ki_kare = ((na - beklenen) ** 2 + (nb - beklenen) ** 2) / beklenen
print("SRM p:", round(math.erfc(math.sqrt(ki_kare / 2)), 3))   # 1 serbestlik derecesi

# 2) iki oran z-testi
pa, pb = xa / na, xb / nb
p = (xa + xb) / (na + nb)
z = (pb - pa) / math.sqrt(p * (1 - p) * (1 / na + 1 / nb))
p_deger = 2 * (1 - NormalDist().cdf(abs(z)))
print("A:", round(pa, 4), "B:", round(pb, 4), "z:", round(z, 2), "p:", round(p_deger, 4))

# 3) farkın %95 güven aralığı
se = math.sqrt(pa * (1 - pa) / na + pb * (1 - pb) / nb)
print("fark GA:", round(pb - pa - 1.96 * se, 4), "–", round(pb - pa + 1.96 * se, 4))

Cevabı karar diliyle bitir: "SRM yok (p ≈ 0,26). B'nin dönüşümü 3,8 puan yüksek, p ≈ 0,012, güven aralığı yaklaşık 0,8–6,7 puan; sıfırı kapsamıyor. Koruyucu metrikler (iade, gelir başına kullanıcı) bozulmadıysa yayına alırım; etki alt sınırda küçük olabilir, yayından sonra izlerim." Ayrıntılı teori İstatistik ve hipotez testi dersinde.

4. Ürün ve vaka soruları: "metrik düştü" çerçevesi

  1. Netleştir: Hangi metrik, hangi tanım, hangi dönem, neye göre %10? Ani mi, kademeli mi?
  2. Veri hatasını ele: İzleme kodu değişti mi, pipeline gecikti mi, bir ülke/uygulama sürümü veri göndermiyor mu?
  3. Dış etkenler: Mevsimsellik (geçen yılın aynı haftası), tatil, rakip kampanyası, fiyat değişikliği.
  4. Ayrıştır: KPI ağacı (ziyaretçi × dönüşüm × AOV) ve segmentler (cihaz, kanal, şehir, yeni/eski müşteri). Düşüş tek bir segmentte mi?
  5. Hipotez ve doğrulama: En olası 2–3 neden, her biri için hangi veriyle doğrulanacağı.
  6. Öneri: Etki büyüklüğü ve aciliyete göre sıralı eylem; neyi izleyeceğin.

A/B karar soruları ("hedef metrik arttı ama gelir düştü, ne yaparsın?") için aynı yapı geçerlidir: önce deneyin geçerliliği (SRM, süre, yeni özellik etkisi), sonra metrikler arası ödünleşim, en son karar ve izleme planı.

5. ML ve veri mühendisliği soruları

RolSık soruKısa cevap iskeleti
Bilimci / MLVeri sızıntısı (data leakage) örneği?Hedeften sonra oluşan özellik (iade tarihi ile iade tahmini), tüm veride ölçekleme, zaman serisinde rastgele bölme
Bilimci / ML%1 pozitifli veride metrik?Doğruluk yanıltır; PR-AUC, recall@precision, iş maliyetine göre eşik
Bilimci / MLDengesiz sınıf?Önce doğru metrik ve eşik; sonra class_weight, yeniden örnekleme (yalnız eğitimde)
Mühendisİdempotentlik?Aynı işi iki kez çalıştırmak aynı sonucu verir: bölüm üzerine yazma, MERGE/UPSERT, doğal anahtar
MühendisSCD tip 2?Boyut değişince eski satırı kapat (gecerli_bitis), yeni satır aç; tarihe göre doğru sürüme bağlan
MühendisPartition seçimi?Sorgu filtresine göre (çoğunlukla tarih); çok küçük dosya ve çarpık bölümden kaçın

6. Ev ödevi (take-home) teslimi

Take-home analizde değerlendirici önce README'yi ve tek sayfalık özeti okur, koda sonra bakar. Önerilen yapı:

analiz/
  README.md          # soru, veri, nasıl çalıştırılır, varsayımlar, sınırlılıklar
  ozet.md            # 1 sayfa: 3–5 bulgu, her birinde sayı + grafik + öneri
  notebooks/01_eda.ipynb
  src/metrikler.py   # tekrar kullanılan fonksiyonlar (test edilebilir)
  tests/test_metrikler.py
  requirements.txt

Kendi bilgisayarında dene: python -m venv .venv, pip install -r requirements.txt, ardından notebook'u "Restart & Run All" ile baştan çalıştır; hata vermeden bitmesi, teslimin tekrarlanabilir olduğunun kanıtıdır.

Gerçek işteİşe alım komitelerinde aday çoğunlukla teknik doğrulukla değil iletişimle elenir: sessizce 10 dakika kod yazıp yanlış soruyu çözmek, kenar durumu sormamak, sonucu "p < 0,05 o yüzden B iyi" diye tek cümleyle bitirmek. Güçlü adaylar düşünce sürecini sesli anlatır, varsayımını açıkça söyler ve her sayıyı bir karara bağlar. Bu derste her alıştırmayı çözdükten sonra cevabını iki cümleyle sesli anlatmayı dene.
Uzmanlaş

45 dakikalık deneme planı

Bu dersin alıştırmalarıyla gerçekçi bir deneme kur: 2 SQL (15 dk), 2 pandas (15 dk), 1 istatistik (8 dk), 1 vaka anlatımı (7 dk, sesli). Süre bitince çözemediklerini "nasıl yaklaşırdım" diye yaz; mülakatta kısmi ama doğru yönlü cevap, sessizlikten her zaman iyidir.

Lehçe farkları

Mülakat platformları çoğunlukla PostgreSQL ya da MySQL kullanır. SQLite'ta strftime ile yaptığını PostgreSQL'de date_trunc('month', d) ve EXTRACT ile, medyanı PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x) ile yaparsın. Platform başında hangi lehçe olduğunu sor.

Bootstrap ile sağlam cevap

Gelir gibi çarpık metriklerde z-testi varsayımı zayıflar; mülakatta "bootstrap ile güven aralığı da bakarım" demek ve bunu 6 satırda yazabilmek güçlü bir sinyaldir (Meydan okuma alıştırması).

Alıştırmalara geç (20)