Think Stats kitabının üçüncü baskısını Bookshop.org ve Amazon üzerinden edinebilirsiniz (bunlar satış ortaklığı bağlantılarıdır). Ücretsiz çevrimiçi sürümü beğeniyorsanız bana bir kahve ısmarlamayı düşünebilirsiniz.
2. Dağılımlar#
Bu bölümde istatistiğin en temel kavramlarından biri olan dağılımı ele alacağız. Önce bir veri kümesindeki değerleri ve her değerin kaç kez görüldüğünü gösteren frekans tablolarıyla başlayacak, ardından bu tabloları National Survey of Family Growth (NSFG) verilerini keşfetmek için kullanacağız. Ayrıca aykırı değer adı verilen aşırı veya hatalı değerleri arayacak ve bunları ele alma yollarını değerlendireceğiz.
Bu notebook’u Colab’da çalıştırmak için buraya tıklayın.
2.1. Frekans tabloları#
Bir değişkeni betimlemenin yollarından biri, değişkenin değerlerini ve bunların frekanslarını, yani her değerin kaç kez görüldüğünü içeren frekans tablosudur. Bu betimlemeye değişkenin dağılımı denir.
Dağılımları temsil etmek için empiricaldist adlı bir kütüphane kullanacağız.
Bu bağlamda “ampirik”, dağılımların matematiksel modellere değil verilere dayandığını ifade eder.
empiricaldist, frekans tablolarını hesaplamak ve çizmek için kullanabileceğimiz FreqTab adlı bir sınıf sağlar.
Bu sınıfı şu şekilde içe aktarabiliriz.
from empiricaldist import FreqTab
Nasıl çalıştığını göstermek için küçük bir değer listesiyle başlayacağız.
t = [1.0, 2.0, 2.0, 3.0, 5.0]
FreqTab, bir dizi alıp FreqTab nesnesi oluşturan from_seq adlı bir yöntem sunar.
ftab = FreqTab.from_seq(t)
ftab
| freqs | |
|---|---|
| 1.0 | 1 |
| 2.0 | 2 |
| 3.0 | 1 |
| 5.0 | 1 |
FreqTab nesnesi, değerleri ve bunların frekanslarını içeren bir tür Pandas Series nesnesidir.
Bu örnekte 1.0 değerinin frekansı 1, 2.0 değerinin frekansı 2’dir ve bu şekilde devam eder.
FreqTab, frekans tablosunu çubuk grafik olarak çizen bar adlı bir yöntem sunar.
ftab.bar()
decorate(xlabel="Değer", ylabel="Frekans")
FreqTab bir Pandas Series nesnesi olduğu için köşeli ayraç işlecini kullanarak bir değeri arayabilir ve frekansını elde edebiliriz.
ftab[2.0]
np.int64(2)
Ancak Pandas Series nesnesinden farklı olarak, bir değeri aramak için FreqTab nesnesini fonksiyon gibi de çağırabiliriz.
ftab(2.0)
np.int64(2)
FreqTab içinde bulunmayan bir değeri ararsak fonksiyon söz dizimi 0 döndürür.
ftab(4.0)
0
FreqTab nesnesinin, değerlerden oluşan bir dizi döndüren qs adlı niteliği vardır. qs, “quantities” (nicelikler) sözcüğünün kısaltmasıdır; gerçi teknik açıdan bütün değerler nicelik değildir.
ftab.qs
array([1., 2., 3., 5.])
FreqTab ayrıca frekanslardan oluşan bir dizi döndüren fs adlı niteliğe sahiptir.
ftab.fs
array([1, 2, 1, 1])
Nicelik-frekans çiftleri üzerinde döngü kurmak için FreqTabin items yöntemini kullanabiliriz:
for x, freq in ftab.items():
print(x, freq)
1.0 1
2.0 2
3.0 1
5.0 1
İlerledikçe başka FreqTab yöntemleri de göreceğiz.
2.2. NSFG dağılımları#
Yeni bir veri kümesiyle çalışmaya başladığınızda kullanmayı planladığınız değişkenleri tek tek keşfetmenizi öneririm. Bunun için iyi bir başlangıç noktası frekans tablolarını incelemektir.
Örnek olarak National Survey of Family Growth (NSFG) verilerini ele alalım.
Önceki bölümde bu veri kümesini indirmiş, bir Pandas DataFrameine okumuş ve değişkenlerden birkaçını temizlemiştik.
Verileri yüklemek ve temizlemek için kullandığımız kod nsfg.py adlı modülde bulunuyor. Modülün kurulum talimatları bu bölümün notebook’unda yer alıyor.
Aşağıdaki hücreler veri dosyalarını indirir ve verileri okumak için ihtiyaç duyduğumuz statadict paketini kurar.
try:
import statadict
except ImportError:
%pip install statadict
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/nb/nsfg.py")
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemPreg.dct")
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemPreg.dat.gz")
Modülü içe aktarıp gebelik dosyasını şu şekilde okuyabiliriz.
from nsfg import read_fem_preg
preg = read_fem_preg()
Bu bölümdeki örneklerde canlı doğumla sonuçlanan gebeliklere odaklanacağız.
outcome değerinin 1 olduğu satırları seçmek için query yöntemini kullanabiliriz.
live = preg.query("outcome == 1")
query yöntemine geçirilen string içinde outcome gibi değişken adları, DataFramedeki sütun adlarını ifade eder.
Bu string, == gibi işleçleri ve 1 gibi işlenenleri de içerebilir.
Şimdi FreqTab.from_seq yöntemini kullanarak doğum ağırlığının libre bölümünü içeren birthwgt_lb değişkenindeki her niceliğin kaç kez görüldüğünü sayabiliriz.
name argümanı FreqTab nesnesine bir ad verir; bu ad, grafiği çizdiğimizde etiket olarak kullanılır.
ftab_lb = FreqTab.from_seq(live["birthwgt_lb"], name="doğum ağırlığı (libre)")
Dağılım şu şekilde görünür.
ftab_lb.bar()
decorate(xlabel="Libre", ylabel="Frekans")
Böyle bir dağılıma baktığımızda ilk dikkatimizi çeken biçimidir. Bu biçim, ünlü çan eğrisine; daha resmî adıyla normal dağılıma veya Gauss dağılımına benzer.
Dağılımın dikkat çeken diğer özelliği, en sık görülen değer olan moddur.
Modu bulmak için en yüksek frekansla ilişkili niceliği bulan idxmax yöntemini kullanabiliriz.
ftab_lb.idxmax()
np.float64(7.0)
FreqTab, aynı işlemi yapan mode adlı bir yöntem sunar.
ftab_lb.mode()
np.float64(7.0)
Bu dağılımın modu 7 libredir.
Başka bir örnek olarak doğum ağırlığının ons bölümünü içeren birthwgt_oz değişkeninin frekans tablosuna bakalım.
ftab_oz = FreqTab.from_seq(live["birthwgt_oz"], name="doğum ağırlığı (ons)")
ftab_oz.bar()
decorate(xlabel="Ons", ylabel="Frekans")
Doğada libre ve ons ayrımı olmadığı için birthwgt_oz değişkenindeki bütün değerlerin eşit olasılıklı olmasını, yani bu dağılımın tekdüze olmasını bekleyebiliriz.
Ancak 0 diğer niceliklerden daha sık, 1 ve 15 ise daha seyrek görünüyor. Bu durum, katılımcıların tam libreye yakın doğum ağırlıklarını yuvarladığını düşündürüyor.
Başka bir örnek olarak gebeliğin sonlandığı tarihte annenin yaşını içeren agepreg değişkeninin frekans tablosuna bakalım.
ftab_age = FreqTab.from_seq(live["agepreg"], name="anne yaşı")
NSFG’de yaş, yıl ve ay cinsinden kaydedildiği için burada incelediğimiz diğer dağılımlara göre daha fazla benzersiz değer vardır.
Bu nedenle bar yöntemine anahtar kelime argümanı olarak width=0.1 geçeceğiz. Bu argüman çubukların genişliğini, çok fazla üst üste gelmeyecek biçimde ayarlar.
ftab_age.bar(width=0.1)
decorate(xlabel="Yaş", ylabel="Frekans")
Dağılım çok kabaca çan biçimindedir; ancak sağa çarpıktır. Başka bir deyişle sağ kuyruğu sol kuyruğundan daha uzağa uzanır.
Son olarak gebeliğin hafta cinsinden süresini içeren prglngth değişkeninin frekans tablosuna bakalım.
xlim argümanı x ekseninin sınırlarını 20 ile 50 hafta aralığına ayarlar. Bu aralığın dışında fazla değer yoktur ve bunlar muhtemelen hatalıdır.
ftab_length = FreqTab.from_seq(live["prglngth"], name="gebelik süresi")
ftab_length.bar()
decorate(xlabel="Hafta", ylabel="Frekans", xlim=[20, 50])
Açık ara en sık görülen nicelik 39 haftadır. Sol kuyruk sağ kuyruktan daha uzundur. Erken doğum yaygındır; ancak gebelikler nadiren 43 haftayı aşar ve aştığında doktorlar çoğu zaman müdahale eder.
2.3. Aykırı değerler#
Frekans tablolarına bakarak dağılımın biçimini ve en sık görülen nicelikleri belirlemek kolaydır; ancak seyrek nicelikler her zaman görünmeyebilir. Devam etmeden önce, ölçüm veya kayıt hatası olabilecek ya da seyrek olayların doğru bildirimleri olabilecek aşırı değerleri, yani aykırı değerleri kontrol etmek iyi bir fikirdir.
Aykırı değerleri belirlemek için aşağıdaki fonksiyon bir FreqTab nesnesiyle n tam sayısını alır ve en küçük n niceliği frekanslarıyla birlikte seçmek üzere bir dilim indeksi kullanır.
def smallest(ftab, n=10):
return ftab[:n]
prglngth frekans tablosundaki en küçük 10 değer şunlardır.
smallest(ftab_length)
prglngth
0 1
4 1
9 1
13 1
17 2
18 1
19 1
20 1
21 2
22 7
Name: gebelik süresi, dtype: int64
Canlı doğumlara ait satırları seçtiğimiz için 10 haftadan kısa gebelik süreleri kesinlikle hatalıdır. En olası açıklama, gebelik sonucunun doğru kodlanmamış olmasıdır. 30 haftadan uzun süreler muhtemelen geçerlidir. 10 ile 30 hafta arasında kesin konuşmak zordur. Bazı nicelikler muhtemelen hatalıdır; bazılarıysa doğru kaydedilmiş erken doğumlardır.
Aşağıdaki fonksiyon bir FreqTab nesnesindeki en büyük değerleri seçer.
def largest(ftab, n=10):
return ftab[-n:]
Veri kümesindeki en uzun gebelik süreleri şunlardır.
largest(ftab_length)
prglngth
40 1116
41 587
42 328
43 148
44 46
45 10
46 1
47 1
48 7
50 2
Name: gebelik süresi, dtype: int64
Bu değerlerden bazıları da muhtemelen hatalıdır. Doktorların çoğu gebelik 41 haftayı aşarsa doğumun başlatılmasını önerdiği için 50 haftalık bir değerin doğru olması pek olası görünmüyor. Ancak kesinlikle hatalı değerlerle seyrek olayların doğru bildirimleri olabilecek değerler arasında net bir sınır yoktur.
Aykırı değerleri ele almanın en iyi yolu “alan bilgisine”, yani verilerin nereden geldiği ve ne anlama geldiği hakkındaki bilgilere bağlıdır. Ayrıca yapmayı planladığınız analize göre değişir.
Bu örnekte bizi harekete geçiren soru, ilk bebeklerin diğer bebeklerden daha erken veya daha geç doğmaya yatkın olup olmadığıdır. Bu nedenle az sayıdaki hatalı değerden çok fazla etkilenmeyen istatistikler kullanacağız.
2.4. İlk bebekler#
Şimdi ilk bebeklerle diğerlerinin gebelik süresi dağılımlarını karşılaştıralım.
İlk bebekleri ve diğerlerini temsil eden satırları seçmek için query yöntemini kullanabiliriz.
firsts = live.query("birthord == 1")
others = live.query("birthord != 1")
Ardından her grup için gebelik sürelerinin FreqTab nesnesini oluşturabiliriz.
ftab_first = FreqTab.from_seq(firsts["prglngth"], name="ilkler")
ftab_other = FreqTab.from_seq(others["prglngth"], name="diğerleri")
Aşağıdaki fonksiyon iki frekans tablosunu yan yana çizer.
def two_bar_plots(ftab1, ftab2, width=0.45):
ftab1.bar(align="edge", width=-width)
ftab2.bar(align="edge", width=width, alpha=0.5)
Grafikler şöyledir.
two_bar_plots(ftab_first, ftab_other)
decorate(xlabel="Hafta", ylabel="Frekans", xlim=[20, 50])
Dağılımların biçimlerinde veya aykırı değerlerinde belirgin bir fark yoktur. İlk olmayan bebeklerin daha büyük bir bölümü 39. haftada doğmuş gibi görünüyor; ancak veri kümesinde ilk olmayan bebeklerin sayısı daha fazladır. Bu nedenle sayımları doğrudan karşılaştırmamalıyız.
firsts["prglngth"].count(), others["prglngth"].count()
(np.int64(4413), np.int64(4735))
Dağılımların ortalamalarını karşılaştırdığımızda ilk bebeklerin ortalama olarak biraz daha geç doğduğu görülüyor.
first_mean = firsts["prglngth"].mean()
other_mean = others["prglngth"].mean()
first_mean, other_mean
(np.float64(38.60095173351461), np.float64(38.52291446673706))
Ancak fark yalnızca 0,078 hafta, yani yaklaşık 13 saattir.
diff = first_mean - other_mean
diff, diff * 7 * 24
(np.float64(0.07803726677754952), np.float64(13.11026081862832))
Gözlenen bu farkın birkaç olası nedeni vardır:
İlk bebeklerle diğerleri arasında ortalama gebelik süresi bakımından gerçek bir fark olabilir.
Bu veri kümesinde gördüğümüz fark, örnekleme sürecindeki, yani araştırma katılımcılarının seçimindeki yanlılığın sonucu olabilir.
Gözlenen fark bir ölçüm hatasından kaynaklanabilir. Örneğin kişilerin bildirdiği gebelik süreleri ilk bebekler veya diğer bebekler için daha doğru olabilir.
Gözlenen fark, örnekleme sürecindeki rastgele değişkenliğin sonucu olabilir.
Sonraki bölümlerde bu olası açıklamaları daha dikkatli değerlendireceğiz. Şimdilik sonucu olduğu gibi kabul edeceğiz: Bu veri kümesinde iki grubun gebelik süreleri arasında küçük bir fark vardır.
2.5. Etki büyüklüğü#
Bu tür bir farka bazen “etki” denir. Bir etkinin büyüklüğünü nicelleştirmenin birkaç yolu vardır. En basit yol, farkı mutlak olarak bildirmektir. Bu örnekte fark 0,078 haftadır.
Başka bir yol, farkı göreli olarak bildirmektir. Örneğin ilk gebeliklerin diğerlerinden ortalama %0,2 daha uzun olduğunu söyleyebiliriz.
diff / live["prglngth"].mean() * 100
np.float64(0.20237586646738304)
Başka bir seçenek, farklı nicelikler ve gruplar arasında karşılaştırılabilir biçimde etki büyüklüğünü nicelleştirmeyi amaçlayan bir istatistik olan standartlaştırılmış etki büyüklüğünü bildirmektir.
Standartlaştırma, farkı standart sapmanın bir katı olarak ifade etmek demektir. Bu nedenle aşağıdakine benzer bir hesaplama yapmak isteyebiliriz.
diff / live["prglngth"].std()
np.float64(0.028877623375210403)
Ancak standart sapmayı hesaplarken iki grubu birlikte kullandığımıza dikkat edin. Gruplar önemli ölçüde farklıysa bir araya getirildiklerinde hesaplanan standart sapma, her iki grubun kendi standart sapmasından daha büyük olur. Bu da etki büyüklüğünün küçük görünmesine yol açabilir.
Başka bir seçenek yalnızca bir grubun standart sapmasını kullanmaktır; fakat hangi grubun seçileceği açık değildir. İki standart sapmanın ortalamasını alabiliriz, ancak grupların büyüklükleri farklıysa bu yöntem gruplardan birine gereğinden fazla, diğerineyse gereğinden az ağırlık verir.
Yaygın bir çözüm, grupların varyanslarının ağırlıklı toplamı olan birleştirilmiş varyansın karekökünü, yani birleştirilmiş standart sapmayı kullanmaktır. Hesaplamaya varyanslarla başlayacağız.
group1, group2 = firsts["prglngth"], others["prglngth"]
v1, v2 = group1.var(), group2.var()
Grup büyüklüklerinin ağırlık olarak kullanıldığı ağırlıklı toplam şöyledir.
n1, n2 = group1.count(), group2.count()
pooled_var = (n1 * v1 + n2 * v2) / (n1 + n2)
Son olarak birleştirilmiş standart sapma şöyledir.
np.sqrt(pooled_var)
np.float64(2.7022108144953862)
Birleştirilmiş standart sapma, grupların standart sapmaları arasında yer alır.
firsts["prglngth"].std(), others["prglngth"].std()
(np.float64(2.7919014146687204), np.float64(2.6158523504392375))
Birleştirilmiş standart sapmayı kullanan standartlaştırılmış etki büyüklüğüne Cohen’in etki büyüklüğü denir. Aşağıdaki fonksiyon bu değeri hesaplar.
def cohen_effect_size(group1, group2):
diff = group1.mean() - group2.mean()
v1, v2 = group1.var(), group2.var()
n1, n2 = group1.count(), group2.count()
pooled_var = (n1 * v1 + n2 * v2) / (n1 + n2)
return diff / np.sqrt(pooled_var)
Ortalama gebelik süreleri arasındaki farkın etki büyüklüğü şöyledir.
cohen_effect_size(firsts["prglngth"], others["prglngth"])
np.float64(0.028879044654449834)
Bu örnekte fark 0,029 standart sapmadır ve küçüktür. Bir karşılaştırma yapmak gerekirse erkeklerle kadınlar arasındaki boy farkı yaklaşık 1,7 standart sapmadır.
2.6. Sonuçların raporlanması#
İlk bebeklerle diğerlerinin gebelik süreleri arasındaki farkı — gerçekten bir fark varsa — betimlemenin birkaç yolunu gördük. Peki bu sonuçları nasıl raporlamalıyız?
Yanıt, soruyu kimin sorduğuna bağlıdır. Bir bilim insanı ne kadar küçük olursa olsun her gerçek etkiyle ilgilenebilir. Bir doktor yalnızca pratik açıdan anlamlı, yani uygulamada önem taşıyan etkileri dikkate alabilir. Gebe bir kadınsa erken veya geç doğum olasılığı gibi kendisiyle ilgili sonuçlarla ilgilenebilir.
Sonuçları nasıl raporlayacağınız amaçlarınıza da bağlıdır. Bir etkinin önemini göstermek istiyorsanız farklılıkları vurgulayan özet istatistikleri seçebilirsiniz. Bir hastayı rahatlatmaya çalışıyorsanız farklılıkları bağlama yerleştiren istatistikleri tercih edebilirsiniz.
Elbette kararlarınıza meslek etiği de yön vermelidir. İkna edici olmanızda sakınca yoktur; hatta istatistiksel raporları ve görselleştirmeleri bir hikâyeyi açıkça anlatacak biçimde tasarlamalısınız. Ancak raporlarınızı dürüstçe hazırlamak, belirsizlikleri ve sınırlamaları kabul etmek için de elinizden geleni yapmalısınız.
2.7. Terimler#
dağılım: Bir veri kümesindeki değerler ve her değerin görülme sıklığı.
frekans tablosu: Değerleri frekanslarla eşleyen yapı.
frekans: Bir değerin örneklemde görülme sayısı.
çarpık: Bir dağılım simetrik değilse ve aşırı nicelikler bir yönde diğerinden daha uzağa uzanıyorsa çarpıktır.
mod: Bir örneklemde en sık görülen nicelik veya en sık görülen niceliklerden biri.
tekdüze dağılım: Bütün niceliklerin aynı frekansa sahip olduğu dağılım.
aykırı değer: Bir dağılımdaki aşırı nicelik.
standartlaştırılmış: Bir istatistik, farklı veri kümeleri ve alanlar arasında karşılaştırılabilir terimlerle ifade edilmişse standartlaştırılmıştır.
birleştirilmiş standart sapma: Ortak bir standart sapma hesaplamak için iki veya daha fazla gruptaki verileri birleştiren istatistik.
Cohen’in etki büyüklüğü: İki grubun ortalamaları arasındaki farkı nicelleştiren standartlaştırılmış istatistik.
pratik açıdan anlamlı: Bir etki uygulamada önem taşıyacak kadar büyükse pratik açıdan anlamlıdır.
2.8. Alıştırmalar#
Bu bölümdeki alıştırmalar için her kadın katılımcıya ait bir satır içeren NSFG kadın katılımcı dosyasını yükleyeceğiz. Verileri ve kod kitabını indirme talimatları bu bölümün notebook’unda yer alıyor.
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemResp.dct")
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemResp.dat.gz")
Kadın katılımcı dosyasının kod kitabına https://ftp.cdc.gov/pub/Health_Statistics/NCHS/Dataset_Documentation/NSFG/Cycle6Codebook-Female.pdf adresinden ulaşabilirsiniz.
nsfg.py modülü kadın katılımcı dosyasını okuyan, değişkenlerden bazılarını temizleyen ve bir DataFrame döndüren bir fonksiyon sağlar.
from nsfg import read_fem_resp
resp = read_fem_resp()
resp.shape
(7643, 3092)
Bu DataFrame 3.092 sütun içerir; ancak biz bunlardan yalnızca birkaçını kullanacağız.
2.8.1. Alıştırma 2.1#
Katılımcının ailesinin toplam gelirini 1 ile 14 arasında bir değerle kodlayan totincr değişkeniyle başlayacağız.
Her değerin hangi gelir düzeyini temsil ettiğini görmek için katılımcı dosyasının kod kitabını okuyabilirsiniz.
Bu değişkenin dağılımını temsil eden bir FreqTab nesnesi oluşturun ve dağılımı çubuk grafik olarak çizin.
2.8.2. Alıştırma 2.2#
Her katılımcının doğurduğu çocuk sayısını kaydeden parity sütununun frekans tablosunu oluşturun.
Bu dağılımın biçimini nasıl tanımlarsınız?
En büyük parity değerlerini bulmak için largest fonksiyonunu kullanın.
Hatalı olduğunu düşündüğünüz değer var mı?
2.8.3. Alıştırma 2.3#
Geliri daha yüksek veya daha düşük olan kadınların daha fazla çocuk doğurup doğurmadığını inceleyelim.
En yüksek gelir düzeyindeki (14. düzey) katılımcıları seçmek için query yöntemini kullanın.
Yalnızca yüksek gelirli katılımcılar için parity frekans tablosunu çizin.
Yüksek gelirli katılımcılarla diğer katılımcıların ortalama parity değerlerini karşılaştırın.
Bu fark için Cohen’in etki büyüklüğünü hesaplayın. Sonuç, ilk bebeklerle diğerlerinin gebelik süreleri arasındaki farkla nasıl karşılaştırılır?
Bu sonuçlar yüksek gelirli kişilerin daha az çocuk sahibi olduğunu mu gösteriyor, yoksa gözlenen fark için başka bir açıklama düşünebilir misiniz?
Orijinal eser: Think Stats: Exploratory Data Analysis in Python, 3rd Edition
Telif hakkı 2024 Allen B. Downey
Türkçe çeviri: ThinkStatsTr
Kod lisansı: MIT License
Metin lisansı: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International