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.

6. Olasılık yoğunluk fonksiyonları#

Önceki bölümde verileri; binom, Poisson, üstel ve normal dağılımların da aralarında bulunduğu kuramsal dağılımlarla modelledik.

Binom ve Poisson dağılımları kesiklidir; başka bir deyişle sonuçların, isabet ve ıska sayıları ya da atılan gol sayısı gibi birbirinden ayrı öğeler olması gerekir. Kesikli bir dağılımda her sonuç bir olasılık kütlesiyle ilişkilidir.

Üstel ve normal dağılımlar süreklidir; yani sonuçlar, olası değerler aralığındaki herhangi bir noktada bulunabilir. Sürekli bir dağılımda her sonuç bir olasılık yoğunluğu ile ilişkilidir. Olasılık yoğunluğu soyut bir kavramdır ve başlangıçta birçok kişiye zor gelir; ancak konuyu adım adım ele alacağız. İlk adım olarak dağılımları karşılaştırmayı yeniden düşünelim.

Bu notebook’u Colab’da çalıştırmak için buraya tıklayın.

Kod hücresi içeriğini gizle

from os.path import basename, exists


def download(url):
    filename = basename(url)
    if not exists(filename):
        from urllib.request import urlretrieve

        local, _ = urlretrieve(url, filename)
        print("İndirildi: " + local)


download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/nb/thinkstats.py")

Kod hücresi içeriğini gizle

try:
    import empiricaldist
except ImportError:
    %pip install empiricaldist

Kod hücresi içeriğini gizle

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from thinkstats import decorate

6.1. Dağılımların karşılaştırılması#

Önceki bölümde kesikli dağılımları karşılaştırırken olasılık kütle fonksiyonlarını (PMF’leri) göstermek için çubuk grafik kullandık. Sürekli dağılımları karşılaştırırken ise kümülatif dağılım fonksiyonlarını (CDF’leri) göstermek için çizgi grafiği kullandık.

Kesikli dağılımlar için CDF’leri de kullanabilirdik. Örneğin aşağıda, NSFG verilerindeki hane büyüklüğü dağılımı için iyi bir model olan lam=2.2 parametreli Poisson dağılımının PMF’si yer alıyor.

Aşağıdaki hücreler veri dosyalarını indirir ve verileri okumak için gereken statadict paketini kurar.

download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/nb/nsfg.py")
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemResp.dct")
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemResp.dat.gz")
try:
    import statadict
except ImportError:
    %pip install statadict

Katılımcılara ait veri dosyasını okumak için read_fem_resp işlevini kullanabiliriz.

from nsfg import read_fem_resp

resp = read_fem_resp()

Ardından 25 yaş ve üzerindeki kişilerin hane büyüklüklerini seçeceğiz.

older = resp.query("age >= 25")
num_family = older["numfmhh"]

Sonra yanıtların dağılımını temsil eden bir Pmf oluşturacağız.

from empiricaldist import Pmf

pmf_family = Pmf.from_seq(num_family, name="veri")

Aşağıda aynı ortalamaya sahip bir Poisson dağılımını temsil eden başka bir Pmf var.

from thinkstats import poisson_pmf

lam = 2.2
ks = np.arange(11)
ps = poisson_pmf(ks, lam)

pmf_poisson = Pmf(ps, ks, name="Poisson modeli")

Veri dağılımının Poisson modeliyle karşılaştırması ise şöyle görünüyor.

from thinkstats import two_bar_plots

two_bar_plots(pmf_family, pmf_poisson)
decorate(xlabel="Aile bireyi sayısı")
_images/4e3437f0d61a0d686d550b681535d288309b534d00e538b4e3b3d6cb3571fbee.png

PMF’leri karşılaştırdığımızda modelin bazı sapmalarla birlikte veriye iyi uyduğunu görebiliriz.

Bu sapmaların ne ölçüde önemli olduğunu anlamak için CDF’leri karşılaştırmak yararlı olabilir. Verinin ve modelin CDF’lerini hesaplamak üzere make_cdf işlevini kullanabiliriz.

cdf_family = pmf_family.make_cdf()
cdf_poisson = pmf_poisson.make_cdf()

Nasıl göründüklerine bakalım.

from thinkstats import two_cdf_plots

two_cdf_plots(cdf_poisson, cdf_family)
decorate(xlabel="Aile bireyi sayısı")
_images/dfd13b4fc8ae5b399e6978bcd9bbf8a9710bd1497e011d5b9fa0049e2df1e98d.png

CDF’leri karşılaştırdığımızda sapmalar daha az belirgindir; yine de dağılımların nerede ve nasıl farklılaştığını görebiliriz. PMF’ler küçük farklılıkları öne çıkarma eğilimindedir; CDF’ler bazen genel görünümü daha iyi yansıtır.

CDF’ler sürekli verilerle de iyi çalışır. Örnek olarak, NSFG gebelik dosyasında bulunan doğum ağırlıkları dağılımına yeniden bakalım.

download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemPreg.dct")
download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/2002FemPreg.dat.gz")
from nsfg import read_fem_preg

preg = read_fem_preg()
birth_weights = preg["totalwgt_lb"].dropna()

Aşağıda, önceki bölümde verilere normal model uydurmak için kullandığımız kod yer alıyor.

from scipy.stats import trimboth
from thinkstats import make_normal_model

trimmed = trimboth(birth_weights, 0.01)
cdf_model = make_normal_model(trimmed)

Verinin dağılımının normal modelle karşılaştırması şöyledir.

from empiricaldist import Cdf

cdf_birth_weight = Cdf.from_seq(birth_weights, name="örneklem")
two_cdf_plots(cdf_model, cdf_birth_weight, xlabel="Doğum ağırlığı (libre)")
_images/e39f64aeedf0b6f170829c2e525d23f16981009c872303c5292e3dcc9a3219df.png

Önceki bölümde gördüğümüz gibi normal model, en düşük ağırlıklı bebeklerin bulunduğu aralık dışında veriye iyi uyuyor.

Bana göre verileri bir modelle karşılaştırmanın genellikle en iyi yolu CDF’lerdir. Ancak CDF’lere aşina olmayan kitleler için bir seçenek daha vardır: olasılık yoğunluk fonksiyonları.

6.2. Olasılık yoğunluğu#

Normal dağılımın olasılık yoğunluk fonksiyonu (PDF) ile başlayacağız. Bu fonksiyon, mu ve sigma verildiğinde xs niceliklerinin yoğunluğunu hesaplar.

def normal_pdf(xs, mu, sigma):
    """Normal olasılık yoğunluk fonksiyonunu değerlendirir."""
    z = (xs - mu) / sigma
    return np.exp(-(z**2) / 2) / sigma / np.sqrt(2 * np.pi)

mu ve sigma için kırpılmış doğum ağırlıklarının ortalamasını ve standart sapmasını kullanacağız.

m, s = np.mean(trimmed), np.std(trimmed)

Şimdi normal_pdf işlevini bir ağırlık aralığı üzerinde değerlendireceğiz.

low = m - 4 * s
high = m + 4 * s
qs = np.linspace(low, high, 201)
ps = normal_pdf(qs, m, s)

Ardından grafiğini çizeceğiz.

plt.plot(qs, ps, label="normal model", ls=":", color="gray")
decorate(xlabel="Doğum ağırlığı (libre)", ylabel="Yoğunluk")
_images/9294e70258ea8c5e6a318e1ba4eed5389fb579b78cd11cdbb9789869644d0006.png

Sonuç, normal dağılımın ayırt edici özelliği olan çan eğrisine benziyor.

normal_pdf işlevini değerlendirdiğimizde sonuç bir olasılık yoğunluğudur. Örneğin aşağıda yoğunluk fonksiyonu, yoğunluğun en yüksek olduğu ortalama değerinde değerlendirilmiştir.

normal_pdf(m, m, s)
np.float64(0.32093416297880123)

Bir olasılık yoğunluğu tek başına pek bir anlam taşımaz; en önemlisi, bir olasılık değildir. Rastgele seçilen bir doğum ağırlığının m değerine eşit olma olasılığının %32 olduğunu söylemek yanlış olur. Aslında bir doğum ağırlığının gerçekten, tam ve kesin olarak m değerine —ya da başka herhangi bir belirli değere— eşit olma olasılığı sıfırdır.

Bununla birlikte, eğrinin altında kalan alanı hesaplayarak bir sonucun iki değer arasındaki bir aralığa düşme olasılığını bulmak için olasılık yoğunluklarını kullanabiliriz.

Bunu normal_pdf işleviyle yapabiliriz; ancak thinkstats modülünde tanımlanan NormalPdf sınıfını kullanmak daha elverişlidir. NSFG veri kümesindeki doğum ağırlıklarıyla aynı ortalama ve standart sapmaya sahip bir NormalPdf nesnesini şöyle oluştururuz.

from thinkstats import NormalPdf

pdf_model = NormalPdf(m, s, name="normal model")
pdf_model
NormalPdf(7.280883100022579, 1.2430657948614345, name='normal model')

Bu nesneyi bir işlev gibi çağırırsak normal PDF’yi değerlendirir.

pdf_model(m)
np.float64(0.32093416297880123)

PDF’nin altında kalan alanı hesaplamak için aşağıdaki işlevi kullanabiliriz. İşlev bir NormalPdf nesnesi ile low ve high aralık sınırlarını alır. low ile high arasındaki eşit aralıklı niceliklerde normal PDF’yi değerlendirir ve eğrinin altındaki alanı tahmin etmek için SciPy’nin simpson işlevini kullanır (simpson adı, Simpson yöntemi denen algoritmayı kullanmasından gelir).

from scipy.integrate import simpson


def area_under(pdf, low, high):
    qs = np.linspace(low, high, 501)
    ps = pdf(qs)
    return simpson(y=ps, x=qs)

Grafikteki en düşük noktadan en yüksek noktaya kadar eğrinin altında kalan alanı hesaplarsak sonuç 1’e yakındır.

area_under(pdf_model, 2, 12)
np.float64(0.9999158086616793)

Aralığı negatif sonsuzdan pozitif sonsuza genişletirsek toplam alan tam olarak 1 olur.

0’dan —ya da ortalamanın çok altındaki herhangi bir değerden— başlayarak 8,5 pound veya daha düşük doğum ağırlıklarının oranını hesaplayabiliriz.

area_under(pdf_model, 0, 8.5)
np.float64(0.8366380335513807)

“Belirli bir değerden küçük veya ona eşit olanların oranı”nın CDF’nin tanımı olduğunu hatırlayabilirsiniz. Dolayısıyla aynı sonucu normal dağılımın CDF’sini kullanarak da hesaplayabiliriz.

from scipy.stats import norm

norm.cdf(8.5, m, s)
np.float64(0.8366380358092718)

Benzer şekilde, 6 ile 8 pound arasındaki doğum ağırlıklarının oranını hesaplamak için yoğunluk eğrisinin altındaki alanı kullanabiliriz.

area_under(pdf_model, 6, 8)
np.float64(0.5671317752927691)

Aynı sonucu, CDF ile 8’den küçük olanların oranını hesaplayıp bundan 6’dan küçük olanların oranını çıkararak da elde edebiliriz.

norm.cdf(8, m, s) - norm.cdf(6, m, s)
np.float64(0.5671317752921801)

Dolayısıyla CDF, PDF eğrisinin altında kalan alandır. Kalkülüs biliyorsanız aynı şeyi şöyle de söyleyebiliriz: CDF, PDF’nin integralidir. Tersine PDF de CDF’nin türevidir.

6.3. Üstel PDF#

Olasılık yoğunluğu kavramını daha iyi anlamak için başka bir örnek görmek yararlı olabilir. Önceki bölümde bir hokey maçındaki ilk gole kadar geçen süreyi modellemek için üstel dağılım kullandık. Aşağıdaki işlevle üstel CDF’yi hesapladık; burada lam, zaman birimi başına gol oranıdır.

def exponential_cdf(x, lam):
    """Üstel CDF’yi hesaplar.

    x: kayan noktalı sayı veya kayan noktalı sayı dizisi
    lam: oran parametresi

    döndürür: kayan noktalı sayı veya kümülatif olasılıklardan oluşan NumPy dizisi
    """
    return 1 - np.exp(-lam * x)

Üstel dağılımın PDF’sini şöyle hesaplayabiliriz.

def exponential_pdf(x, lam):
    """Üstel PDF’yi değerlendirir.

    x: kayan noktalı sayı veya kayan noktalı sayı dizisi
    lam: oran parametresi

    döndürür: kayan noktalı sayı veya olasılık yoğunluklarından oluşan NumPy dizisi
    """
    return lam * np.exp(-lam * x)

thinkstats, üstel PDF’yi hesaplamak için bu işlevi kullanan bir ExponentialPdf nesnesi sağlar. Maç başına 6 gol oranlı bir üstel dağılımı temsil etmek üzere bu nesneyi kullanabiliriz.

from thinkstats import ExponentialPdf

lam = 6
pdf_expo = ExponentialPdf(lam, name="üstel model")
pdf_expo
ExponentialPdf(6, name='üstel model')

ExponentialPdf, PDF’nin grafiğini çizmek için kullanabileceğimiz bir plot yöntemi sağlar. Burada zaman biriminin önceki bölümdeki gibi saniye değil, maç olduğuna dikkat edin.

qs = np.linspace(0, 1.5, 201)
pdf_expo.plot(qs, ls=":", color="gray")
decorate(xlabel="Zaman (maç)", ylabel="Yoğunluk")
_images/3a264eef7fd1095198320ed17739d90ea8f17262074839b6cd84db0b314e028b.png

Y eksenine baktığınızda bazı yoğunlukların 1’den büyük olduğunu fark edebilirsiniz; bu, olasılık yoğunluğunun olasılık olmadığını anımsatır. Ancak bir yoğunluk eğrisinin altında kalan alan olasılıktır ve bu nedenle hiçbir zaman 1’den büyük olmamalıdır.

Bu eğrinin altında 0 ile 1,5 maç arasında kalan alanı hesaplarsak sonucun 1’e yakın olduğunu doğrulayabiliriz.

area_under(pdf_expo, 0, 1.5)
np.float64(0.999876590779019)

Aralığı çok daha ileriye uzatırsak sonuç 1’den biraz büyük çıkar; bunun nedeni alanı sayısal olarak yaklaşık hesaplamamızdır. Matematiksel olarak değer tam 1’dir; bunu üstel CDF ile doğrulayabiliriz.

from thinkstats import exponential_cdf

exponential_cdf(7, lam)
np.float64(1.0)

Herhangi bir aralıkta gol olma olasılığını hesaplamak için yoğunluk eğrisinin altındaki alanı kullanabiliriz. Örneğin aşağıda, 60 dakikalık bir maçın ilk dakikasında gol olma olasılığı yer alıyor.

area_under(pdf_expo, 0, 1 / 60)
np.float64(0.09516258196404043)

Aynı sonucu üstel CDF’yi kullanarak hesaplayabiliriz.

exponential_cdf(1 / 60, lam)
np.float64(0.09516258196404048)

Özetle bir PDF’yi değerlendirdiğimizde sonuç, olasılık olmayan bir olasılık yoğunluğudur. Buna karşılık PDF’nin altında kalan alanı hesaplarsak sonuç, bir niceliğin belirli bir aralığa düşme olasılığıdır. Aynı olasılığı, aralığın başlangıç ve bitiş noktalarında CDF’yi değerlendirip aradaki farkı hesaplayarak da bulabiliriz.

6.4. PMF’ler ile PDF’lerin karşılaştırılması#

Bir örneklemin PMF’sini kuramsal bir modelin PDF’siyle karşılaştırmak yaygın bir hatadır. Örneğin doğum ağırlıkları dağılımını normal bir modelle karşılaştırmak istediğimizi varsayalım. Aşağıda veri dağılımını temsil eden bir Pmf bulunuyor.

pmf_birth_weight = Pmf.from_seq(birth_weights, name="veri")

Aynı ortalama ve standart sapmaya sahip normal dağılımın PDF’sini temsil eden pdf_model zaten elimizde. İkisini aynı eksenlerde çizersek ne olduğuna bakalım.

pdf_model.plot(ls=":", color="gray")
pmf_birth_weight.plot()

decorate(xlabel="Doğum ağırlığı (libre)", ylabel="PMF? Yoğunluk?")
_images/1fbff5ec01b867abe54fc11658433a876a16a90995ff710ac8bc3cc1fbcc32b1.png

Pek iyi sonuç vermiyor. Bunun bir nedeni aynı birimlerde olmamalarıdır. PMF olasılık kütlelerini, PDF ise olasılık yoğunluklarını içerir; dolayısıyla bunları karşılaştıramayız ve aynı eksenlerde çizmemeliyiz.

Sorunu çözmeye yönelik ilk deneme olarak PDF’yi kesikli bir nokta kümesinde değerlendirip normal dağılıma yaklaşan bir Pmf oluşturabiliriz. NormalPdf, bunu yapan bir make_pmf yöntemi sağlar.

pmf_model = pdf_model.make_pmf()

Sonuç, olasılık kütleleri içeren normalize edilmiş bir Pmf’dir; dolayısıyla en azından veri PMF’siyle aynı eksenlerde çizebiliriz.

pmf_model.plot(ls=":", color="gray")
pmf_birth_weight.plot()

decorate(xlabel="Doğum ağırlığı (libre)", ylabel="PMF")
_images/afae2efea29cc03b9adae37628f87118359d714e4c261b502e80a6d83733bb81.png

Ancak bu hâlâ dağılımları karşılaştırmanın iyi bir yolu değildir. Sorunlardan biri, iki Pmf nesnesinin farklı sayıda nicelik içermesi ve pmf_birth_weight içindeki niceliklerin eşit aralıklı olmamasıdır; bu nedenle olasılık kütleleri gerçekte karşılaştırılabilir değildir.

len(pmf_model), len(pmf_birth_weight)
(201, 184)

Diğer sorun, verinin Pmf’sinin gürültülü olmasıdır. Öyleyse başka bir şey deneyelim.

6.5. Çekirdek yoğunluk tahmini#

Modelden bir PMF üretmek yerine veriden bir PDF üretebiliriz. Bunun nasıl çalıştığını göstermek için verinin küçük bir örneklemiyle başlayacağım.

# Her çalıştırmada aynı sonuçları almak için rastgelelik tohumunu ayarla
np.random.seed(3)
n = 10
sample = birth_weights.sample(n)

Bu örneklemin Pmf’si şöyle görünür.

for weight in sample:
    pmf = Pmf.from_seq([weight]) / n
    pmf.bar(width=0.08, alpha=0.5)

xlim = [1.5, 12.5]
decorate(xlabel="Doğum ağırlığı (libre)", ylabel="PMF", xlim=xlim)
_images/4dfaf1f09b90ee74e45739ae24254940277bafaa308e56cd0eea6653106f1a36.png

Bu gösterim biçimi veriyi kesikliymiş gibi ele alır; böylece her olasılık kütlesi tek bir noktada yığılır. Oysa doğum ağırlığı gerçekte süreklidir, yani ölçümlerin arasındaki nicelikler de mümkündür. Bu olasılığı, her kesikli olasılık kütlesinin yerine aşağıdaki gibi sürekli bir olasılık yoğunluğu koyarak temsil edebiliriz.

qs = np.linspace(2, 12, 201)

for weight in sample:
    ps = NormalPdf(weight, 0.75)(qs) / n
    plt.plot(qs, ps, alpha=0.5)

decorate(xlabel="Doğum ağırlığı (libre)", ylabel="PDF", xlim=xlim)
_images/42afc17da0bb511e509c9edfd0ef5a01dfb5f695905631c933397b5e025515ac.png

Örneklemdeki her ağırlık için, gözlenen ağırlığı ortalama kabul eden bir NormalPdf oluşturuyoruz; şimdi bunları toplayalım.

low_ps = np.zeros_like(qs)

for weight in sample:
    ps = NormalPdf(weight, 0.75)(qs) / n
    high_ps = low_ps + ps
    plt.fill_between(qs, low_ps, high_ps, alpha=0.5, lw=1, ec="white")
    low_ps = high_ps

decorate(xlabel="Doğum ağırlığı (libre)", ylabel="PDF", xlim=xlim)
_images/6af7400bb34454b4c493e435b4ba1f1fd7f33642114833458811b3fba0e08032.png

Her veri noktası için olasılık yoğunluklarını topladığımızda sonuç, tüm örneklemin olasılık yoğunluğuna ilişkin bir tahmindir. Bu sürece çekirdek yoğunluk tahmini ya da KDE denir. Bu bağlamda “çekirdek”, topladığımız küçük yoğunluk fonksiyonlarından biridir. Kullandığımız çekirdekler Gauss dağılımları olarak da bilinen normal dağılımlar olduğundan, daha özel olarak bir Gauss KDE’si hesapladığımızı söyleyebiliriz.

SciPy, bu algoritmayı uygulayan gaussian_kde adlı bir işlev sağlar. Doğum ağırlıkları dağılımını tahmin etmek için bu işlevi şöyle kullanabiliriz.

from scipy.stats import gaussian_kde

kde = gaussian_kde(birth_weights)

Sonuç, tahmin edilen PDF’yi temsil eden bir nesnedir; bu nesneyi işlev gibi çağırarak değerlendirebiliriz.

ps = kde(qs)

Sonuç şöyle görünür.

plt.plot(qs, ps)

decorate(xlabel="Doğum ağırlığı (libre)", ylabel="Yoğunluk")
_images/50fb9a9a122d78dbb02a2f909fa9b97f1afa25c740b21911df3d1ab212045a0f.png

thinkstats, gaussian_kde sonucunu ve yoğunluğun nerede değerlendirileceğini belirten bir tanım kümesini alan bir Pdf nesnesi sağlar. Bu nesneyi şöyle oluştururuz.

from thinkstats import Pdf

domain = np.min(birth_weights), np.max(birth_weights)
kde_birth_weights = Pdf(kde, domain, name="veri")

Pdf, örneklemin tahmin edilen PDF’sini normal dağılımın PDF’siyle karşılaştırmak için kullanabileceğimiz bir plot yöntemi sağlar.

pdf_model.plot(ls=":", color="gray")
kde_birth_weights.plot()

decorate(xlabel="Doğum ağırlığı (libre)", ylabel="Yoğunluk")
_images/b9395e7098c75b2e7e6a42f965eb50c8bbbebc34a973f4668ec560d801d522c2.png

Çekirdek yoğunluk tahmini, bir veri kümesinin dağılımını kuramsal bir modelle karşılaştırmayı mümkün kılar ve bazı kitleler için bu karşılaştırmayı görselleştirmenin iyi bir yoludur. Ancak CDF’lere aşina olan kitleler için CDF’leri karşılaştırmak çoğu zaman daha iyidir.

6.6. Dağılım çerçevesi#

Bu noktada dağılımları temsil etmek için eksiksiz bir araç takımımız var: PMF’ler, CDF’ler ve PDF’ler. Aşağıdaki şekil bu gösterimleri ve birinden diğerine geçişleri gösteriyor. Örneğin elimizde bir Pmf varsa olasılıkların kümülatif toplamını hesaplamak için cumsum işlevini kullanabilir ve aynı dağılımı temsil eden bir Cdf elde edebiliriz.

https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/figs/distribution_framework.png

Bu geçişleri göstermek için, açıklamasına göre “Avustralya’nın Brisbane kentindeki bir hastanede 24 saatlik bir süre içinde doğan 44 bebeğin her biri için doğum saati, cinsiyet ve doğum ağırlığını içeren” yeni bir veri kümesi kullanacağız. Verileri indirme yönergeleri bu bölümün notebook’unda yer alıyor.

Dosyadaki bilgilere göre:

Kaynak: Steele, S. (21 Aralık 1997), “Babies by the Dozen for Christmas: 24-Hour Baby Boom,” The Sunday Mail (Brisbane), s. 7.

VERİNİN ÖYKÜSÜ: Avustralya’nın Queensland eyaletindeki Brisbane kentinde bulunan Mater Mothers’ Hospital’da 18 Aralık 1997 günü, 24 saatlik bir süre içinde yeni bir rekor olan kırk dört bebek doğdu. The Sunday Mail, 44 bebeğin her biri için doğum saatini, çocuğun cinsiyetini ve gram cinsinden doğum ağırlığını kaydetti.

Bu veri kümesiyle ilgili ek bilgi, Journal of Statistics Education dergisindeki (Dunn 1999) “Datasets and Stories” yazısının “A Simple Dataset for Demonstrating Common Distributions” bölümünde bulunabilir.

https://jse.amstat.org/datasets/babyboom.txt adresinden indirilmiştir.

download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/babyboom.dat")

Veriyi şöyle okuyabiliriz.

from thinkstats import read_baby_boom

boom = read_baby_boom()
boom.head()
time sex weight_g minutes
0 5 1 3837 5
1 104 1 3334 64
2 118 2 3554 78
3 155 2 3838 115
4 257 2 3625 177

minutes sütunu “her doğum için gece yarısından itibaren geçen dakika sayısını” kaydeder. Bu nedenle ardışık doğumlar arasındaki süreyi hesaplamak için diff yöntemini kullanabiliriz.

diffs = boom["minutes"].diff().dropna()

Doğumlar günün herhangi bir dakikasında eşit olasılıkla gerçekleşiyorsa bu aralıkların üstel dağılım izlemesini bekleriz. Gerçekte bu varsayım tam olarak doğru değildir; yine de üstel dağılım veri için iyi bir model olabilir.

Bunu öğrenmek için aralıkların dağılımını temsil eden bir Pmf oluşturarak başlayacağız.

pmf_diffs = Pmf.from_seq(diffs, name="veri")
pmf_diffs.bar(width=1)

decorate(xlabel="Aralık (dakika)", ylabel="PMF")
_images/4fd27fac0a6f12920883d7847397769cdaaba273d9ab125b8900f5f0c8412689.png

Ardından kümülatif olasılıkları hesaplayıp bir Cdf nesnesinde saklamak için make_cdf işlevini kullanabiliriz.

cdf_diffs = pmf_diffs.make_cdf()
cdf_diffs.step()

decorate(xlabel="Aralık (dakika)", ylabel="CDF")
_images/0d6476ccababdfef6784656838cfe45f500c2f0f6d070dd7c466cb0c54edc749.png

Pmf ile Cdf, biri verildiğinde diğerini hesaplayabilmemiz bakımından eşdeğerdir. Bunu göstermek için bir Cdf içindeki ardışık olasılıkların farklarını hesaplayıp bir Pmf döndüren make_pmf yöntemini kullanacağız.

pmf_diffs2 = cdf_diffs.make_pmf()

Sonucun özgün Pmf ile aynı olması gerekir; ancak küçük kayan nokta hataları bulunabilir. Sonucun özgün Pmf’ye yakın olduğunu denetlemek için allclose işlevini kullanabiliriz.

np.allclose(pmf_diffs, pmf_diffs2)
True

Gerçekten de öyle.

Bir Pmf’den yoğunluk fonksiyonunu tahmin etmek için gaussian_kde işlevini, Pmf’deki olasılıkları ağırlık olarak vererek çağırabiliriz.

kde = gaussian_kde(pmf_diffs.qs, weights=pmf_diffs.ps)

Sonuçları çizmek için kde işleviyle bir Pdf nesnesi oluşturup plot yöntemini çağırabiliriz.

domain = np.min(pmf_diffs.qs), np.max(pmf_diffs.qs)
kde_diffs = Pdf(kde, domain=domain, name="tahmin edilen yoğunluk")

kde_diffs.plot(ls=":", color="gray")
decorate(xlabel="Aralık (dakika)", ylabel="Yoğunluk")
_images/ccaaf4b3135599c084fe32aabb0466385aa9703b000954840b27e59b6f1b32bb.png

Tahmin edilen yoğunluğun üstel bir modeli izleyip izlemediğini görmek için verilerle aynı ortalamaya sahip bir ExponentialCdf oluşturabiliriz.

from thinkstats import ExponentialCdf

m = diffs.mean()
lam = 1 / m
cdf_model = ExponentialCdf(lam, name="üstel CDF")

Verinin CDF’siyle karşılaştırıldığında şöyle görünür.

cdf_model.plot(ls=":", color="gray")
cdf_diffs.step()

decorate(xlabel="Aralık (dakika)", ylabel="CDF")
_images/6a6ff06b02e2408c701069ae7e70a7107b1f3fa6a940d394097c38d8b5e8cf5f.png

Üstel model, verinin CDF’sine iyi uyuyor.

Bir ExponentialCdf verildiğinde CDF’yi kesiklileştirmek için make_cdf işlevini kullanabiliriz; yani CDF’yi eşit aralıklı bir nicelik dizisinde değerlendirerek kesikli bir yaklaşım oluşturabiliriz.

qs = np.linspace(0, 160)
discrete_cdf_model = cdf_model.make_cdf(qs)
discrete_cdf_model.step(color="gray")

decorate(xlabel="Aralık (dakika)", ylabel="CDF")
_images/07122da0329a866f053b6229cc70c0ab0e9d1bfd7117d4d9fdb175e400214003.png

Son olarak kesikli bir CDF’den sürekli CDF’ye geçmek için basamakların arasına değer biçebiliriz. step yöntemi yerine plot yöntemini kullandığımızda gördüğümüz de budur.

discrete_cdf_model.plot(color="gray")

decorate(xlabel="Aralık (dakika)", ylabel="CDF")
_images/9d62ccd832073071ce357b1f7cddda0359623d8c3a5dec9cc35baedd66567015.png

Sonuç olarak PDF, sürekli bir CDF’nin türevidir; CDF ise PDF’nin integralidir.

Bunu göstermek için SymPy ile üstel bir dağılımın CDF’sini tanımlayıp türevini hesaplayabiliriz.

import sympy as sp

x = sp.Symbol("x", real=True, positive=True)
λ = sp.Symbol("λ", real=True, positive=True)

cdf = 1 - sp.exp(-λ * x)
cdf
\[\displaystyle 1 - e^{- x λ}\]
pdf = sp.diff(cdf, x)
pdf
\[\displaystyle λ e^{- x λ}\]

Sonucu integre edersek CDF’yi geri elde ederiz; ancak bu süreçte integral sabitini kaybederiz.

sp.integrate(pdf, x)
\[\displaystyle - e^{- x λ}\]

Bu örnek, PMF’leri, CDF’leri ve PDF’leri temsil etmek için Pmf, Cdf ve Pdf nesnelerini nasıl kullandığımızı gösteriyor ve her birinden diğerlerine dönüştürme sürecini açıklıyor.

6.7. Sözlük#

  • sürekli (continuous): Bir nicelik, sayı doğrusundaki bir aralıkta herhangi bir değeri alabiliyorsa süreklidir. Ağırlık, uzaklık ve zaman gibi dünyada ölçtüğümüz şeylerin çoğu süreklidir.

  • kesikli (discrete): Bir nicelik, tam sayılar veya kategoriler gibi sınırlı bir değer kümesini alabiliyorsa kesiklidir. Kategorik değişkenlerin yanı sıra kesin sayımlar da kesiklidir.

  • olasılık yoğunluk fonksiyonu (probability density function, PDF): Yoğunluğun —olasılığın değil— sürekli bir değişkenin değerleri boyunca nasıl dağıldığını gösteren fonksiyon. Belirli bir aralıkta PDF’nin altında kalan alan, değişkenin o aralığa düşme olasılığını verir.

  • olasılık yoğunluğu (probability density): Bir PDF’nin belirli bir noktadaki değeri. Tek başına olasılık değildir, ancak olasılık hesaplamak için kullanılabilir.

  • çekirdek yoğunluk tahmini (kernel density estimation, KDE): Bir örnekleme dayanarak PDF tahmin etme yöntemi.

  • kesiklileştirmek (discretize): Sürekli bir niceliği, aralığını kesikli düzeylere veya kategorilere bölerek yaklaşık olarak temsil etmek.

6.8. Alıştırmalar#

6.8.1. Alıştırma 6.1#

Dünya Kupası futbolunda ilk gole kadar geçen sürenin, maç başına lam=2.5 gol oranlı bir üstel dağılımla iyi modellendiğini varsayın. Bu dağılımı temsil eden bir ExponentialPdf oluşturun ve ilk gole kadar geçen sürenin yarım maçtan kısa olma olasılığını area_under ile hesaplayın. Ardından aynı olasılığı bir ExponentialCdf ile hesaplayın ve sonuçların tutarlı olduğunu denetleyin.

İlk golün maçın ikinci yarısında atılma olasılığını ExponentialPdf ile hesaplayın. Ardından aynı olasılığı bir ExponentialCdf ile hesaplayın ve sonuçların tutarlı olduğunu denetleyin.

6.8.2. Alıştırma 6.2#

Blue Man Group’a katılmak için 5 fit 10 inç ile 6 fit 1 inç, yani yaklaşık 178 ile 185 santimetre arasında boyu olan bir erkek olmanız gerekir. Amerika Birleşik Devletleri’ndeki yetişkin erkek nüfusunun ne kadarının bu koşulu karşıladığına bakalım.

BRFSS’deki erkek katılımcıların boyları, ortalaması 178 cm ve standart sapması 7 cm olan normal bir dağılımla iyi modellenir.

download("https://github.com/TerekliTahaBerk/thinkstatstr/raw/v3/data/CDBRFS08.ASC.gz")
from thinkstats import read_brfss

brfss = read_brfss()
male = brfss.query("sex == 1")
heights = male["htm3"].dropna()
from scipy.stats import trimboth

trimmed = trimboth(heights, 0.01)
m, s = np.mean(trimmed), np.std(trimmed)
m, s
(np.float64(178.10278947124948), np.float64(7.017054887136003))

Aşağıda kırpılmış verilerle aynı ortalama ve standart sapmaya sahip normal dağılımı temsil eden bir NormalCdf nesnesi bulunuyor.

from thinkstats import NormalCdf

cdf_normal_model = NormalCdf(m, s, name='normal model')

Verinin CDF’siyle karşılaştırması şöyledir.

cdf_height = Cdf.from_seq(heights, name="veri")
cdf_normal_model.plot(ls=":", color="gray")
cdf_height.step()

xlim = [140, 210]
decorate(xlabel="Boy (cm)", ylabel="CDF", xlim=xlim)
_images/5629fc5bb168895e9082bea56aceef78e28afdfb7904c3dbef902a77ac8ce050.png

Erkek boyunun PDF’sine yaklaşan bir Pdf oluşturmak için gaussian_kde işlevini kullanın. İpucu: Tahmin edilen yoğunluğun pürüzsüzlüğünü denetlemek için kullanılabilen bw_method argümanını inceleyin. Tahmin edilen yoğunluğun grafiğini çizin ve ortalaması m, standart sapması s olan bir NormalPdf ile karşılaştırın.

Normal modelde 178 ile 185 santimetre arasında olan kişilerin oranını bir NormalPdf ve area_under kullanarak hesaplayın. Aynı oranı bir NormalCdf ile hesaplayıp sonuçların tutarlı olduğunu denetleyin. Son olarak veri kümesindeki kişilerin ne kadarının aynı aralıkta olduğunu görmek için verinin ampirik Cdf’sini kullanın.

Think Stats: Python ile Keşifsel Veri Analizi, 3. Baskı

Telif hakkı 2024 Allen B. Downey

Kod lisansı: MIT Lisansı

Metin lisansı: Creative Commons Atıf-Gayriticari-AynıLisanslaPaylaş 4.0 Uluslararası