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:
| Tur | Süre | Ne ölçülür? | Tipik soru |
|---|---|---|---|
| SQL canlı | 30–45 dk | JOIN, GROUP BY, pencere, NULL, tanecik düzeyi | "Her departmanın en yüksek maaşlı çalışanı" |
| pandas / Python canlı | 30–45 dk | merge, groupby + transform, pivot, zaman penceresi | "Her siparişin müşterinin toplam harcamasındaki payı" |
| İstatistik ve deney | 30 dk | p-değeri, güven aralığı, güç, SRM, çoklu test | "B varyantı kazandı mı, yayına alalım mı?" |
| Ürün / vaka | 45 dk | Problem ç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 |
|---|---|---|
| 1 | n'inci en yüksek değer | DENSE_RANK() OVER (ORDER BY x DESC) = n |
| 2 | Grup içi en iyi / ilk k | ROW_NUMBER/RANK OVER (PARTITION BY g ...) |
| 3 | Tekilleştirme (son kayıt) | ROW_NUMBER ... DESC = 1 |
| 4 | Hiç ... olmayanlar | NOT EXISTS / LEFT JOIN ... IS NULL |
| 5 | Oran ve yüzde | 100.0 * SUM(kosul) / COUNT(*) |
| 6 | Koşullu toplama ile pivot | SUM(CASE WHEN kanal = 'web' THEN 1 ELSE 0 END) |
| 7 | Kümülatif toplam / YTD | SUM() OVER (PARTITION BY yıl ORDER BY ay) |
| 8 | Dönem değişimi | LAG(), filtre CTE dışında |
| 9 | Hareketli ortalama | ROWS BETWEEN 6 PRECEDING AND CURRENT ROW |
| 10 | Ardışık dönemler (boşluk-ada) | değer − ROW_NUMBER() ile grup |
| 11 | Kohort / tutundurma | İlk tarih CTE'si + dönem farkı |
| 12 | Kendine birleştirme | Çalışan–yönetici, ardışık olay |
| 13 | Grup içi pay | x / SUM(x) OVER (PARTITION BY g) |
| 14 | Medyan / yüzdelik | PERCENTILE_CONT (PostgreSQL) ya da sıra ile |
| 15 | Yinelenen kayıt bulma | GROUP 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)
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))
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ı
| Soru | Güç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 denetlenir | SRM'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
- Netleştir: Hangi metrik, hangi tanım, hangi dönem, neye göre %10? Ani mi, kademeli mi?
- Veri hatasını ele: İzleme kodu değişti mi, pipeline gecikti mi, bir ülke/uygulama sürümü veri göndermiyor mu?
- Dış etkenler: Mevsimsellik (geçen yılın aynı haftası), tatil, rakip kampanyası, fiyat değişikliği.
- 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?
- Hipotez ve doğrulama: En olası 2–3 neden, her biri için hangi veriyle doğrulanacağı.
- Ö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ı
| Rol | Sık soru | Kısa cevap iskeleti |
|---|---|---|
| Bilimci / ML | Veri 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 / ML | Dengesiz 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ühendis | SCD 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ühendis | Partition 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.
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ı).