İçeriğe geç
academia.sh

Ders 01 / 09

Veri Analitiği Nedir

Tek bir abone tablosundan 'etkin abone sayısı' üç savunulabilir tanımla hesaplanıyor ve üç ayrı sayı çıkıyor: sözleşme kaydı etkin olan 3 849, son 30 günde okuma gönderen 3 548, sözleşmesi açık olan 4 211. İlk iki tanımın toplamları yalnız 301 fark ediyor ama 645 abone üzerinde anlaşmıyorlar; tek serbest sayı olan pencere 14 günden 90 güne açıldığında sayı 1 859'dan 3 750'ye çıkıyor. Hiçbir hesap değişmedi, yalnız tanım değişti.

İçindekiler

Bir rapordaki sayının nereden geldiği sorulduğunda verilen ilk cevap çoğunlukla hesabı anlatır: hangi satırlar toplandı, hangi bölme yapıldı. Bu müfredat başka bir yere bakar. Bir sayı çoğu zaman hesap yüzünden değil, hesap başlamadan önce alınmış bir tanım kararı yüzünden o değerdedir. Hesap doğru yapılmış olabilir ve sayı yine de başka bir sayı olabilirdi.

Veri analitiği, bir soruyu veriyle cevaplanabilir duruma getirme ve çıkan sayıyı bir karara bağlama işidir. Bu dersin ölçtüğü şey zincirin ilk halkasıdır: bir soru ölçülebilir kılınırken alınan kararlar sayıyı ne kadar oynatıyor. Ölçü birimi farktır — aynı veriden, aynı soruya, iki savunulabilir kararla kaç farklı sayı çıkıyor.

  • AK1. Veri kümesi bir bölgesel ölçüm ağının abone kayıtlarıdır: su sayaçlarından okuma toplayan, okumaları faturaya çeviren ve saha ekibine iş emri açan bir ağ. Ağ da kayıtlar da kurgudur; aşağıdaki bütün sayılar bir modelin çıktısıdır, ölçülmüş bir kurulumun değil.
  • AK2. Tablo hazırdır ve bu kursta veri kümesi olarak kullanılır. Şema tasarımı, normalizasyon ve sorgu yazımı Veritabanı Temelleri kursunda kuruldu; burada tekrarlanmaz.
  • AK3. Ölçüm günü 0’dır. Adı Gun ile biten her alan o günden geriye sayılan gün sayısıdır: sonOkumaGun 4 ise sayaç dört gün önce okuma göndermiştir.
  • AK4. Sayaçların okuma döngüsü modelde aylıktır. Bir sayacın 30 gün boyunca hiç okuma göndermemesi olağan değildir; 14 gün göndermemesi olağandır.
  • AK5. Sözleşme durumu üç değer alır: etkin, askida, kapali. Bu alan kayıt sisteminin durumudur; sayacın davranışıyla arasında zorunlu bir bağ yoktur.
  • AK6. Soru şudur: ağda kaç etkin abone var. Soru bu haliyle bir hesap değil, bir tanım isteğidir.

Ham Kayıtta Ne Var

Soruyu cevaplamadan önce elde ne olduğuna bakılır. Aşağıdaki blok kümeyi kurar ve tek bir çapraz tablo basar: sözleşme durumu ile sayacın son okuma zamanı. İkisi arasındaki uyuşmazlık dersin geri kalanının kaynağıdır.

// kume.mjs — bolgesel olcum aginin abone kayitlari; MODELDIR, kayitlar kurgudur.
// Olcum gunu 0'dir; "gun" ile biten alanlar o gunden geriye sayilan gun sayisidir.
export const TOHUM = 20260601, ABONE = 4800, BOLGE = ["kuzey", "guney", "dogu", "bati", "merkez"];
const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(TOHUM);
const sec = (u, d) => { let t = 0; for (const [a, p] of d) { t += p; if (u < t) return a; }
  return d.at(-1)[0]; };

export const KAYITLAR = Array.from({ length: ABONE }, (_, i) => {
  const bolge = BOLGE[Math.floor(r() * 5)];
  const basamak = sec(r(), [[1, 0.46], [2, 0.34], [3, 0.20]]);
  const baglantiGun = 30 + Math.floor(r() * 2900);
  // Kapanis olasiligi en dusuk tarife basamaginda yuksektir ve kapanislar son aylara yigilir.
  const kapanmaP = [0.16, 0.09, 0.05][basamak - 1];
  const kapanisGun = r() < kapanmaP
    ? Math.min(baglantiGun - 1, Math.floor(r() * r() * 420)) : null;
  const askida = kapanisGun === null && r() < 0.085;
  const sozlesme = kapanisGun !== null ? "kapali" : askida ? "askida" : "etkin";
  // Sayacin son okumasindan bu yana gecen gun. Kapali ve askidaki abonelerin bir bolumu
  // hala okuma gonderir; etkin abonelerin bir bolumu (arizali sayac, bos konut) gondermez.
  const g = sozlesme === "etkin" ? (r() < 0.13 ? 31 + Math.floor(r() * 300) : Math.floor(r() * 29))
    : sozlesme === "askida" ? (r() < 0.34 ? Math.floor(r() * 29) : 31 + Math.floor(r() * 250))
    : (r() < 0.11 ? Math.floor(r() * 29) : 31 + Math.floor(r() * 500));
  const tuketim = Math.round((2 + basamak * 4 + r() * 9) * 10) / 10;
  const ucret = Math.round((6 + basamak * basamak * 7) * (0.85 + r() * 0.3));
  return { i, bolge, basamak, baglantiGun, kapanisGun, sozlesme, sonOkumaGun: g,
    tuketim, ucret, destek: r() < 0.22 ? 1 + Math.floor(r() * 3) : 0 };
});

const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");

if (import.meta.url === `file://${process.argv[1]}`) {
  console.log(`tohum ${TOHUM}: ${ABONE} satir, ${Object.keys(KAYITLAR[0]).length} alan`);
  console.log("alanlar: " + Object.keys(KAYITLAR[0]).join(", ") + "\n");
  const y = gen([12, 9, 11, 11, 11]);
  console.log(y(["sozlesme", "abone", "0-30 gun", "31-90 gun", "90+ gun"]));
  for (const s of ["etkin", "askida", "kapali"]) {
    const l = KAYITLAR.filter((k) => k.sozlesme === s);
    console.log(y([s, l.length, l.filter((k) => k.sonOkumaGun <= 30).length,
      l.filter((k) => k.sonOkumaGun > 30 && k.sonOkumaGun <= 90).length,
      l.filter((k) => k.sonOkumaGun > 90).length]));
  }
  const t = (f) => KAYITLAR.filter(f).length;
  console.log(y(["toplam", ABONE, t((k) => k.sonOkumaGun <= 30),
    t((k) => k.sonOkumaGun > 30 && k.sonOkumaGun <= 90), t((k) => k.sonOkumaGun > 90)]));
}
tohum 20260601: 4800 satir, 10 alan
alanlar: i, bolge, basamak, baglantiGun, kapanisGun, sozlesme, sonOkumaGun, tuketim, ucret, destek

sozlesme        abone   0-30 gun  31-90 gun    90+ gun
etkin            3849       3376         88        385
askida            362        121         55        186
kapali            589         51         59        479
toplam           4800       3548        202       1050

Tablo dört köşesiyle okunur. Sol üst köşe beklenen durumdur: sözleşmesi etkin olan 3 376 abonenin sayacı son 30 gün içinde okuma göndermiştir. Sağ üst köşe beklenmeyendir: sözleşmesi etkin olan 385 abonenin sayacı 90 günden uzun süredir susmaktadır. Sol alt köşe de beklenmeyendir: sözleşmesi kapalı olan 51 abonenin sayacı son 30 gün içinde okuma göndermiştir.

Bu iki köşe bir veri hatası değildir; ikisinin de olağan bir açıklaması vardır. Susan sayaç arızalı olabilir ya da konut boş olabilir. Kapalı sözleşmeden okuma gelmesi, kapanış kaydının işlenmesiyle sayacın sökülmesi arasında geçen süredir. Kayıt sistemi ile fiziksel dünya farklı hızlarda ilerler ve tablo ikisini ayrı sütunlarda tutar.

Soru bu tabloya sorulduğunda cevap tek değildir, çünkü “etkin abone” sözü hangi sütuna bakılacağını söylemez.

Üç Tanım, Üç Sayı

Aşağıdaki blok aynı kümeye üç tanımı ayrı ayrı uygular ve sonuçları yan yana basar. Üçü de savunulabilir tanımlardır: birincisi kayıt sistemine, ikincisi sayacın davranışına, üçüncüsü sözleşmenin hukuki durumuna bakar.

// etkin.mjs — ayni ham kumeden "etkin abone sayisi": uc savunulabilir tanim yan yana.
import { KAYITLAR, ABONE, TOHUM } from "./kume.mjs";

const TANIM = [
  { ad: "A: sozlesme kaydi etkin", f: (k) => k.sozlesme === "etkin" },
  { ad: "B: 30 gunde okuma var", f: (k) => k.sonOkumaGun <= 30 },
  { ad: "C: sozlesme acik", f: (k) => k.sozlesme !== "kapali" },
];
const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");

const S = TANIM.map((t) => new Set(KAYITLAR.filter(t.f).map((k) => k.i)));
const y = gen([26, 8, 10]);
console.log(`tohum ${TOHUM}: ${ABONE} abone kaydi, tek tablo, tek olcum gunu\n`);
console.log(y(["tanim", "abone", "% kayit"]));
TANIM.forEach((t, a) => console.log(y([t.ad, S[a].size,
  ((100 * S[a].size) / ABONE).toFixed(1)])));

const z = gen([10, 9, 11, 11, 13]);
console.log("\n" + z(["cift", "fark", "yalniz ilk", "yalniz son", "anlasmazlik"]));
for (const [a, b] of [[0, 1], [2, 0], [2, 1]]) {
  const i1 = [...S[a]].filter((i) => !S[b].has(i)).length;
  const i2 = [...S[b]].filter((i) => !S[a].has(i)).length;
  console.log(z([`${"ABC"[a]} - ${"ABC"[b]}`, S[a].size - S[b].size, i1, i2, i1 + i2]));
}

// B tanimindaki tek serbest sayi pencere uzunlugudur; okuma dongusu aylik oldugu icin
// 30 gunden kisa pencere sayacin hic gonderemedigi bir sikligi sorar.
const w = gen([9, 9, 12]);
console.log("\n" + w(["pencere", "abone", "A'ya oran"]));
for (const p of [14, 21, 30, 45, 60, 90]) {
  const n = KAYITLAR.filter((k) => k.sonOkumaGun <= p).length;
  console.log(w([`${p} gun`, n, (n / S[0].size).toFixed(2)]));
}
tohum 20260601: 4800 abone kaydi, tek tablo, tek olcum gunu

tanim                        abone   % kayit
A: sozlesme kaydi etkin       3849      80.2
B: 30 gunde okuma var         3548      73.9
C: sozlesme acik              4211      87.7

cift           fark yalniz ilk yalniz son  anlasmazlik
A - B           301        473        172          645
C - A           362        362          0          362
C - B           663        714         51          765

pencere      abone   A'ya oran
14 gun        1859        0.48
21 gun        2733        0.71
30 gun        3548        0.92
45 gun        3602        0.94
60 gun        3654        0.95
90 gun        3750        0.97

Üç tanım üç sayı verdi: 3 849, 3 548 ve 4 211. Aradaki en büyük fark 663 abonedir, kayıtların yüzde 13,8’i. Hiçbir hesap değişmedi — üç tanım da tek bir süzme işlemidir ve hiçbiri diğerinden daha karmaşık değildir. Değişen tek şey hangi sütuna bakıldığıdır.

İkinci tablo daha önemli bir şey yazıyor. A ile B’nin toplamları yalnız 301 fark ediyor, ama iki tanımın anlaşmadığı abone sayısı 645. 473 abone yalnız A’da, 172 abone yalnız B’de. Toplam fark küçük görünüyor çünkü iki yönlü anlaşmazlığın büyük bölümü birbirini götürüyor. Toplamların yakın olması, iki tanımın aynı kişileri saydığı anlamına gelmez. Bir raporda “iki yöntem de yaklaşık aynı sonucu veriyor” cümlesi yazılacaksa, ölçülmesi gereken şey toplamların farkı değil, kayıt düzeyindeki anlaşmazlıktır.

Pencere de Bir Karardır

B tanımı bir sayı içeriyor: 30 gün. Bu sayı veriden gelmedi, seçildi. Üçüncü tablo seçimin bedelini yazıyor: pencere 14 güne indirildiğinde etkin abone 1 859’a düşüyor, 90 güne açıldığında 3 750’ye çıkıyor. Aynı tablo, aynı soru, aynı hesap; sonuç iki katından fazla oynuyor.

Pencerenin her değeri savunulabilir değildir. AK4 okuma döngüsünün aylık olduğunu söylüyor; 14 günlük pencere, sayacın göndermesi beklenmeyen bir sıklığı ölçüt yapar ve etkin abonelerin yarısını susmuş sayar. 90 günlük pencere ise ters yöne düşer: üç ay boyunca hiç okuma göndermemiş bir sayacı etkin sayar. 30 ile 60 arasındaki değerler sonucu yalnız 106 abone oynatıyor — yani bu aralıkta tanım sağlamdır. Bir eşiğin savunulabilirliği, sonucun o eşik etrafında ne kadar oynadığına bakılarak ölçülür. Eşiği 14’ten 21’e taşımak sayıyı 874 abone oynatıyor; 45’ten 60’a taşımak 52 abone. İlk bölge kırılgan, ikinci bölge kararlı.

Sıralama da Oynar

Bir raporda tek bir toplam nadiren durur. Toplamın yanında bir kırılım olur ve birileri o kırılımın en üstündeki satıra göre iş yapar: önce hangi bölgeye gidilecek, bütçe önce nereye verilecek. Tanım kararı toplamı oynatıyorsa sıralamayı da oynatır.

// bolge.mjs — tanim karari yalniz toplami degil siralamayi da oynatir: bes bolge, iki tanim.
import { KAYITLAR, BOLGE } from "./kume.mjs";

const oran = (l, f) => l.filter(f).length / l.length;
const sat = BOLGE.map((b) => {
  const l = KAYITLAR.filter((k) => k.bolge === b);
  return { b, n: l.length, a: oran(l, (k) => k.sozlesme === "etkin"),
    c: oran(l, (k) => k.sonOkumaGun <= 30) };
});
// Sira 1 = en dusuk oran, yani raporda "once bakilacak" bolge.
const sira = (alan) => new Map([...sat].sort((x, y) => x[alan] - y[alan])
  .map((s, i) => [s.b, i + 1]));
const sA = sira("a"), sB = sira("c");

const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");
const y = gen([9, 8, 10, 8, 10, 8, 8]);
console.log(y(["bolge", "abone", "A orani", "A sira", "B orani", "B sira", "kayma"]));
for (const s of sat) {
  console.log(y([s.b, s.n, s.a.toFixed(4), sA.get(s.b), s.c.toFixed(4), sB.get(s.b),
    sB.get(s.b) - sA.get(s.b)]));
}
const ilk = (alan) => [...sat].sort((x, y2) => x[alan] - y2[alan]);
const yay = (alan) => Math.max(...sat.map((s) => s[alan])) - Math.min(...sat.map((s) => s[alan]));
console.log(`\nA tanimiyla ilk bakilacak bolge ${ilk("a")[0].b}, B tanimiyla ${ilk("c")[0].b}`);
console.log(`B'de ilk iki bolgeyi ayiran fark ${(ilk("c")[1].c - ilk("c")[0].c).toFixed(4)}, ` +
  `bes bolgenin tum yayilimi ${yay("c").toFixed(4)}`);
bolge       abone   A orani  A sira   B orani  B sira   kayma
kuzey         929    0.7976       3    0.7277       3       0
guney         913    0.7985       4    0.7240       1      -3
dogu          999    0.8248       5    0.7648       5       0
bati          969    0.7905       1    0.7245       2       1
merkez        990    0.7970       2    0.7525       4       2

A tanimiyla ilk bakilacak bolge bati, B tanimiyla guney
B'de ilk iki bolgeyi ayiran fark 0.0005, bes bolgenin tum yayilimi 0.0408

Listenin başı değişti: A tanımıyla ilk bakılacak bölge batı, B tanımıyla güney. Merkez iki sıra yukarı kaydı, güney üç sıra aşağı. Doğu her iki tanımda da en iyi bölge kaldı — kayma her satırda olmuyor, ama karar verilen satırda oluyor.

Nedeni son satırda yazılı. Beş bölgenin oranı B tanımında yalnız 0,0408 aralığa yayılıyor. İki tanım arasındaki fark ise ağ genelinde 6,3 puandır — ikinci tablodaki yüzde 80,2 ile yüzde 73,9 arasındaki fark. Gruplar arası fark, tanım değişikliğinin ürettiği kaymadan küçükse sıralama bölgelerin değil tanımın bir özelliğidir. B tanımında ilk iki bölgeyi ayıran fark 0,0005; güneyde tek bir sayacın okuma göndermesi (661 yerine 662) sıralamayı ters çevirir. Saha ekibini bu listenin başına göre yönlendirmek, bir yuvarlama farkına göre yönlendirmektir.

Kararı Yazılmayan Sayı

Bu dersin ürettiği sayı 3 548’dir: son 30 gün içinde okuma göndermiş abone sayısı. Yanında üç karar durur ve üçü de yazılmadan sayı raporlanamaz:

  • Hangi sütun ölçüt. Sayacın davranışı ölçüt alındı, sözleşme kaydı değil. Gerekçe: soru “kaç aboneye fatura kesilebilir” sorusuna hizmet ediyorsa okuma olmadan fatura da olmaz.
  • Pencere 30 gün. Gerekçe: okuma döngüsü aylıktır ve sonuç 30 ile 60 arasında 106 abone oynuyor, yani seçim bu aralıkta sonucu belirlemiyor.
  • Sözleşme durumu süzülmedi. Sözleşmesi kapalı 51 abone, okuma gönderdiği için sayının içinde kaldı. Gerekçe: fatura kesilebilirlik sorusunda bunlar da sayılır; kapanışın işlenmemiş olması ayrı bir sorundur.

Bu üç satır yazılmadığında geriye 3 548 sayısı kalır ve o sayı bir bulgu değil, bir seçimdir. Kararı yazılmayan sayı ölçülmemiş sayılır. Bu kural müfredat boyunca sürecek ve her derste aynı biçimde uygulanacak: rapor edilen sayı, onu üretirken alınan kararlar, kararı değiştirince ortaya çıkan fark.

Buradan çıkan ikinci sonuç daha rahatsız edicidir. “Veri konuşur” diye bir şey yoktur. Bu tabloda üç doğru sayı vardır ve hiçbiri diğerini yanlışlamaz; hangisinin raporlanacağını belirleyen şey veri değil, sorunun ne için sorulduğudur. Aynı tablodan bir sayaç değiştirme listesi çıkarılacaksa A ile B’nin anlaşmadığı 645 abone hedeftir; bir fatura kesimi yapılacaksa B’dir; bir sözleşme sayımı raporlanacaksa C. Analitiğin işi bu üç sayıdan birini seçmek değil, hangisinin hangi soruya karşılık geldiğini yazmaktır.

Özet

  • Veri analitiği bir soruyu ölçülebilir duruma getirme işidir; sayı çoğu zaman hesap sırasında değil, tanım sırasında belirlenir.
  • Tek bir abone tablosundan “etkin abone” üç savunulabilir tanımla üç ayrı sayı verir: 3 849 (sözleşme kaydı etkin), 3 548 (son 30 günde okuma var), 4 211 (sözleşme açık).
  • Toplamların yakınlığı anlaşmayı göstermez: A ile B’nin toplam farkı 301, ama kayıt düzeyinde anlaşmadıkları abone sayısı 645.
  • Tanımın içindeki eşik de bir karardır; pencere 14 günden 90 güne açıldığında sayı 1 859’dan 3 750’ye çıkar. Eşiğin savunulabilirliği sonucun o eşik etrafındaki oynaklığıyla ölçülür.
  • Tanım kararı sıralamayı da oynatır: ilk bakılacak bölge A tanımıyla batı, B tanımıyla güneydir ve B’de ilk iki bölgeyi tek bir sayacın okuması ayırır.
  • Bir sayının yanında hangi kararla üretildiği yazılmıyorsa o sayı bir bulgu değil bir seçimdir; kararı yazılmayan sayı ölçülmemiş sayılır.

Sonraki Adım

Bu ders tek bir soruyu ölçülebilir duruma getirdi: kaç etkin abone var. Sorunun kendisi sorgulanmadı. Oysa aynı tabloya sorulabilecek sorular tek bir çeşit değildir. “Kaç abone var” sorusu geçmişi anlatır; “abone sayısı neden düştü” sorusu bir neden ister; “gelecek çeyrekte kaç abone olacak” sorusu bir tahmin ister; “hangi aboneye önce gidilmeli” sorusu bir eylem önerir. Dördü de aynı tabloya sorulabilir, ama dördü aynı veriyle cevaplanamaz. Sonraki ders bu dört soru çeşidini ayırır ve bir soru kümesinde kaçının eldeki kümeyle cevaplanabildiğini sayar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat