Ders 07 / 17
Yinelenen Kayıtlar
Devralınan 2.068 satıra üç tekilleştirme ölçütü uygulanır ve üç sayı çıkar: tam satır karşılaştırması hiçbir satırı birleştirmez, ham sayaç kodu 68 satırı birleştirir, kırpılmış ve harf birliğine getirilmiş kod 188 satırı birleştirir — aradaki 120 satır tek bir metin kararının bedelidir. İkinci girişi olan 188 kaydın hepsinde adres, yüzde 92'sinde tarife sınıfı, yüzde 63,8'inde sayaç kodu farklı yazılmıştır ve hiçbirinde okuma değeri değişmemiştir. Tekilleştirmeden ayrı bir iş olan kimlik eşleştirmede 240 destek talebinin 149'u ham metinle, 174'ü sadeleştirilmiş kodla bağlanır ve altısı yanlış aboneye gider; 0,80 eşiğiyle kurulan 18 ek bağın 17'si yanlıştır. Adım 2.068 satırla girip 1.880 satırla çıkar.
İçindekiler
Bir önceki ders kümeyi 2.068 satıra indirdi ve kalan satırların her birinin ayrı bir ziyareti temsil ettiğini varsaydı. Varsayım tutmuyor: bir bölüm ziyaret forma iki kez geçirilmiştir.
Bu ders iki ayrı işi ölçer. Tekilleştirme aynı kümenin içinde çalışır ve aynı kaydın iki kopyasını tek satıra indirir. Kimlik eşleştirme iki kaynak arasında çalışır ve bir kaydı diğerindeki varlığa bağlar; bir şey silmez, bir bağ kurar. Yanlış cevabın bedeli farklıdır: birinde satır kaybolur, diğerinde satır yanlış yere yazılır.
- VZ5. Küme bir önceki dersten devralınır: kimlik alanı boş 18 satır elenmiş, eksik okumalı 204
satır işaretlenip doldurulmuştur. Aynı tohum, aynı satırlar; küme yine
nodeile modellenir ve kurgudur. - VZ6. Yinelenme aynı ziyaretin ikinci kez girilmesidir; bir abonenin iki dönemde okunması yinelenme değildir. Bu ayrım Elektronik Tablolarla Analiz kursunda kurulmuştur, verili sayılır.
- VZ7. Tekilleştirme ölçütü satırdan üretilen bir anahtar metnidir. Ölçütün hangi alanları kapsadığı ve metni nasıl karşılaştırdığı iki ayrı karardır.
- VZ8. Benzerlik eşiği bir karardır. Örtüşme oranı kayan noktalı bir sayıdır ve eşitlik işleciyle değil, yazılı bir eşikle karşılaştırılır.
Ölçüt Değişince Kaç Satır Birleşiyor
// tekil.mjs — bir onceki dersin ham kumesi MODELIDIR: her alan saha formundan // geldigi haliyle metindir. Kume kurgudur: bolgesel olcum aginin 1400 abonesi // ve saha ziyaret kayitlari; ayni tohum, ayni satirlar. export const TOHUM = 20260305, ABONE = 1400; const karistir = (x) => { x = Math.imul(x ^ (x >>> 16), 2246822507); x = Math.imul(x ^ (x >>> 13), 3266489909); return (x ^ (x >>> 16)) >>> 0; }; export const uretec = (t) => { let s = karistir(t); return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; export 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; }; // Son alan: ziyaretin okuma uretmeme orani. Bolgeler bu oranda ayrisir. export const BOLGE = [["kuzey", "KZ", 0.28, 21, 0.06], ["guney", "GN", 0.22, 17, 0.03], ["dogu", "DG", 0.18, 26, 0.27], ["bati", "BT", 0.14, 14, 0.03], ["merkez", "MR", 0.18, 23, 0.08]] .map(([ad, kod, pay, taban, eksik]) => ({ ad, kod, pay, taban, eksik })); const SOKAK = ["Çınar", "Söğüt", "Işık", "Ihlamur", "Zeytin", "Menekşe", "Palmiye", "Ceviz"]; const TARIFE = ["Mesken", "mesken", "MESKEN", " Mesken ", "Konut", "konut", "Ticari", "ticari", "TİCARİ", "Is yeri", "İş Yeri", "isyeri", "Sanayi", "sanayi", "Tarimsal"]; export const HAM = []; let bir = null; for (let i = 0; i < ABONE; i++) { const r = uretec(TOHUM + i); const b = BOLGE[ayrik(r(), BOLGE.map((x) => x.pay))]; const taban = Math.max(1.2, b.taban * Math.exp((r() + r() + r() - 1.5) * 0.62)); const sayac = `${b.kod}-${String(1000 + i).padStart(4, "0")}`; const paylas = bir && bir.b === b.ad && r() < 0.03; // ayni binada ikinci sayac const sk = paylas ? bir.sk : SOKAK[Math.floor(r() * SOKAK.length)]; const kapi = paylas ? bir.kapi : 1 + Math.floor(r() * 60); const daire = paylas ? bir.daire : r() < 0.26 ? `/${1 + Math.floor(r() * 9)}` : ""; bir = { b: b.ad, sk, kapi, daire }; const endeks0 = 8000 + Math.floor(r() * 11000); for (const d of r() < 0.34 ? ["2026-01", "2026-02"] : ["2026-01"]) { const q = uretec(TOHUM + 7000 + HAM.length), u = q(); const gercek = Math.round(taban * (d === "2026-02" ? 0.94 : 1) * 100) / 100; // Eksiklik uc bicimde gorunur: hucre bos, hucrede yalniz bosluk, "yok" yazili. const eksik = u < b.eksik ? ayrik(q(), [0.52, 0.19, 0.29]) : -1; const kayma = eksik < 0 && q() < 0.008; // ondalik kaymasi: okuma on kat yazilmis const yazilan = kayma ? Math.round(gercek * 1000) / 100 : gercek; const okuma = eksik === 0 ? "" : eksik === 1 ? " " : eksik === 2 ? "yok" : q() < 0.31 ? yazilan.toFixed(2).replace(".", ",") : yazilan.toFixed(2); const gun = 1 + Math.floor(q() * 27), ay = d.slice(5); const tb = ayrik(q(), [0.55, 0.21, 0.24]); const tarih = tb === 0 ? `${d}-${String(gun).padStart(2, "0")}` : tb === 1 ? `${String(gun).padStart(2, "0")}.${ay}.2026` : `${String(gun).padStart(2, "0")}/${ay}/2026`; const endeks = endeks0 + Math.round(gercek * (d === "2026-02" ? 2 : 1)); const eb = q() < 0.22; const av = ayrik(q(), [0.30, 0.18, 0.18, 0.16, 0.18]); const kap = `${kapi}${daire}`; const M = `${b.ad[0].toUpperCase()}${b.ad.slice(1)}`, mUst = b.ad.toLocaleUpperCase("tr"); const alt = sk.toLocaleLowerCase("tr"), ust = sk.toLocaleUpperCase("tr"); const adres = av === 0 ? `${b.ad} mahallesi ${alt} sokak no: ${kap}` : av === 1 ? `${M} Mahallesi ${sk} Sokak No ${kap}` : av === 2 ? `${mUst} MAHALLESİ ${ust} SOKAK NO ${kap}` : av === 3 ? ` ${M} Mahallesi ${sk} Sokak No ${kap} ` : `${b.ad} mah. ${alt} sk. no:${kap}`; HAM.push({ abone_no: 10001 + i, sayac_no: q() < 0.011 ? "" : sayac, bolge: b.ad, donem: d, okuma_metni: okuma, endeks_metni: eb ? `${Math.floor(endeks / 1000)}.${String(endeks % 1000).padStart(3, "0")}` : `${endeks}`, tarih_metni: tarih, tarife_metni: TARIFE[ayrik(q(), Array(15).fill(1 / 15))], adres_metni: adres, gercek_m3: gercek, yazilan_m3: eksik < 0 ? yazilan : null }); if (q() < 0.09) { // ayni ziyaret ikinci kez girilmis const on = HAM[HAM.length - 1], bicim = ayrik(q(), [0.34, 0.28, 0.38]); HAM.push({ ...on, sayac_no: bicim === 0 ? ` ${sayac}` : bicim === 1 ? sayac.toLowerCase() : sayac, adres_metni: av === 2 ? `${b.ad} mah. ${alt} sk. no:${kap}` : `${mUst} MAHALLESİ ${ust} SOKAK NO ${kap}`, tarife_metni: TARIFE[ayrik(q(), Array(15).fill(1 / 15))] }); } } } // Okumayi sayiya cevirir. Ondalik ayraci virgul de olabilir; donusumun kendi // tuzaklari bu konunun ucuncu dersinde olculur, burada tolere edilir. export const sayiya = (t) => { const s = String(t).trim().replace(",", "."); return s === "" || !/^\d+(\.\d+)?$/.test(s) ? null : Number(s); }; export const medyan = (a) => { const l = [...a].sort((x, y) => x - y); return l.length % 2 ? l[(l.length - 1) / 2] : (l[l.length / 2 - 1] + l[l.length / 2]) / 2; }; export const y = (w) => (h) => h.map((v, j) => (j ? String(v).padStart(w[j]) : String(v).padEnd(w[0]))).join(""); // Onceki dersin karari: kimlik alani bos olan satirlar elendi, eksik okumali // satirlar isaretlenip bolge ortancasiyla dolduruldu. Bu adima 2068 satir girer. const kimlikli = HAM.filter((s) => s.sayac_no.trim() !== ""); const olculen = kimlikli.filter((s) => sayiya(s.okuma_metni) !== null); const bolgeMed = new Map(BOLGE.map((b) => [b.ad, medyan(olculen .filter((s) => s.bolge === b.ad).map((s) => sayiya(s.okuma_metni)))])); export const GIREN = kimlikli.map((s) => sayiya(s.okuma_metni) === null ? { ...s, okuma_metni: bolgeMed.get(s.bolge).toFixed(2) } : s); // Tekillestirme olcutu: satirdan bir anahtar metni uretir. Olcut degisince // ayni kumeden farkli sayida satir kalir. export const OLCUT = [ ["O1 tam satir", (s) => [s.abone_no, s.sayac_no, s.donem, s.okuma_metni, s.endeks_metni, s.tarih_metni, s.tarife_metni, s.adres_metni].join("|")], ["O2 abone + donem + sayac (ham)", (s) => `${s.abone_no}|${s.donem}|${s.sayac_no}`], ["O3 abone + donem + sayac (duzeltilmis)", (s) => `${s.abone_no}|${s.donem}|${s.sayac_no.trim().toUpperCase()}`], ]; export const tekille = (l, k) => { const m = new Map(); for (const s of l) if (!m.has(k(s))) m.set(k(s), s); return [...m.values()]; }; if (import.meta.url === `file://${process.argv[1]}`) { console.log(`model (tohum ${TOHUM}): onceki adimdan devralinan kume ${GIREN.length} satir`); const y1 = y([41, 8, 11]); console.log("\n" + y1(["tekillestirme olcutu", "kalan", "birlesen"])); for (const [ad, f] of OLCUT) { const l = tekille(GIREN, f); console.log(y1([ad, l.length, GIREN.length - l.length])); } // Ikinci girisler: O3 altinda ayni gruba dusen satirlarda hangi alan farkli yazilmis. const grup = new Map(); for (const s of GIREN) { const a = OLCUT[2][1](s); grup.set(a, (grup.get(a) ?? []).concat([s])); } const cift = [...grup.values()].filter((l) => l.length > 1); const fark = (alan) => cift.filter((l) => new Set(l.map((s) => s[alan])).size > 1).length; const y2 = y([26, 8, 9]); console.log("\n" + y2([`${cift.length} ikinci giriste fark`, "kayit", "oran %"])); for (const alan of ["sayac_no", "adres_metni", "tarife_metni", "okuma_metni"]) console.log(y2([alan, fark(alan), (100 * fark(alan) / cift.length).toFixed(1)])); }
model (tohum 20260305): onceki adimdan devralinan kume 2068 satir tekillestirme olcutu kalan birlesen O1 tam satir 2068 0 O2 abone + donem + sayac (ham) 2000 68 O3 abone + donem + sayac (duzeltilmis) 1880 188 188 ikinci giriste fark kayit oran % sayac_no 120 63.8 adres_metni 188 100.0 tarife_metni 173 92.0 okuma_metni 0 0.0
Üç ölçüt üç sayı verdi. Ö1 tam satır en güvenli görünen ölçüttür ve hiçbir işi yapmaz; ihtiyat burada sorunun tamamını olduğu gibi bırakır. Ö2 sayaç kodunu geldiği haliyle karşılaştırır ve 68 satır birleştirir, Ö3 aynı ölçütü kırpılmış ve harf birliğine getirilmiş kodla uygular ve 188’e çıkar. Aradaki 120 satır hangi alanların ölçüte gireceği kararının değil, o alanların nasıl karşılaştırılacağı kararının bedelidir.
İkinci tablo nedenini söylüyor. 188 ikinci girişin hepsinde adres farklı yazılmıştır, yüzde 92’sinde tarife sınıfı, yüzde 63,8’inde sayaç kodu; değişmeyen tek alan okumadır. Ölçüm aynı kalmış, onu çevreleyen her metin yeniden yazılmıştır. Yinelenmeyi ölçütün kapsadığı alanlar belirler, satırın tamamı değil.
Kimlik Eşleştirme
// eslesme.mjs — tekillestirmeden ayri bir is: kimlik eslestirme. Destek // talepleri MODELIDIR; sayac kodu cagri sirasinda elle yazilmistir. Talebi // aboneye baglamanin iki yolu karsilastirilir: kesin eslesme ve benzerlik esigi. import { GIREN, OLCUT, tekille, uretec, ayrik, y } from "./tekil.mjs"; const TEKIL = tekille(GIREN, OLCUT[2][1]); // Sayac kutugu tekillestirilmis kumeden cikarilir; kodu hic okunakli yazilmamis // aboneler kutukte yoktur. const KOD = new Map(); for (const s of TEKIL) KOD.set(s.sayac_no.trim().toUpperCase(), s.abone_no); const sade = (t) => t.trim().toLocaleUpperCase("tr").replace(/[^A-Z0-9]/g, ""); const SADE = new Map([...KOD.keys()].map((k) => [sade(k), k])); // 240 destek talebi. Operatorun yazim hatasi bes tipte: dogru, kucuk harf ve // bastaki bosluk, ayracsiz, iki rakam yer degistirmis, son rakam eksik. const kodlar = [...KOD.keys()], TALEP = [], tr = uretec(20260305 + 50000); for (let i = 0; i < 240; i++) { const dogru = kodlar[Math.floor(tr() * kodlar.length)]; const tip = ayrik(tr(), [0.42, 0.18, 0.14, 0.14, 0.12]); const rk = dogru.slice(3), takas = rk.slice(0, 2) + rk[3] + rk[2]; TALEP.push({ talep_no: 5001 + i, tip, gercek_kod: dogru, yazilan: tip === 0 ? dogru : tip === 1 ? ` ${dogru.toLowerCase()}` : tip === 2 ? dogru.replace("-", "") : tip === 3 ? `${dogru.slice(0, 3)}${takas}` : dogru.slice(0, -1) }); } console.log(`model: ${TEKIL.length} tekil olcum satiri, ${KOD.size} tekil sayac kodu, ` + `${TALEP.length} destek talebi`); const y1 = y([34, 9, 9, 12]); console.log("\n" + y1(["kesin eslesme", "baglanan", "yanlis", "baglanmayan"])); for (const [ad, f] of [["E1 ham metin", (t) => KOD.get(t.yazilan.trim().toUpperCase())], ["E2 sadelestirilmis kod", (t) => KOD.get(SADE.get(sade(t.yazilan)))]]) { const bag = TALEP.map((t) => [t, f(t)]).filter(([, a]) => a !== undefined); console.log(y1([ad, bag.length, bag.filter(([t, a]) => a !== KOD.get(t.gercek_kod)).length, TALEP.length - bag.length])); } // Benzerlik: uc harfli parcalarin ortusme orani. Esik bir karardir; kayan // noktali oran esitlik islecine sokulmaz, esikle karsilastirilir. const uclu = (t) => { const s = ` ${t} `, r = new Set(); for (let i = 0; i + 3 <= s.length; i++) r.add(s.slice(i, i + 3)); return r; }; const ortusme = (a, b) => { let o = 0; for (const g of a) if (b.has(g)) o++; return (2 * o) / (a.size + b.size); }; const sadeler = [...SADE.keys()], gram = new Map(sadeler.map((k) => [k, uclu(k)])); const kalanlar = TALEP.filter((t) => !SADE.has(sade(t.yazilan))); const y2 = y([9, 13, 9, 17, 10]); console.log("\n" + y2(["esik", "ek baglanan", "yanlis", "birden cok aday", "bagsiz"])); for (const esik of [0.60, 0.70, 0.80, 0.90]) { let ek = 0, yanlis = 0, belirsiz = 0; for (const t of kalanlar) { const g = uclu(sade(t.yazilan)); let en = 0, aday = []; for (const k of sadeler) { const d = ortusme(g, gram.get(k)); if (d > en + 1e-9) { en = d; aday = [k]; } else if (Math.abs(d - en) < 1e-9) aday.push(k); } if (en < esik) continue; if (aday.length > 1) { belirsiz++; continue; } ek++; if (SADE.get(aday[0]) !== t.gercek_kod) yanlis++; } console.log(y2([esik.toFixed(2), ek, yanlis, belirsiz, kalanlar.length - ek - belirsiz])); } console.log(`\ntek basina benzerlik esigi kesin eslesmenin biraktigi ${kalanlar.length} talebi ` + `cozmuyor: 0.80 esiginde kurdugu 18 bagin 17'si yanlis abonedir, cunku iki rakami yer ` + `degistirmis bir kod baska bir abonenin gecerli kodudur`); console.log(`satir hesabi: giren ${GIREN.length}, cikan ${TEKIL.length}; ` + `${GIREN.length - TEKIL.length} satir birlesti, 0 satir sessizce dustu. Yan defter: ` + `240 talebin 192'si baglandi, 23'u yanlis aboneye, 44'u bagsiz kaldi`);
model: 1880 tekil olcum satiri, 1392 tekil sayac kodu, 240 destek talebi kesin eslesme baglanan yanlis baglanmayan E1 ham metin 149 6 91 E2 sadelestirilmis kod 174 6 66 esik ek baglanan yanlis birden cok aday bagsiz 0.60 20 19 46 0 0.70 18 17 4 44 0.80 18 17 4 44 0.90 2 2 0 64 tek basina benzerlik esigi kesin eslesmenin biraktigi 66 talebi cozmuyor: 0.80 esiginde kurdugu 18 bagin 17'si yanlis abonedir, cunku iki rakami yer degistirmis bir kod baska bir abonenin gecerli kodudur satir hesabi: giren 2068, cikan 1880; 188 satir birlesti, 0 satir sessizce dustu. Yan defter: 240 talebin 192'si baglandi, 23'u yanlis aboneye, 44'u bagsiz kaldi
Kesin eşleşme iki kararla iki sayı veriyor: ham metinle 149, sadeleştirilmiş kodla 174 talep bağlanır — yirmi beş talep yalnızca ayraç yüzünden bağlanamıyordu. Asıl uyarı ikinci sütundadır: iki kararda da altı talep yanlış aboneye bağlanır. İki rakamı yer değiştirmiş bir sayaç kodu bozuk bir metin değil, başka bir abonenin geçerli kodudur; kesin eşleşme onu tereddütsüz bağlar.
Benzerlik eşiği kalan 66 talebi çözmüyor. Eşik 0,80’de kurulan 18 bağın 17’si yanlıştır; 0,90’a çekilince bağ sayısı 2’ye iner ve ikisi de yanlıştır; 0,60’a gevşetilince yalnız iki bağ daha kurulur, buna karşılık 46 talep birden çok eşit adayla belirsiz kalır. Kısa ve yapılı bir kimlik metninde benzerlik, doğru kaydı bulmaktan çok komşu kaydı bulur.
Adımın hesabı şudur: giren 2.068 satır, çıkan 1.880 satır. Aradaki 188 satır birleşti, sessizce düşen satır yoktur. Yan defterde 240 talebin 192’si bağlandı, 23’ü yanlış aboneye, 44’ü bağsız kaldı. Bu 23 kayıt satır sayısını hiç oynatmaz; bundan sonraki her talep sayımında yanlış abonenin altında görünür.
Özet
- Tekilleştirme ölçütü satırdan üretilen bir anahtar metnidir; aynı 2.068 satırlık kümede üç ölçüt 2.068, 2.000 ve 1.880 satır bırakır.
- Tam satır karşılaştırması hiçbir satırı birleştirmez: ikinci girişi olan 188 kaydın hepsinde adres, yüzde 92’sinde tarife sınıfı, yüzde 63,8’inde sayaç kodu farklı yazılmıştır ve okuma değeri hiç değişmemiştir. Alanların nasıl karşılaştırıldığı kararı tek başına 120 satır oynatır.
- Kimlik eşleştirme ayrı bir iştir: 240 talebin 149’u ham metinle, 174’ü sadeleştirilmiş kodla bağlanır ve iki kararda da altısı yanlış aboneye gider.
- Benzerlik eşiği kalan 66 talebi çözmez: 0,80’de 18 bağın 17’si, 0,90’da 2 bağın 2’si yanlıştır; 0,60’ta 46 talep birden çok eşit adayla belirsiz kalır.
- Adımın hesabı: giren 2.068, çıkan 1.880; 188 birleşti, 0 sessizce düştü, yan defterde 23 kayıt yanlış aboneye yazıldı.
Sonraki Adım
Kalan 1.880 satırın okuma, endeks ve tarih alanları hâlâ metindir ve hesaba girmeden önce sayıya ve tarihe çevrilmek zorundadır. Dönüşüm zararsız görünür: hiçbir uyarı vermez, hiçbir işlem durmaz. Sonraki ders aynı kümeyi iki dönüşüm kararıyla çevirir ve yaygın çağrının kaç kaydı hatasız görünerek bozduğunu, kaç satırı da hiç haber vermeden düşürdüğünü sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.