Ders 12 / 13
Kohort ve Huni Analizi
Kohort tanımının aynı kümeden kaç ayrı elde tutma oranı çıkardığının sayılması: gözlenemeyen kohortu sıfır sayan kural 0,619, dışlayan kural 0,867 verir ve üç dönemlik pencerenin dışında 3.199 okumanın 843'ü kalır. Giriş penceresi dönem alındığında iki gözlenebilir kohort ve 0,009 oran açıklığı, beş hafta alındığında altı kohort ve 0,040 açıklık çıkar; en dar oynama payı 0,087 olduğu için açıklığın tamamı tek bir kohortun payının içine sığar. Huni yalnız toplulaştırma tersine dönmesi kadar geçer: toplulaştırılmış fark -0,237 iken gözlenebilir iki kohortun ikisinde de fark +0,083 ve +0,028'dir.
İçindekiler
Önceki ders bir katsayının kırılıma göre yön değiştirdiğini gösterdi, ama kırılım hep bölgeydi ve zaman hiç işin içine girmedi. Aboneler ağa aynı anda katılmıyor ve aynı süre boyunca izlenmiyor. Bir aboneyi kayıt açıldıktan bir dönem sonra da okuyabiliyor olmak bir başarıdır ve bu başarının oranına elde tutma denir.
Bu dersin sayısı kohort tanımının oynattığı elde tutma oranıdır. Kohort, giriş dönemine göre kurulan kümedir ve tanımı iki karar içerir: giriş penceresi ne kadar geniştir ve gözlem penceresi nerede kapanır. İki karar da savunulabilir, ikisi de aynı kümeden başka bir oran çıkarır. Dönüşüm hunisinin adım hesabı burada tekrarlanmaz; adım dönüşümü, terk oranı ve kümülatif dönüşüm Kullanıcı Deneyimi kursunun Dönüşüm Hunisi dersinde ölçüldü. Huni bu derse yalnız toplulaştırma tersine dönmesi kadar girer.
- UI36. Küme K03’ten devralınır ve kurgudur: 3.199 okuma, 1.329 abone, üç dönem, beş bölge.
- UI37. Giriş haftası alanı da kurgudur: ölçüm ağı bölgelere aynı anda ulaşmamıştır, bu yüzden giriş dönemi dağılımı bölgeye bağlıdır.
- UI38. Okuma indisi bu derste girişten sonraki dönemi verir; takvim dönemi giriş dönemi ile indisin toplamından bir eksiktir ve takvim üç dönemde kapanır.
- UI39. Elde tutma, kohortun girişten dönem sonra hâlâ okuma üreten abonelerinin oranıdır. dönem sonrası takvimin dışına düşüyorsa oran gözlenemez.
- UI40. Gözlenemeyen aboneye iki kural uygulanabilir: sıfır sayan kural onu elde tutulmamış sayar, dışlayan kural onu paydadan çıkarır.
- UI41. Oynama payı geri koyarak örneklemeyle üretilir: kohort listesi 1.000 kez yeniden çekilir.
- UI42. Okuma yöntemi alanı da kurgudur ve iki ilişkiyi birlikte taşır: uzaktan okuma geç dönemlerde yaygınlaşmıştır ve kesintisi daha azdır.
Gözlem Penceresi Kohortu Keser
Takvim üç dönemde kapanıyor. Üçüncü dönemde ağa katılan bir abone için “bir dönem sonra hâlâ okunabiliyor mu” sorusunun cevabı kümede yoktur. Bu abone elde tutulamamış değildir; henüz ölçülmemiştir.
# kohort.py — MODELDIR. K03'un kurgu kumesi ayni tohumla uretilir. Giris haftasi # alani da KURGUDUR: olcum agi bolgelere ayni anda ulasmamistir, bu yuzden giris # donemi dagilimi bolgeye baglidir. Okuma indisi girisden sonraki donemi verir; # takvim donemi = giris donemi + indis - 1 ve takvim uc donemde kapanir. import math TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26), ("bati", 0.14, 14), ("merkez", 0.18, 23)] GIRIS = {"dogu": [0.60, 0.28, 0.12], "merkez": [0.52, 0.30, 0.18], "kuzey": [0.40, 0.34, 0.26], "guney": [0.28, 0.36, 0.36], "bati": [0.16, 0.32, 0.52]} 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 def ayrik(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 ABONE, ELENEN = [], [] for i in range(HAM): r = uretec(TOHUM + i) b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])] ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05]) ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17]) (ELENEN if r() < 0.046 else ABONE).append( {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]}) GECERLI = {k["abone_no"] for k in ABONE} OKUMA = {} for k in ABONE + ELENEN: r = uretec(TOHUM + 7000 + k["abone_no"]) for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])): t = 0.0 if r() < 0.038 else math.floor( k["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100 + 0.5) / 100 if k["abone_no"] in GECERLI: OKUMA.setdefault(k["abone_no"], []).append(t) r() KATILAN = [] for k in ABONE: if k["abone_no"] not in OKUMA: continue r = uretec(TOHUM + 61000 + k["abone_no"]) e = ayrik(r(), GIRIS[k["bolge"]]) hafta = e * 13 + 1 + int(r() * 13) KATILAN.append({"no": k["abone_no"], "bolge": k["bolge"], "hafta": hafta, "kohort": e + 1, "blok": (hafta - 1) // 5 + 1, "n": len(OKUMA[k["abone_no"]])}) PENCERE = 3 def gozlenebilir(x, K): """Kohortun K donem sonrasi uc donemlik takvimin icinde kaliyor mu.""" return x["kohort"] + K <= PENCERE def tutuldu(x, K): return x["n"] >= K + 1 and gozlenebilir(x, K) def oran(alt, K, kural): pay = alt if kural == "sifir sayan" else [x for x in alt if gozlenebilir(x, K)] if not pay: return None, 0 return sum(1 for x in pay if tutuldu(x, K)) / len(pay), len(pay) print(f"kurgu kume (tohum {TOHUM}): {len(ABONE)} abone, " f"{sum(len(v) for v in OKUMA.values())} okuma") print("giris kohortu (donem): " + ", ".join( f"{k}: {sum(1 for x in KATILAN if x['kohort'] == k)}" for k in (1, 2, 3)) + f" (en az bir okumasi olan {len(KATILAN)} abone)") print(f"uc donemlik pencerenin disinda kalan okuma: " f"{sum(max(0, x['kohort'] + x['n'] - 1 - PENCERE) for x in KATILAN)}" f" / {sum(x['n'] for x in KATILAN)}") for K in (1, 2): print(f"\nelde tutma (girisden {K} donem sonra)") print("kohort n gozlenebilir sifir sayan dislayan") for koh in (1, 2, 3): alt = [x for x in KATILAN if x["kohort"] == koh] o1, n1 = oran(alt, K, "sifir sayan") o2, n2 = oran(alt, K, "dislayan") print(f"{koh:>6} {len(alt):>6} {n2:>14} {o1:>11.3f} " + (f"{o2:>8.3f}" if o2 is not None else f"{'--':>8}")) o1, _ = oran(KATILAN, K, "sifir sayan") o2, n2 = oran(KATILAN, K, "dislayan") print(f"{'toplu':>6} {len(KATILAN):>6} {n2:>14} {o1:>11.3f} {o2:>8.3f}")
kurgu kume (tohum 20260218): 1329 abone, 3199 okuma
giris kohortu (donem): 1: 488, 2: 412, 3: 360 (en az bir okumasi olan 1260 abone)
uc donemlik pencerenin disinda kalan okuma: 843 / 3199
elde tutma (girisden 1 donem sonra)
kohort n gozlenebilir sifir sayan dislayan
1 488 488 0.871 0.871
2 412 412 0.862 0.862
3 360 0 0.000 --
toplu 1260 900 0.619 0.867
elde tutma (girisden 2 donem sonra)
kohort n gozlenebilir sifir sayan dislayan
1 488 488 0.648 0.648
2 412 0 0.000 --
3 360 0 0.000 --
toplu 1260 488 0.251 0.648
Rapora girecek sayı, bir dönemlik elde tutma için 0,619 mu 0,867 mi. İkisi de aynı kümeden ve aynı tanımdan çıkıyor; ayrıldıkları tek yer, gözlenemeyen 360 abonenin ne sayıldığı. Sıfır sayan kural onları elde tutulmamış kabul ediyor ve oranı 0,248 aşağı çekiyor. İki dönemlik elde tutmada fark daha da büyük: 0,251 ile 0,648.
Gözlenemeyen aboneyi sıfır saymak bir ölçüm değil bir varsayımdır ve bu varsayım kümede karşılanmıyor. Kohort 3’ün 360 abonesi için sonraki dönemin okuması hiç toplanmamıştır; toplanmış olsaydı ilk iki kohortun 0,871 ve 0,862 oranlarına yakın bir sayı vermesi beklenir.
Üçüncü satır bu kesilmenin boyutunu veriyor: 3.199 okumanın 843’ü üç dönemlik takvimin dışına düşüyor. Kohort tanımı kümeyi kesiyor ve kesilen parça küçük değil.
Giriş Penceresi Kohort Sayısını Değiştirir
Kohortun ikinci kararı giriş penceresinin genişliğidir. Dönem yerine beş haftalık bloklar alınırsa aynı aboneler sekiz kohorta dağılır. Her kohortun oranının yanına payı da basılır.
# Giris penceresi degisince ayni kumeden kac ayri oran cikiyor. ORNEKLEME = 1000 rs = uretec(TOHUM + 44000) def oran_araligi(alt, K): """Geri koyarak ornekleme: kohort listesi 1.000 kez yeniden cekilir.""" n, cekilen = len(alt), [] for _ in range(ORNEKLEME): cekilen.append(sum(1 for _ in range(n) if tutuldu(alt[int(rs() * n)], K)) / n) cekilen.sort() return cekilen[24], cekilen[974] def tara(alan, ad): anahtarlar = sorted({x[alan] for x in KATILAN}) print(f"\ngiris penceresi: {ad} -> {len(anahtarlar)} kohort " "(dislayan kural, 1 donem)") print("kohort n elde tutma oynama payi genislik") genislikler, oranlar = [], [] for a in anahtarlar: alt = [x for x in KATILAN if x[alan] == a and gozlenebilir(x, 1)] if len(alt) < 10: print(f"{a:>6} {len(alt):>6} {'gozlenemez':>10}") continue o = sum(1 for x in alt if tutuldu(x, 1)) / len(alt) d, u = oran_araligi(alt, 1) genislikler.append(u - d) oranlar.append(o) print(f"{a:>6} {len(alt):>6} {o:>10.3f} [{d:.3f}, {u:.3f}] {u - d:>10.3f}") print(f" oran acikligi {max(oranlar) - min(oranlar):.3f}, " f"en dar pay {min(genislikler):.3f}, en genis pay {max(genislikler):.3f}") return oranlar d3 = tara("kohort", "donem (13 hafta)") d8 = tara("blok", "bes hafta") print(f"\nayni kumeden cikan elde tutma orani sayisi: {len(d3)} ve {len(d8)}") print(f"donem tanimiyla aralik {min(d3):.3f} - {max(d3):.3f}, " f"bes haftalik tanimla {min(d8):.3f} - {max(d8):.3f}")
giris penceresi: donem (13 hafta) -> 3 kohort (dislayan kural, 1 donem)
kohort n elde tutma oynama payi genislik
1 488 0.871 [0.840, 0.898] 0.057
2 412 0.862 [0.828, 0.896] 0.068
3 0 gozlenemez
oran acikligi 0.009, en dar pay 0.057, en genis pay 0.068
giris penceresi: bes hafta -> 8 kohort (dislayan kural, 1 donem)
kohort n elde tutma oynama payi genislik
1 181 0.878 [0.829, 0.923] 0.094
2 185 0.854 [0.805, 0.903] 0.097
3 184 0.891 [0.848, 0.935] 0.087
4 174 0.856 [0.805, 0.908] 0.103
5 155 0.852 [0.800, 0.903] 0.103
6 21 0.857 [0.667, 1.000] 0.333
7 0 gozlenemez
8 0 gozlenemez
oran acikligi 0.040, en dar pay 0.087, en genis pay 0.333
ayni kumeden cikan elde tutma orani sayisi: 2 ve 6
donem tanimiyla aralik 0.862 - 0.871, bes haftalik tanimla 0.852 - 0.891
Giriş penceresi daraldığında raporlanabilir oran sayısı 2’den 6’ya çıkıyor ve oranlar arasındaki açıklık 0,009’dan 0,040’a genişliyor. Bir kohort matrisine bakan kişi bu genişlemeyi bir örüntü diye okuyabilir: üçüncü blok 0,891, beşinci blok 0,852, aradaki 4 puanlık düşüş bir eğilim gibi görünür.
Payların sütunu bu okumayı kapatıyor. En dar oynama payı 0,087 genişliğinde; altı blok arasındaki 0,040’lık açıklığın tamamı tek bir kohortun payının içine sığıyor. Blokların ikişer ikişer farkları da payların içinde kalıyor. İnce kohort tanımı yeni bilgi üretmedi, aynı bilgiyi daha çok sayıya böldü ve her sayının payını büyüttü.
Altıncı blok bunun uç örneği. Yirmi bir gözlenebilir aboneye dayanan oran 0,857, payı ise [0,667, 1,000]; genişliği 0,333, yani beş kohortun payının üç katından fazla. Yedinci ve sekizinci bloklar tümüyle gözlem penceresinin dışında kalıyor ve hiç oran üretmiyor. Kohort tanımı inceldikçe gözlenemeyen kohort sayısı da artıyor.
Toplulaştırılmış Oran ve Kohortların Oranı
Kohortun ne işe yaradığı, toplulaştırılmış bir oranın yanına konduğunda görünür. Kümede iki okuma yöntemi var ve huninin iki adımı şudur: kohorta giriş, sonraki dönemde okuma.
# Okuma yontemi alani da KURGUDUR ve iki iliskiyi birlikte tasir: uzaktan okuma # gec donemlerde yayginlasmistir ve kesintisi daha azdir. YONTEM_TABAN = {1: 0.28, 2: 0.55, 3: 0.80} for x in KATILAN: r = uretec(TOHUM + 71000 + x["no"]) p = YONTEM_TABAN[x["kohort"]] + (0.10 if x["n"] >= 2 else -0.10) x["yontem"] = "uzaktan" if r() < p else "yerinde" YON = ("yerinde", "uzaktan") def fark_araligi(a, b, K): na, nb, cekilen = len(a), len(b), [] for _ in range(ORNEKLEME): oa = sum(1 for _ in range(na) if tutuldu(a[int(rs() * na)], K)) / na ob = sum(1 for _ in range(nb) if tutuldu(b[int(rs() * nb)], K)) / nb cekilen.append(ob - oa) cekilen.sort() return cekilen[24], cekilen[974] print("huni: kohorta giris -> sonraki donemde okuma") print("\nkohort yerinde okuma uzaktan okuma fark oynama payi") for koh in (1, 2, 3): alt = [x for x in KATILAN if x["kohort"] == koh] a = [x for x in alt if x["yontem"] == "yerinde"] b = [x for x in alt if x["yontem"] == "uzaktan"] if not gozlenebilir(alt[0], 1): print(f"{koh:>6} {'--':>5} ({len(a):>3}) {'--':>5} ({len(b):>3})" f" -- gozlenemez") continue oa = sum(1 for x in a if tutuldu(x, 1)) / len(a) ob = sum(1 for x in b if tutuldu(x, 1)) / len(b) d, u = fark_araligi(a, b, 1) print(f"{koh:>6} {oa:.3f} ({len(a):>3}) {ob:.3f} ({len(b):>3})" f" {ob - oa:>+.3f} [{d:+.3f}, {u:+.3f}]") a = [x for x in KATILAN if x["yontem"] == "yerinde"] b = [x for x in KATILAN if x["yontem"] == "uzaktan"] oa = sum(1 for x in a if tutuldu(x, 1)) / len(a) ob = sum(1 for x in b if tutuldu(x, 1)) / len(b) d, u = fark_araligi(a, b, 1) print(f"{'toplu':>6} {oa:.3f} ({len(a):>3}) {ob:.3f} ({len(b):>3})" f" {ob - oa:>+.3f} [{d:+.3f}, {u:+.3f}]") print("\nyontemin kohort bilesimi (satir yuzdesi)") for y in YON: alt = [x for x in KATILAN if x["yontem"] == y] print(f" {y:<8} " + " ".join( f"kohort {k}: {sum(1 for x in alt if x['kohort'] == k) / len(alt):.3f}" for k in (1, 2, 3)))
huni: kohorta giris -> sonraki donemde okuma
kohort yerinde okuma uzaktan okuma fark oynama payi
1 0.843 (325) 0.926 (163) +0.083 [+0.028, +0.139]
2 0.844 (154) 0.872 (258) +0.028 [-0.041, +0.099]
3 -- ( 56) -- (304) -- gozlenemez
toplu 0.755 (535) 0.519 (725) -0.237 [-0.284, -0.184]
yontemin kohort bilesimi (satir yuzdesi)
yerinde kohort 1: 0.607 kohort 2: 0.288 kohort 3: 0.105
uzaktan kohort 1: 0.225 kohort 2: 0.356 kohort 3: 0.419
Toplulaştırılmış satır tek bir cümle söylüyor: uzaktan okunan sayaçlarda elde tutma 0,237 puan düşük ve payı [-0,284, -0,184], sıfırdan uzakta. Bu sayı ve bu pay, kursun kuralının istediği biçimde yazılmıştır ve yanlıştır.
Kohort satırları tersini gösteriyor. Birinci kohortta uzaktan okuma 0,083 puan önde ve payı [+0,028, +0,139], sıfırı içermiyor. İkinci kohortta 0,028 puan önde, payı sıfırı içeriyor. Üçüncü kohort gözlenemiyor. Toplulaştırılmış oranın gösterdiği yönü hiçbir kohort göstermiyor: ikisi tersini söylüyor, biri hiçbir şey söylemiyor.
Bu olguya toplulaştırma tersine dönmesi denir ve mekanizması son tabloda duruyor. Yerinde okunan sayaçların yüzde 60,7’si birinci kohortta; uzaktan okunanların yüzde 41,9’u üçüncü kohortta. Üçüncü kohort gözlem penceresi yüzünden sıfır sayılıyor, dolayısıyla uzaktan okumanın toplulaştırılmış oranı ağırlıklı olarak ölçülememiş abonelerden geliyor. İki yöntemin farkı değil, iki yöntemin kohort bileşimi ölçülmüştür.
Toplulaştırılmış bir oran, kohortlar arasında oran farkı ve bileşim farkı varsa hiçbir kohortu tarif etmez. Bileşim yazılmadan raporlanan bir toplulaştırılmış oran, bir ölçüm değil bir ortalama alma kararıdır.
Özet
- Gözlenemeyen kohorta uygulanan kural aynı kümeden iki oran çıkarır: bir dönemlik elde tutma sıfır sayan kuralla 0,619, dışlayan kuralla 0,867; iki dönemlikte 0,251 ile 0,648.
- Üç dönemlik gözlem penceresinin dışında 3.199 okumanın 843’ü kalır; kohort tanımı kümeyi keser.
- Giriş penceresi dönemden beş haftaya inince raporlanabilir oran sayısı 2’den 6’ya, oran açıklığı 0,009’dan 0,040’a çıkar; en dar oynama payı 0,087 olduğu için açıklığın tamamı tek bir kohortun payının içine sığar.
- Yirmi bir aboneye dayanan altıncı blokta oran 0,857, payı [0,667, 1,000] ve genişliği 0,333; yedinci ve sekizinci bloklar tümüyle gözlem penceresinin dışındadır.
- Toplulaştırma tersine dönmesi: toplulaştırılmış fark -0,237 ve payı sıfırdan uzakken, gözlenebilir iki kohortta fark +0,083 ve +0,028’dir. Yerinde okunanların yüzde 60,7’si birinci, uzaktan okunanların yüzde 41,9’u üçüncü kohorttadır.
Sonraki Adım
Üç derste sayının yanına payı yazıldı ve payı büyüten üç kaynak sayıldı: bileşim sayısı, modelde bulunmayan değişken, kohort tanımı. Üçünde de küme verilmiş kabul edildi. Kursun son dersi bu kabulü kaldırır: küme zaten okunabilmiş sayaçlardan ve en az bir okuma üretmiş abonelerden oluşuyor, yani ölçülen şey ağın kendisi değil ağın görünen kısmı. Aynı derste gürültüden kaç bulgu üretilebildiği sayılır ve bu sayı beklenen değerle karşılaştırılır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.