İçeriğe geç
academia.sh

Ders 06 / 10

Veri Kalitesi Boyutları

Kalite boyutlarının çalışan denetimlere çevrilmesi ve her denetimin hem yakaladığının hem kendi kusurunun sayılması: altı bozulma sınıfının yakalama gecikmesi sırasıyla 4, 0, 0, 4, 0 ve 0 aşamadır; kusurlu denetim kümesiyle üç sınıf hiç yakalanmaz; bölüm denetimi bölge kümesine baktığı için bir bölgenin tek dönemi hiç yazılmadığında yeşil kalır; birim değişimi özetin on hücresini oynatıp sunumu birebir aynı bırakır.

İçindekiler

Beş derste hat kuruldu ve sayıldı: hangi aşamanın kaç satır düşürdüğü, toplu ile akış işlemenin tazeliği nasıl değiştirdiği. O sayıların tamamı tek bir soruya yanıt veriyordu, kaç satır. Hiçbir aşamada satırın doğru olup olmadığı sorulmadı.

Bunlar iki ayrı sorudur ve aralarındaki fark bir veri hattının kendine özgü kusurudur: hat çöktüğünde haber verir, yanlış veri taşıdığında hiçbir şey söylemez. Bu ders kalite boyutlarını çalışan denetimlere çevirir, altı bozulma sınıfının kaç aşama sonra yakalandığını sayar ve denetimin kendisinin de kusurlu olabileceğini aynı kümede gösterir.

  • VK1. Kaynak, beş aşamalı hat, bozulma sınıfları ve denetimler kurgudur; gerçek bir veritabanı, dosya biçimi ya da orkestratör çağrılmaz. Tohum 20260218, hat koşumdan bağımsızdır.
  • VK2. Bozulma sınıfları saldırı değildir, üretici tarafı kazalarıdır: geciken dosya, iki kez gönderilen yük, adı değişen alan, birimi değişen sayaç.
  • VK3. Taban çizgisi denetimsiz hattır: yakalanan bozulma 0, gecikme tanımsız.
  • VK4. Her bozulma tek bir dönemi (2026-07) bozar; kalan on bir dönem sağlam kalır.
  • VK5. Temizleme, eksik ve aykırı değer yordamı Veri Toplama ve Hazırlama kursunda, sızıntı ile etiket gürültüsü Veri Hazırlama ve Öznitelik Mühendisliği kursunda ölçüldü ve tekrarlanmaz.
  • VK6. Gecikme aşama olarak yazılır, ham süre yazılmaz: aşama 0 kaynağın kendisi, 1’den 5’e hattın aşamalarıdır.
  • VK7. Çözünürlük ölçüm bandının alt sınırıdır: satır oranının adımı 0,0004, özet hücresinin 0,0182, sunum değerinin 0,2000. Sunum üzerinde oran basılmaz.

Boyut Bir Cümle Değil Bir Denetimdir

Veri kalitesi boyutu (data quality dimension) çoğunlukla bir cümle olarak yazılır: “veri eksiksiz olmalı”, “veri tutarlı olmalı”. Bu cümleler ölçülemez, çünkü hangi kümede, hangi aşamada ve neye karşı bakılacağını söylemezler. Boyut ancak bir yordama çevrildiğinde ölçülür: belirli bir aşamadan sonra koşan ve doğru ya da yanlış döndüren bir işlev.

Boyut Yordamın sorduğu Denetim Aşama
bütünlük (completeness) beklenen satırın hepsi geldi mi satir_sayisi, bolum, bolum_donem 0
tekillik (uniqueness) aynı anahtar iki kez var mı tekillik 0
geçerlilik (validity) alanlar beklenen adı ve türü taşıyor mu sema, tip 0
tutarlılık (consistency) değerler önceki dönemlerin düzeninde mi dagilim 4
doğruluk (accuracy) değer gerçeği yansıtıyor mu yok

Son satır tabloya bilerek konmuştur. Doğruluk (accuracy) hattın içinden ölçülemez: sayacın gerçekten kaç metreküp geçirdiğini bilmek için hattın dışında bir ölçüm gerekir ve bu hatta öyle bir kaynak yok. Ölçülebilen şey tutarlılıktır; kalite raporunda “doğruluk yüzde doksan dokuz” yazan satır çoğu kez tutarlılığı ölçüp adına doğruluk demiştir. Aşama sütunu ikinci bir şey söyler: dagilim kaynağa değil dördüncü aşamanın ürettiği özete bakar.

Ortak Tanım

Birinci blok kaynağı, beş aşamalı hattı, altı bozulma sınıfını ve yedi denetimi kurar. denetimli_hat her aşamadan sonra açık denetimleri işletir; ilk yakalayan denetimi ile yakalama gecikmesini döndürür.

# KURGUDUR. Olcum aginin sayac okumalari, bes asamali hat, bozulma siniflari ve
# denetimler ders icinde tanimlanir; gercek bir depo ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


# ---- KURGU kaynak: olcum aginin sayac okumalari, on iki donem
BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


# Her donem her abone icin bir okuma. Birim m3 , endeks artan.
def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- KURGU bozulma siniflari. Hicbiri bir saldiri degildir; hepsi
# gunluk hayatta olan uretici tarafi kazalaridir.
BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


# ---- HAT: bes asama. Her asama kendi cikardigi satiri sayar.
def a1_ayikla(kayit):
    """Zorunlu alanlari tasimayan satir dusurulur."""
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    """Sunum: bolge basina donem ortalamasi."""
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def hat(kayit):
    iz, veri = [], kayit
    for ad, f in ASAMA:
        veri, dusen = f(veri)
        iz.append({"asama": ad, "kalan": len(veri), "dusen": dusen})
    return veri, iz


# ---- DENETIMLER: her biri belirli bir asamadan SONRA kosar
def d_sema(veri, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in veri)


def d_tip(veri):
    return any(not isinstance(k.get("endeks"), int) for k in veri)


def d_tekillik(veri):
    ad = [(k["abone"], k["donem"]) for k in veri]
    return len(ad) != len(set(ad))


def d_satir_sayisi(veri, beklenen, pay=0.05):
    return abs(len(veri) - beklenen) / beklenen > pay


def d_bolum_tamligi(veri):
    """KUSURLU DENETIM: bolge kumesine bakar , donemi gormez."""
    return set(k["bolge"] for k in veri) != set(BOLGE)


def d_bolum_tamligi_donem(veri):
    """DOGRU DENETIM: her donemde her bolge var mi."""
    gor = set((k["bolge"], k["donem"]) for k in veri)
    return gor != set((b, d) for b in BOLGE for d in DONEM)


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


# Denetim kaydi: (ad , asama , islev). Asama 0 = kaynak , 1..5 = ASAMA sirasi.
DENETIM = [
    ("sema", 0, lambda v, t: d_sema(v)),
    ("tip", 0, lambda v, t: d_tip(v)),
    ("tekillik", 0, lambda v, t: d_tekillik(v)),
    ("satir_sayisi", 0, lambda v, t: d_satir_sayisi(v, t["beklenen_satir"])),
    ("bolum", 0, lambda v, t: d_bolum_tamligi(v)),
    ("bolum_donem", 0, lambda v, t: d_bolum_tamligi_donem(v)),
    ("dagilim", 4, lambda v, t: d_dagilim(v, t["taban_ozet"])),
]


def denetimli_hat(kayit, taban, acik=None):
    """Hatti kosturur ve her asamadan sonra acik denetimleri isletir.
    Donen deger: sunum , iz , ilk yakalayan denetim ve yakalama gecikmesi (asama)."""
    acik = [a for a, _, _ in DENETIM] if acik is None else acik
    veri, iz, yakalayan, gecikme = kayit, [], None, None
    for adim in range(len(ASAMA) + 1):
        if adim > 0:
            ad, f = ASAMA[adim - 1]
            veri, dusen = f(veri)
            iz.append({"asama": ad, "kalan": len(veri), "dusen": dusen})
        for dad, dasama, df in DENETIM:
            if dad not in acik or dasama != adim:
                continue
            if df(veri, taban):
                if yakalayan is None:
                    yakalayan, gecikme = dad, adim
    sunum = veri if isinstance(veri, dict) and veri and isinstance(
        list(veri.values())[0], float) else None
    return sunum, iz, yakalayan, gecikme


def taban_kur(kaynak):
    v1, _ = a1_ayikla(kaynak)
    v2, _ = a2_tekille(v1)
    v3, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(v3)
    sun, _ = a5_sun(oz)
    return {"beklenen_satir": len(kaynak), "taban_ozet": oz, "taban_sunum": sun}

Yakalama Gecikmesi

İkinci blok kuralı sayıya çevirir. Her bozulma sınıfı için hat iki kez koşuyor: bir kez yedi denetim açıkken, bir kez ["sema", "tip", "tekillik", "bolum"] kümesiyle. İkinci küme kusurludur, çünkü dönem duyarlı bütünlük denetimi ile dağılım denetimi kapalıdır. Son sütun sunulan beş değerin tabanla birebir aynı kalıp kalmadığını yazar.

KAYNAK_KAYIT = kaynak_uret()
TABAN = taban_kur(KAYNAK_KAYIT)
HEPSI = [a for a, _, _ in DENETIM]
KUSURLU = ["sema", "tip", "tekillik", "bolum"]

_, iz = hat(KAYNAK_KAYIT)
print("saglam hat:", " ".join(f"{i['asama']}={i['kalan']}" for i in iz))
print("sunum:", TABAN["taban_sunum"])
print("saglam hatta ateslenen denetim:", denetimli_hat(KAYNAK_KAYIT, TABAN, HEPSI)[2])
print()
print(f"{'bozulma':<15}{'satir':>6}{'yakalayan':>13}{'gecikme':>9}"
      f"{'kusurlu kume':>14}{'sunum':>9}")
for sinif in BOZULMA:
    bozuk, n = boz(KAYNAK_KAYIT, sinif)
    sunum, _, yak, gec = denetimli_hat(bozuk, TABAN, HEPSI)
    _, _, kyak, _ = denetimli_hat(bozuk, TABAN, KUSURLU)
    durum = "ayni" if sunum == TABAN["taban_sunum"] else "degisti"
    print(f"{sinif:<15}{n:>6}{yak:>13}{gec:>9}{str(kyak):>14}{durum:>9}")
saglam hat: ayikla=2400 tekille=2400 tuketim=2200 ozet=55 sun=5
sunum: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
saglam hatta ateslenen denetim: None

bozulma         satir    yakalayan  gecikme  kusurlu kume    sunum
gec_gelen          40      dagilim        4          None  degisti
yinelenen          60     tekillik        0      tekillik     ayni
sema_degisti      200         sema        0          sema  degisti
birim_degisti     200      dagilim        4          None     ayni
eksik_bolum        46  bolum_donem        0          None  degisti
kayan_tip         200          tip        0           tip     ayni

İlk üç satır tabanı verir: sağlam kaynakta beş aşama 2400’den 5 değere iniyor ve hiçbir denetim ateşlemiyor. Bu, denetim kümesinin iyi satırı yanlış engellemediğinin kanıtıdır.

Gecikme sütunu kuralın kendisidir. Dört sınıf aşama 0’da, kaynağın kendisinde yakalanıyor; hat hiç kirlenmiyor. İki sınıf ise aşama 4’te: gec_gelen ve birim_degisti yalnızca dördüncü aşamanın özetine bakan dağılım denetimiyle görünür oluyor. Aradaki dört aşama bir maliyet birimidir, çünkü bozulmuş satır o dört aşama boyunca ayıklanmış, tekilleştirilmiş, tüketime çevrilmiş ve özete toplanmıştır; düzeltmeden sonra geri doldurulacak olan da bu dört aşamanın çıktısıdır.

Son sütun kursun üçüncü iddiasını taşır: üç sınıf sunulan sayıyı birebir aynı bırakıyor. yinelenen, birim_degisti ve kayan_tip sonrasında beş sunum değeri tabanla aynıdır. Çıktıya bakan hiçbir gözlem bu üçünü göremez; tek kanıt denetimin kendisidir. kayan_tip en uç örnektir: endeks metne döndüğünde tüketim aşaması onu yine sayıya çeviriyor, tek bir satır bile düşmüyor.

Dördüncü sütun kusurlu kümeyi ölçer: üç sınıf hiç yakalanmıyor. gec_gelen ile birim_degisti dağılım denetimi kapalı olduğu için, eksik_bolum ise açık olan bolum denetimi kusurlu olduğu için. Bu kümeyi kuran ekip dört denetim yazmış, dördü de yeşil yanmış ve hattın altıda üçü ölçülmemiş kalmıştır.

Yeşil Kalan Denetim

Üçüncü blok kusuru açar. eksik_bolum bozulmasında kuzey bölgesinin 2026-07 dönemi hiç yazılmıyor. d_bolum_tamligi bölge kümesini beklenen kümeyle karşılaştırır; kuzey öbür on bir dönemde göründüğü için küme tamdır ve denetim geçer. d_bolum_tamligi_donem ise bölge ile dönemin çiftine bakar ve aynı kaynakta ateşler.

def ara_ozet(kayit):
    """Dorduncu asamanin cikisi: bolge ve donem basina toplam."""
    v, _ = a1_ayikla(kayit)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return a4_ozet(v)[0]


bozuk, n = boz(KAYNAK_KAYIT, "eksik_bolum")
print("yazilmayan satir:", n, " kuzeyin 2026-07 satiri:",
      sum(1 for k in bozuk if k["bolge"] == "kuzey" and k["donem"] == "2026-07"))
print("kaynaktaki bolge kumesi:", sorted(set(k["bolge"] for k in bozuk)))
print("kusurlu bolum denetimi:", d_bolum_tamligi(bozuk),
      " donem duyarli denetim:", d_bolum_tamligi_donem(bozuk))
oz1 = ara_ozet(bozuk)
print("ozet hucresi:", len(oz1), " kuzeyin ozetteki donem sayisi:",
      sum(1 for b, d in oz1 if b == "kuzey"))
print("sunum kuzey:", a5_sun(oz1)[0]["kuzey"], " taban:", TABAN["taban_sunum"]["kuzey"])

print()
oz2 = ara_ozet(boz(KAYNAK_KAYIT, "birim_degisti")[0])
for ad in (("kuzey", "2026-06"), ("kuzey", "2026-07"), ("kuzey", "2026-08")):
    print("ozet", ad, "taban:", TABAN["taban_ozet"][ad], " bozuk:", oz2[ad])
print("degisen ozet hucresi:", sum(1 for a in oz2 if oz2[a] != TABAN["taban_ozet"][a]),
      "/", len(oz2))
print("sunum kuzey:", a5_sun(oz2)[0]["kuzey"], " taban:", TABAN["taban_sunum"]["kuzey"])
yazilmayan satir: 46  kuzeyin 2026-07 satiri: 0
kaynaktaki bolge kumesi: ['bati', 'dogu', 'guney', 'kuzey', 'merkez']
kusurlu bolum denetimi: False  donem duyarli denetim: True
ozet hucresi: 53  kuzeyin ozetteki donem sayisi: 9
sunum kuzey: 1218.67  taban: 1212.55

ozet ('kuzey', '2026-06') taban: 1041  bozuk: 1041
ozet ('kuzey', '2026-07') taban: 1251  bozuk: 1812946491
ozet ('kuzey', '2026-08') taban: 1119  bozuk: -1812944121
degisen ozet hucresi: 10 / 55
sunum kuzey: 1212.55  taban: 1212.55

Birinci küme cümleyi kurar: bir denetimin yeşil olması ölçüldüğü anlamına gelmez. Bölge kümesi beş elemanlıdır, kusurlu denetim False döndürür. Aynı kaynakta 46 satır hiç gelmemiştir, özet 55 hücreden 53’e düşmüştür ve kuzeyin sunum değeri 1212,55’ten 1218,67’ye kaymıştır — sunum çözünürlüğü 0,2000 olduğuna göre otuz adımdan büyük, ölçülebilir bir fark, ama bakan yoktur. Kuzeyin özetteki dönem sayısı 9’a inmiştir, çünkü bir dönemin hiç yazılmaması onu izleyen dönemi de düşürür: tüketim dönem farkıyla çalışır ve 2026-08 satırlarının önceki dönemi artık yoktur. Bir aşamanın düşürdüğü satır, bozulmanın doğrudan sildiği satırla aynı sayı değildir.

İkinci küme en sessiz bozulmayı açar. birim_degisti sayacı metreküp yerine litre okuyor ama birim etiketini m3 bırakıyor. Özette kuzeyin 2026-07 toplamı 1251’den 1812946491‘e çıkıyor, 2026-08 toplamı 1119’dan −1812944121’e düşüyor. Mekanizma iptaldir: bozulan dönemde endeks bin katına çıktığı için o dönemin farkı şişer, izleyen dönemde endeks eski ölçeğine döndüğü için aynı büyüklükte eksi bir fark oluşur. Özetin on hücresi oynuyor. Bölgenin özetteki on bir döneminin ortalaması alındığında iki hücre birbirini götürüyor ve sunum değeri 1212,55’te birebir kalıyor; sunum kusursuz görünürken iki dönem çöptür.

Özet

  • Bir kalite boyutu ancak belirli bir aşamadan sonra koşan çalışan bir denetime çevrildiğinde ölçülmüş sayılır; doğruluk bu hatta ölçülemez, onun yerine ölçülen şey tutarlılıktır.
  • Sağlam kaynakta beş aşama 2400 satırı 5 sunum değerine indiriyor ve yedi denetimin hiçbiri ateşlemiyor; iyi satır yanlış engellenmiyor.
  • Altı bozulma sınıfının yakalama gecikmesi sırasıyla 4, 0, 0, 4, 0 ve 0 aşamadır; dört aşama, ara tabloları kirlenmiş ve geri doldurulması gereken bir hat demektir.
  • ["sema", "tip", "tekillik", "bolum"] kümesiyle üç sınıf hiç yakalanmıyor: gec_gelen, birim_degisti ve eksik_bolum. bolum bölge kümesine bakıp dönemi görmediği için 46 satırın hiç yazılmadığı kaynakta yeşil kalıyor.
  • Üç sınıf sunulan beş değeri birebir aynı bırakıyor; birim_degisti özetin on hücresini 1812946491 ile −1812944121 arasına taşıdığı hâlde ortalama 1212,55’te kalıyor.

Sonraki Adım

Bu dersin en hızlı yakalanan bozulması sema_degisti oldu: alan adı değiştiğinde sema denetimi aşama 0’da ateşledi. Ama denetimin baktığı alan listesi kodun içine gömülüydü ve o listeyi kimin yazdığı hiç sorulmadı. Üretici alanın adını değiştirdiğinde kendi tarafında hiçbir hata almaz; kırılan şey tüketicidir. Sonraki ders bu listeyi bir veri sözleşmesine çevirir, alan adı değiştiğinde kaç satırın düştüğünü ve sözleşmenin bozulmayı hangi aşamada yakaladığını sayar, sonra sözleşmenin kendi bedelini ölçer.

İ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