Ders 05 / 09
Veri Türleri
1.400 abone kaydının sekiz alanı sayı olarak depolanır ve sekizinin de ortalaması hesaplanır, ama üçü nitel alandır: bölge kodunun ortalaması alfabetik kodlamada 3,19, büyüklük kodlamasında 2,69 çıkar ve veride tek bir kayıt değişmemiştir. Kesikli ziyaret alanının 1,20 ortalamasına eşit tek bir kayıt yoktur ve kayıtların yüzde 68,3'ü bu ortalamanın altındadır; sürekli tüketim alanı 5 m³ basamağına yuvarlandığında toplam yalnız binde 1,4 kayar ama 229 kayıt tarife basamağı değiştirir.
İçindekiler
Bir önceki konu bir soruyu ölçülebilir hale getirmenin asıl karar olduğunu gösterdi: “etkin abone” tanımı değişince hiçbir hesap değişmeden sayı oynadı. Ama o dört ders boyunca bir şey hiç sorulmadı — tanımın üzerinde çalıştığı verinin kendisi ne tür bir şey. Bir alanın ortalaması alınabilir mi, alınırsa ne anlama gelir, iki değeri arasındaki fark bir büyüklük müdür yoksa yalnız bir etiket farkı mı.
Bu soru teknik bir ayrıntı gibi görünür ve öyle değildir. Bir veri kümesinde her alan bir sayı olarak durabilir; hesap makinesi hepsinin ortalamasını verir ve hiçbirinde hata mesajı üretmez. Türü yanlış atanmış bir alan, kendini bir hatayla değil, anlamı olmayan bir sayıyla gösterir. Bu ders o sayıyı üç ayrı biçimde ölçer.
- VY1. Veri kümesi bölgesel ölçüm ağının abone kayıtlarıdır: 1.400 satır, sekiz alan, beş bölge. Küme de sayılar da kurgudur; aşağıdaki her değer bir modelin çıktısıdır. Aynı küme bu konunun bütün derslerinde kullanılır.
- VY2. Nicel veri (quantitative data), değerleri bir büyüklük bildiren veridir: iki değer arasındaki fark bir miktardır. Nitel veri (qualitative data) bir kategori bildirir; değerler ayrıdır ama aralarındaki fark bir miktar değildir.
- VY3. Nicel bir alan kesikli (discrete) ise yalnız sayılabilir değerler alır ve ara değerlerin karşılığı yoktur; sürekli (continuous) ise iki değer arasında her zaman bir başkası vardır ve ölçüm bir hassasiyetle kesilir.
- VY4. Bir alanın depolama türü ile veri türü aynı şey değildir. Tam sayı olarak depolanan bir alan nitel olabilir; ondalıklı depolanan bir alanın gerçekte kesikli olması da mümkündür.
- VY5. Tarife basamağı eşikleri 10, 25 ve 40 m³’tür. Bu eşikler veridir; bu sayfada ölçülmüş değil, modele girdi olarak verilmiştir.
Depolama Türü ile Veri Türü Aynı Şey Değil
// kume.mjs — bolgesel olcum agi kaydi: her satir bir abone, alanlari sayac // okumasi, saha ziyareti ve memnuniyet notu. MODELDIR, veri kumesi kurgudur. export const TOHUM = 20260218, KAYIT = 1400; const karistir = (x) => { x = Math.imul(x ^ (x >>> 16), 2246822507); x = Math.imul(x ^ (x >>> 13), 3266489909); return (x ^ (x >>> 16)) >>> 0; }; const uretec = (t) => { let s = karistir(t); return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; const ayrik = (u, w) => { let t = 0; for (let i = 0; i < w.length; i++) { t += w[i]; if (u < t) return i; } return w.length - 1; }; export const BOLGE = [ { ad: "kuzey", pay: 0.28, taban: 21, mem: [0.05, 0.10, 0.25, 0.40, 0.20] }, { ad: "guney", pay: 0.22, taban: 17, mem: [0.02, 0.08, 0.30, 0.45, 0.15] }, { ad: "dogu", pay: 0.18, taban: 26, mem: [0.20, 0.08, 0.05, 0.12, 0.55] }, { ad: "bati", pay: 0.14, taban: 14, mem: [0.10, 0.15, 0.25, 0.30, 0.20] }, { ad: "merkez", pay: 0.18, taban: 23, mem: [0.06, 0.12, 0.28, 0.34, 0.20] }, ]; export const KAYITLAR = Array.from({ length: KAYIT }, (_, i) => { const r = uretec(TOHUM + i); const b = BOLGE[ayrik(r(), BOLGE.map((x) => x.pay))]; const tuketim = Math.max(1.2, b.taban * Math.exp((r() + r() + r() - 1.5) * 0.62)); return { abone_no: 10001 + i, bolge: b.ad, tuketim_m3: Math.round(tuketim * 100) / 100, sayac_sayisi: ayrik(r(), [0.62, 0.24, 0.10, 0.04]) + 1, ziyaret_sayisi: ayrik(r(), [0.38, 0.32, 0.16, 0.08, 0.04, 0.02]), memnuniyet: ayrik(r(), b.mem) + 1, sicaklik_c: Math.round((6 + r() * 22) * 10) / 10, gecen_gun: Math.floor(Math.pow(r(), 1.9) * 210) }; }); // Alfabetik kodlama: bolge adlari siralanip 1'den numaralandirilir. export const KOD_A = Object.fromEntries(BOLGE.map((b) => b.ad).sort() .map((ad, j) => [ad, j + 1])); export const ort = (a) => a.reduce((x, y) => x + y, 0) / a.length; const farkli = (f) => new Set(KAYITLAR.map(f)).size; export const ALANLAR = [ { ad: "abone_no", depo: "tam sayi", tur: "nitel", olcum: "-", f: (k) => k.abone_no }, { ad: "bolge_kodu", depo: "tam sayi", tur: "nitel", olcum: "-", f: (k) => KOD_A[k.bolge] }, { ad: "memnuniyet", depo: "tam sayi", tur: "nitel", olcum: "sirali", f: (k) => k.memnuniyet }, { ad: "sayac_sayisi", depo: "tam sayi", tur: "nicel", olcum: "kesikli", f: (k) => k.sayac_sayisi }, { ad: "ziyaret_sayisi", depo: "tam sayi", tur: "nicel", olcum: "kesikli", f: (k) => k.ziyaret_sayisi }, { ad: "gecen_gun", depo: "tam sayi", tur: "nicel", olcum: "kesikli", f: (k) => k.gecen_gun }, { ad: "tuketim_m3", depo: "ondalik", tur: "nicel", olcum: "surekli", f: (k) => k.tuketim_m3 }, { ad: "sicaklik_c", depo: "ondalik", tur: "nicel", olcum: "surekli", f: (k) => k.sicaklik_c }, ]; 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(`model (tohum ${TOHUM}): ${KAYIT} abone kaydi, ${BOLGE.length} bolge, ` + `${ALANLAR.length} alan; sekiz alanin tamami sayi olarak depolanmistir`); const y = gen([16, 11, 8, 10, 9, 12]); console.log("\n" + y(["alan", "depolanan", "tur", "olcum", "farkli", "ortalama"])); for (const a of ALANLAR) { console.log(y([a.ad, a.depo, a.tur, a.olcum, farkli(a.f), ort(KAYITLAR.map(a.f)).toFixed(2)])); } const anlamli = ALANLAR.filter((a) => a.tur === "nicel").length; console.log(`\nsekiz alanin sekizi icin ortalama hesaplandi; ` + `${anlamli} tanesi nicel, ${ALANLAR.length - anlamli} tanesi degil`); }
model (tohum 20260218): 1400 abone kaydi, 5 bolge, 8 alan; sekiz alanin tamami sayi olarak depolanmistir alan depolanan tur olcum farkli ortalama abone_no tam sayi nitel - 1400 10700.50 bolge_kodu tam sayi nitel - 5 3.19 memnuniyet tam sayi nitel sirali 5 3.56 sayac_sayisi tam sayi nicel kesikli 4 1.55 ziyaret_sayisi tam sayi nicel kesikli 6 1.20 gecen_gun tam sayi nicel kesikli 209 69.39 tuketim_m3 ondalik nicel surekli 1099 21.31 sicaklik_c ondalik nicel surekli 221 16.98 sekiz alanin sekizi icin ortalama hesaplandi; 5 tanesi nicel, 3 tanesi degil
Son sütun bu dersin asıl bulgusudur: sekiz alanın sekizi için de bir ortalama çıktı. abone_no
alanının ortalaması 10 700,50’dir ve bu sayı doğrudur — 1.400 abone numarasının aritmetik
ortalaması gerçekten budur. Aynı sayının hiçbir karşılığı yoktur: ortalama abone numarası diye
bir kavram yoktur, çünkü abone numaraları arasındaki fark bir miktar değil bir ayrımdır. Sayı
hesaplandı, doğru hesaplandı ve ölçülen bir şey yok.
farkli sütunu türü tahmin etmeye yarayan bir işarettir ama karar vermez. bolge_kodu alanı beş
ayrı değer alır ve nitel olduğu buradan sezilebilir. sayac_sayisi de yalnız dört değer alır,
ama nicel ve kesikliktir: iki sayaçlı bir hane bir sayaçlıdan gerçekten bir sayaç fazladır.
Ayrımı yapan şey değer sayısı değil, değerler arasındaki farkın bir miktar bildirip
bildirmediğidir. tuketim_m3 alanı 1.099 farklı değer alır; bu, alanın sürekli olduğunu değil,
ölçümün iki ondalık basamakta kesildiğini gösterir. Sürekli bir büyüklük depolandığı anda hep
kesikli görünür; süreklilik depolanan değerin değil, ölçülen şeyin özelliğidir.
memnuniyet alanı üçüncü bir durumdadır. Beş değeri sıralıdır — 4 notu 3 notundan iyidir — ama
4 ile 3 arasındaki uzaklığın 2 ile 1 arasındakine eşit olduğunu söyleyen bir ölçüm yoktur. Bu ders
onu nitel sayar ve sıralı olduğunu not eder; sıralamanın hangi özetlere izin verdiği sonraki
dersin konusudur.
Türü Yanlış Atanan Alanın Ürettiği Sayı
Tür ataması bir etiketleme işi değildir; rapora giren sayıyı doğrudan oynatır. Aşağıdaki blok üç yanlış atamayı sırayla ölçer.
// tur-hatasi.mjs — ayni alan iki turle okundugunda cikan iki sayi yan yana. import { KAYITLAR, BOLGE, KOD_A, ort } from "./kume.mjs"; // Kodlama B: bolgeler kayit sayisina gore buyukten kucuge numaralanir. const sayim = Object.fromEntries(BOLGE.map((b) => [b.ad, KAYITLAR.filter((k) => k.bolge === b.ad).length])); const KOD_B = Object.fromEntries(Object.keys(sayim) .sort((x, y) => sayim[y] - sayim[x]).map((ad, j) => [ad, j + 1])); const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); console.log("bolge_kodu nitel bir alandir; iki kodlama da savunulabilir"); const y1 = gen([10, 9, 9, 9]); console.log(y1(["bolge", "kayit", "kod A", "kod B"])); for (const ad of Object.keys(sayim)) console.log(y1([ad, sayim[ad], KOD_A[ad], KOD_B[ad]])); const oA = ort(KAYITLAR.map((k) => KOD_A[k.bolge])); const oB = ort(KAYITLAR.map((k) => KOD_B[k.bolge])); console.log(`kod A ortalamasi ${oA.toFixed(2)}, kod B ortalamasi ${oB.toFixed(2)}, ` + `fark ${(oB - oA).toFixed(2)} — veride tek bir kayit degismedi`); // Kesikli alan: ortalama ile dagilim yan yana. const z = KAYITLAR.map((k) => k.ziyaret_sayisi), oz = ort(z); const DEGER = [0, 1, 2, 3, 4, 5]; const say = (v) => z.filter((x) => x === v).length; // Ondalikli esitlik dogrudan sinanmaz; 0.005 esigi bir ziyaretin yuzde yarimidir // ve bu kumede iki farkli tam sayi degerini asla ayni sayamaz. const EPS = 0.005, esit = z.filter((v) => Math.abs(v - oz) < EPS).length; const alti = z.filter((v) => v < oz).length; console.log(`\nziyaret_sayisi: ortalama ${oz.toFixed(2)}, ortanca ` + `${[...z].sort((a, b) => a - b)[z.length / 2]}, tepe deger ` + `${DEGER.reduce((a, v) => (say(v) > say(a) ? v : a), 0)}`); const y2 = gen([10, 9, 10, 12]); console.log(y2(["ziyaret", "kayit", "yuzde", "kumulatif"])); let ku = 0; for (const v of DEGER) { ku += say(v); console.log(y2([v, say(v), (100 * say(v) / z.length).toFixed(1), ku])); } console.log(`ortalamaya esit kayit ${esit}; ortalamanin altinda ${alti} kayit, ` + `yuzde ${(100 * alti / z.length).toFixed(1)}`); // Surekli alan kesikli okundugunda: 5 m3 basamagina yuvarlama. const ESIK = [10, 25, 40]; const basamak = (v) => (v < ESIK[0] ? 1 : v < ESIK[1] ? 2 : v < ESIK[2] ? 3 : 4); const ham = KAYITLAR.map((k) => k.tuketim_m3), yuv = ham.map((v) => Math.round(v / 5) * 5); const kayan = ham.filter((v, i) => basamak(v) !== basamak(yuv[i])).length; const top = (a) => a.reduce((x, y) => x + y, 0); console.log(`\ntuketim_m3 sureklidir; 5 m3 basamagina yuvarlanirsa toplam ` + `${top(ham).toFixed(1)} m3 yerine ${top(yuv).toFixed(1)} m3 olur, fark ` + `${(top(yuv) - top(ham)).toFixed(1)} m3 (binde ` + `${(1000 * (top(yuv) - top(ham)) / top(ham)).toFixed(1)})`); console.log(`ortalama ${ort(ham).toFixed(2)} yerine ${ort(yuv).toFixed(2)}; ` + `${kayan} kayit tarife basamagi degistirir`); const y3 = gen([12, 9, 14, 9]); console.log(y3(["basamak", "ham", "yuvarlanmis", "fark"])); for (const b of [1, 2, 3, 4]) { const h = ham.filter((v) => basamak(v) === b).length; const u = yuv.filter((v) => basamak(v) === b).length; console.log(y3([`${b} (${b === 4 ? "40+" : `<${ESIK[b - 1]}`} m3)`, h, u, u - h])); }
bolge_kodu nitel bir alandir; iki kodlama da savunulabilir bolge kayit kod A kod B kuzey 408 4 1 guney 282 3 2 dogu 240 2 4 bati 209 1 5 merkez 261 5 3 kod A ortalamasi 3.19, kod B ortalamasi 2.69, fark -0.51 — veride tek bir kayit degismedi ziyaret_sayisi: ortalama 1.20, ortanca 1, tepe deger 0 ziyaret kayit yuzde kumulatif 0 514 36.7 514 1 442 31.6 956 2 237 16.9 1193 3 106 7.6 1299 4 62 4.4 1361 5 39 2.8 1400 ortalamaya esit kayit 0; ortalamanin altinda 956 kayit, yuzde 68.3 tuketim_m3 sureklidir; 5 m3 basamagina yuvarlanirsa toplam 29828.2 m3 yerine 29870.0 m3 olur, fark 41.8 m3 (binde 1.4) ortalama 21.31 yerine 21.34; 229 kayit tarife basamagi degistirir basamak ham yuvarlanmis fark 1 (<10 m3) 48 3 -45 2 (<25 m3) 970 853 -117 3 (<40 m3) 348 488 140 4 (40+ m3) 34 56 22
İlk blok nitel alana uygulanan ortalamayı ölçüyor. Bölge kodu iki biçimde verilebilir ve ikisi de savunulabilir: adları alfabetik sıralayıp numaralamak ya da bölgeleri kayıt sayısına göre büyükten küçüğe numaralamak. Birinci kodlamada ortalama bölge kodu 3,19, ikincisinde 2,69 çıkar. Veride hiçbir kayıt değişmemiştir; değişen tek şey etiketlere iliştirilen sayılardır. Bu, nitel bir alanın ortalamasının veriye değil kodlamaya ait olduğunu gösteren en kısa kanıttır. Bir raporda “ortalama bölge kodu 3,19” satırı varsa o satır bir bulgu değil, bir sıralama kararının gölgesidir.
İkinci blok kesikli bir alanı sürekli sayınca ne olduğunu gösteriyor. Ziyaret sayısının ortalaması 1,20’dir; hesap doğrudur. Ama 1.400 kaydın hiçbiri 1,20 ziyaret almamıştır, çünkü yarım ziyaret diye bir şey yoktur. Ortalamaya eşit kayıt sayısı sıfırdır. Dahası kayıtların yüzde 68,3’ü bu ortalamanın altındadır: en kalabalık grup 514 kayıtla hiç ziyaret almamış abonelerdir. “Abone başına 1,20 saha ziyareti” cümlesi tek bir aboneyi bile tarif etmez; “abonelerin yüzde 36,7’si hiç ziyaret almamıştır” cümlesi aynı veriden çıkar ve bambaşka bir şey söyler. İkisi de doğrudur ve ikisi zıt karar önerir — biri ziyaret yükünün dengeli olduğunu, diğeri bir kesimin hiç görülmediğini gösterir. Kesikli bir alan için rapora giren sayı ortalama değil dağılımdır; ortalama yalnız o dağılımın yanında bir özettir.
Üçüncü blok sürekli bir alanın kesikli okunmasını ölçüyor ve şaşırtıcı sonucu buradadır. Tüketim 5 m³ basamağına yuvarlandığında toplam 29 828,2 m³’ten 29 870,0 m³’e çıkar: fark binde 1,4. Toplam neredeyse hiç kımıldamaz, çünkü yukarı ve aşağı yuvarlamalar birbirini götürür. Aynı işlem 229 kaydın tarife basamağını değiştirir. Birinci basamaktaki 48 kayıt 3’e iner, üçüncü basamak 348’den 488’e çıkar. Yuvarlama toplamı korur, sınıflandırmayı bozar. Toplam tüketim üzerinden rapor veren biri yuvarlamanın zararsız olduğunu söyleyebilir ve haklıdır; tarife basamağı dağılımı üzerinden rapor veren biri aynı işlemin kayıtların altıda birini başka basamağa taşıdığını söyler ve o da haklıdır. Hangi kararın zararsız olduğu, sorulan soruya bağlıdır.
Üç ölçüm de aynı kuralı yazıyor: bir sayının yanında hangi tür atamasıyla üretildiği yazılmıyorsa o sayı okunamaz. Bölge kodu ortalaması hangi kodlamada, ziyaret ortalaması hangi dağılımın yanında, tüketim ortalaması yuvarlanmış mı — bu üç bilgi olmadan üç sayı da savunulamaz.
Özet
- Depolama türü veri türünü belirlemez: sekiz alanın sekizi de sayı olarak durur, üçü niteldir ve ortalaması hesaplanabilse de bir büyüklük bildirmez.
- Nicel ile niteli ayıran şey değer sayısı değil, iki değer arasındaki farkın bir miktar bildirip
bildirmediğidir;
sayac_sayisidört değerle nicel,bolge_kodubeş değerle niteldir. - Nitel bir alanın ortalaması kodlamaya aittir: aynı 1.400 kayıt alfabetik kodlamada 3,19, büyüklük kodlamasında 2,69 verir ve veride hiçbir şey değişmemiştir.
- Kesikli bir alan sürekli sayılınca ortalama karşılığı olmayan bir değere iner: 1,20 ziyaret ortalamasına eşit kayıt yoktur ve kayıtların yüzde 68,3’ü bu değerin altındadır.
- Sürekli bir alanın yuvarlanması toplamı binde 1,4 oynatır ama 229 kaydın tarife basamağını değiştirir; aynı kararın zararsız olup olmadığı sorulan soruya göre değişir.
Sonraki Adım
Bu ders bir alanın ortalamasının alınıp alınamayacağını sordu ve memnuniyet alanında yarım bir
cevapla kaldı: değerleri sıralıdır, ama 4 ile 3 arasındaki uzaklığın 2 ile 1 arasındakine eşit
olduğunu kimse ölçmemiştir. Aynı belirsizlik sicaklik_c alanında da vardır — iki sıcaklık
toplanabilir mi, biri diğerinin iki katı sayılabilir mi. Nicel ve nitel ayrımı bu soruları
cevaplamaya yetmiyor, çünkü ayrım fazla kaba. Sonraki ders alanları dört ölçüm ölçeğine
yerleştirir ve her ölçekte hangi özetin meşru olduğunu sayar: sıralayıcı bir alana verilen iki
savunulabilir sayısal kodlama, bölgelerin memnuniyet sıralamasını baştan sona ters çevirir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.