A folytonos eloszlású véletlen változók sűrűségfüggvényének becslésére számos módszer létezik. Ezeket két fő csoportra lehet osztani: a paraméteres és a nem-paraméteres becslési eljárásokra.
Paraméteres és nem-paraméteres valószínűségi sűrűségbecslés
A paraméteres becslési módszerek feltételezik, hogy az adatok egy előre ismert eloszlást (pl. normális eloszlás, háromszög eloszlás, exponenciális eloszlás stb.) követnek. Ezért itt a cél az adott eloszlás jellemző paramétereinek (pl. átlag, szórás) meghatározása a minták alapján. A paraméteres becslési eljárásokat tehát akkor érdemes használni, ha az eloszlás elvi megfontolások alapján biztosan tudható, vagy ésszerűen feltételezhető.
Például, ha vannak mintáink olyan két független folytonos valószínűségi változóból, amelyek egyenletes eloszlásúak adott intervallumban, és szeretnénk megtudni, hogy a két változó összege milyen eloszlású, akkor alkalmazhatunk paraméteres becslési eljárást, mert elvi alapon tudjuk, azaz levezethető, hogy két ilyen véletlen változó összegének sűrűségfüggvénye háromszög alakú, más szóval a két változó összege háromszögeloszlást követ.
Ezzel szemben a nem-paraméteres becslési módszerek nem tesznek feltételezéseket az adatok eloszlására vonatkozóan, hanem közvetlenül az adatokból becsülik a valószínűségi sűrűségfüggvény alakját. Ezért akkor érdemes használni ezeket, ha az adatok alapjául szolgáló eloszlás nem ismert vagy összetett és így a nevezetes eloszlásokkal nem modellezhető.
Számos nem-paraméteres becslési módszer létezik, amelyek közül talán a legismertebb a hisztogram. A hisztogramok készítésénél az adatokat bizonyos számú intervallumra, ú.n. osztályközre (bin) osztjuk, és minden intervallumhoz megszámoljuk, hány adatpont esik bele. A kapott értékek alapján oszlopokat rajzolunk, amelyek magassága arányos az adott intervallumba eső sűrűséggel. Ha az oszlopok magasságát úgy állítjuk be, hogy az oszlopok területének összege 1 legyen, egy sűrűségfüggvényt kapunk.
Bár a hisztogramot egyszerű megérteni és ábrázolni (ezért is elterjedt) vannak hátrányai. Például:
- a becslés minőségét erősen befolyásolja az osztályközök választott hossza. Túl széles osztályközök esetén az eloszlás finomabb részletei kisimulnak vagy el lesznek fedve. Pl. több módusz (több helyi maximum) esetén nem minden maximumérték látszik. Túl keskeny osztályközök viszont „zajos” becslést eredményeznek, ami sok csúcsot és völgyet mutathat. A véletlen ingadozásokból fakadó csúcsok azonban félrevezető képet festhetnek az alapadatok tényleges eloszlásáról.
- A hisztogram egy lépcsős függvény, amely állandó értéket vesz fel az egyes osztályközökön belül. Így a becslés nem folytonos, ami ellentétes a legtöbb valószínűségi sűrűségfüggvény folytonos jellegével. Ez vizuálisan zavaró lehet, és nem lesz megfelelő olyan alkalmazásokhoz, ahol a folytonosság fontos szempont.
- Az eredeti adatok elvesztik egyéni értéküket, és csak a csoportba tartozásuk marad meg. Például, ha két adatpont egyazon osztályközbe esik, a hisztogram már nem tesz különbséget közöttük, ami információvesztést okozhat.
Ezen hiányosságok kiküszöbölésére más nem-paraméteres becslési módszereket is kifejlesztettek, amelyek folytonos sűrűségfüggvényt eredményeznek és a finomabb részleteket is képesek megmutatni. Ilyen például a széles körben alkalmazott magfüggvényes sűrűségbecslés (kernel density estimation), amelyre az angol neve alapján általában a KDE betűszóval hivatkoznak.
A következőkben a KDE módszerrel foglalkozunk. Egyrészt azért, mert az elvi megközelítése hasonló, mint ami a kernelalapú interpolációról szóló korábbi bejegyzésben szerepel. De főleg azért, mert a Python 3.13 verziótól a szabványos könyvtár statistics modulja a sűrűségbecsléshez e módszert támogatja a kde() nevű függvénnyel. Így tehát már külső csomagok használata nélkül is lehet ezt a becslési eljárást alkalmazni.
Felmerülhet a kérdés, hogy ha külső csomagok ezt már tudják, akkor mi értelme van felvenni a szabványos könyvtárba. A válasz az, hogy a statistics modul deklaráltan nem kíván versenytársa lenni a statisztikát hivatásszerűen alkalmazó szakembereknek szánt teljes funkcionalitású statisztikai modulcsomagoknak. A célja, hogy nyelvi eszközöket adjon mindazoknak, akiknek alkalomszerűen van csak szükségük nem túl bonyolult statisztikai számításokra, és ezzel időt takarítson meg számukra, mert így nem kell megtanulniuk egy külső csomag használatát.
A magfüggvényes sűrűségbecslés (KDE) elve
A KDE módszer lényege, hogy minden egyes adatpontra egy magfüggvényt (kernel function) helyez, majd ezeknek a magfüggvényeknek az összegeként állítja elő a becsült sűrűségfüggvényt. Ennek képlete látható az alábbi ábra bal oldalán.

A magfüggvény egy páros, nemnegatív függvény, amelynek az értelmezési tartományra vett integrálja 1. A leggyakrabban használt magfüggvények: egyenletes (uniform) vagy más néven téglalap (rectangular), háromszög (triangular), negyedfokú (quartic) vagy más néven kétszeresen súlyozott (biweight), háromszorosan súlyozott (triweight), Epanechnikov vagy más néven parabolikus (parabolic), koszinuszos (cosine), Gauss, logisztikus (logistic) és szigmoid (sigmoid). Ezek képletét az előző ábra jobb oldala mutatja, alakjukat pedig a következő ábra foglalja össze.

A h sávszélesség a KDE simítási paramétere, amelynek értéke meghatározza az egyes adatpontokra helyezett magfüggvények szélességét. Ezért ennek megválasztása befolyásolja a becsült sűrűségfüggvény alakját. Minél kisebb ennek az értéke annál jobban kiemelődnek a változások, míg a nagyobb értékek simább függvényalakot adnak. A túl nagy érték azonban túlsimításhoz vezet, amely a lokális maximumokat elfedheti. A túl kicsi érték pedig jelentéktelen részleteket mutathat meg, ezért nem elegendően sima függvényalakot eredményez.
Ha ismert lenne a sűrűségfüggvény jellege, akkor a h paraméter optimális értéke az lenne, amely minimalizálja a becsült és a tényleges sűrűségfüggvény közötti eltérést. Mivel azonban a valódi sűrűség nem ismert, egyéb módszerek használatosak a sávszélesség meghatározásához. Ezek közül a legegyszerűbb a Scott-féle „ökölszabály” szerinti (Scott’s Rule of Thumb), amely szintén az első ábrán látható. Ez egy könnyen számolható képlet, amely a sávszélesség kiszámításához a minták számát és szórását használja. Azonban ez optimális h értéket csak normális eloszlásból származó minták esetén ad. Ezért a képlet által szolgáltatott értéket egy korrekciós tényezővel fogjuk módosítani az adott feladat sajátosságainak figyelembevételével.
A KDE, valamint a sávszélesség paraméter meghatározási módszereinek matematikai részleteibe – mivel nem ez a célunk – nem megyünk bele. Akit érdekel, az interneten bőven talál erről leírást elsősorban angolul (ha szükséges, a gépi fordítók segítenek). A Wikipédia „Kernel density estimation” és „Kernel (statistics)” szócikkek jó kiindulópontot jelentenek, de a „Kernel density estimation” keresőkifejezés számos egyéb cikktalálatot ad, és sok videó is foglalkozik a témával.
Most inkább példákon keresztül azt mutatjuk meg, hogy hogyan lehet használni a módszert a szabványos könyvtár statistics moduljában szereplő kde(data, h, kernel=’normal’, *, cumulative=False) függvény alkalmazásával.
Modul létrehozása a becsült sűrűségfüggvény meghatározásához és megjelenítéséhez
A konkrét példák előtt néhány előkészítő lépést teszünk, amelynek során segédfüggvényeket és egy enum osztályt definiálunk a becsült sűrűségfüggvény meghatározásához és ábrázolásához. Ezeket egy kde_tools nevű modulban gyűjtjük össze, és importálunk majd be az egyes példáknál.
A kde() függvény kernel nevű paraméterével lehet meghatározni az alkalmazni kívánt magfüggvényt úgy, hogy értékként a magfüggvény nevét kell karakterláncként megadni, ami ráadásul csak kisbetűs lehet. Ez nem túl szerencsés, mert egyrészt személynevekből származó függvényneveket esetleg hajlamosak vagyunk nagy kezdőbetűvel írni (gauss, epanechnikov), másrészt a hosszabb karakterláncokban gépelési hiba is történhet. Ezért az elírásból adódó hibák megelőzésére célszerű egy felsorolástípust definiálni. Ez azért is hasznos, mert ekkor a fejlesztőrendszerek automatikus kódkiegészítője gépelési időt is megtakarít számunkra. Mivel a felsorolástípus tagjai karakterláncot helyettesítenek, ezért célszerű a Python 3.11 verziójától elérhető StrEnum alosztályaként definiálni a felsorolástípusunkat, ahol a tagok a kde() függvény kernel paraméterének megadható nevek lesznek. Ha pedig automatikus értékadást alkalmazunk, vagyis azt az auto() függvénnyel végezzük, akkor a felsorolástípus tagjainak értéke a tagnév kisbetűs változata lesz. Az ennek megfelelő, Kernel nevű osztály definíciója látható az alább mutatott kde_tools modul felső részén.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# modul: kde_tools # Python 3.13+ from enum import StrEnum, auto from typing import Sequence from statistics import kde, stdev import matplotlib.pyplot as plt type RealNum = int | float class Kernel(StrEnum): # Azok a kernelek, amelyek minden mintapontnak adnak valamilyen súlyt. GAUSS = auto() NORMAL = GAUSS SIGMOID = auto() LOGISTIC = auto() # Azok a kernelek, amelyek csak a sávszélességen belüli mintapontoknak adnak súlyt. RECTANGULAR = auto() UNIFORM = RECTANGULAR TRIANGULAR = auto() EPANECHNIKOV = auto() PARABOLIC = EPANECHNIKOV BIWEIGHT = auto() QUARTIC = BIWEIGHT TRIWEIGHT = auto() COSINE = auto() def compute_kde_curve(datapoints: Sequence[RealNum], kernel_name: Kernel | str, bandwidth_adjust: RealNum = 1) -> tuple[list[RealNum], list[RealNum]]: """ A KDE módszer alapján becsült sűrűségfüggvény-görbe számítása megadott adatmintákból és kernel függvénnyel. Paraméterek: datapoints: A bemeneti adatminták. kernel_name: A használt magfüggvény neve vagy objektuma. bandwidth_adjust: A sávszélesség korrekciós tényezője (alapértelmezett: 1). Visszatérési érték: Az x értékek és a hozzájuk tartozó becsült sűrűségértékek. """ n = len(datapoints) # adatminták száma. sigma = stdev(datapoints) # az adatminták szórása. bandwidth = 1.06 * sigma * n ** (-1 / 5) # A KDE sávszélesség paraméterének "ökölszabály" szerinti meghatározása. bandwidth = bandwidth * bandwidth_adjust # A korrigált sávszélesség érték. # A valószínűségi sűrűségfüggvény becslése KDE módszerrel. pdf_est = kde(datapoints, bandwidth, kernel=kernel_name) # Az adatminták rendezése és a hozzájuk tartozó becsült sűrűségfüggvény értékek meghatározása. sorted_datapoints = sorted(datapoints) min_data, max_data = min(sorted_datapoints), max(sorted_datapoints) num_points = 500 delta = (max_data - min_data) / (num_points - 1) x_values = [min_data + i * delta for i in range(num_points)] y_values = [pdf_est(x) for x in x_values] return x_values, y_values def plot_kde_curve(_x_values: Sequence[RealNum], _y_values: Sequence[RealNum], x_label: str = ''): plt.plot(_x_values, _y_values, color='blue') plt.grid(True) plt.xlabel(x_label) plt.ylabel('Becsült sűrűségfüggvény') plt.minorticks_on() plt.tight_layout() plt.show() def plot_histogram(values, bins=None, x_label: str = ''): plt.hist(values, bins, density=True) plt.xlabel(x_label) plt.ylabel('Becsült sűrűségfüggvény') plt.minorticks_on() plt.tight_layout() plt.show() |
Most definiáljunk egy olyan függvényt, amely az adatminták, egy megadott magfüggvény, valamint egy sávszélesség-korrekciós tényező alapján meghatározza a KDE módszerrel becsült sűrűségfüggvény értékeit, egy megfelelően sűrűn mintavételezett x-tartományon. A függvény egy tuple objektummal tér vissza, amely két listát tartalmaz: az első lista az x értékeket, a második pedig a hozzájuk tartozó becsült sűrűségértékeket. E compute_kde_curve nevű függvény a Kernel osztály alatt látható a kde_tools modulban. A működés megértését a részletes kommentek segítik.
A becsült sűrűségfüggvény kirajzolásához, valamint az adatpontok hisztogramon történő megjelenítéséhez további két függvényt definiálunk, amelyek a fentebbi kódsorokban szintén láthatók.
Alkalmazási példák
Mobilapplikáció megnyitási időpontok
Első példaként vegyük azt, hogy egy mobilapplikáció fejlesztője elemezni kívánja azt, hogy a felhasználók mikor nyitják meg legtöbbször az alkalmazást a nap során. Az események időpontjai folytonos valószínűségi változónak tekinthető (pl. 07:42, 14:05 stb.). A KDE segítségével feltérképezhető a használati ritmus (pl. reggeli, ebédidő, esti csúcsok). A rögzített megnyitási időpontokat, a programkódot, valamint a becsült sűrűségfüggvény és hisztogram képernyőképeit alább láthatjuk.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from kde_tools import Kernel, plot_kde_curve, plot_histogram, compute_kde_curve # Példa: Mobilapp megnyitási időpontjai. # Az applikáció megnyitásainak időpontjai (percben a napkezdettől mérve). app_opening_times = [ 455, 460, 480, 500, 510, 520, 535, 470, 490, 505, 525, 485, 515, 530, 465, 495, 475, 512, 533, 468, 498, 508, 528, 538, 458, 488, 518, 528, 468, 510, 495, 785, 800, 815, 830, 850, 860, 790, 805, 825, 845, 865, 808, 828, 848, 868, 1240, 1255, 1260, 1270, 1285, 1290, 1300, 1315, 1245, 1250, 1265, 1275, 1280, 1305, 1242, 1258, 1278, 1298, 1318, 1248, 1268, 1288, 1308, 1252, 1272, 1292, 1312, 1247, 1267, 1287, 1307, 1257, 1277, 1297, 1317, 1262, 1282, 1302, 1255, 1295, 1258, 1299, 1310, 90, 300, 600, 650, 700, 900, 1000, 1100, 1400, 1440 ] xs, ys = compute_kde_curve(app_opening_times, Kernel.GAUSS, 0.4) plot_kde_curve([minute / 60 for minute in xs], ys, 'App megnyitási időpont (óra)') plot_histogram([minute / 60 for minute in app_opening_times], 8, 'App megnyitási időpont (óra)') |

Az adatsorozat több csúcsot mutat: egyet a reggeli órákban, egy másikat az ebédidőben, és egy harmadikat a késő esti órákban. A KDE a három különböző csúcsot folytonos, jól elkülönülő görbeként ábrázolja, míg hisztogrammal a csúcsok környezetében az aktivitáscsökkenés a lépcsős jelleg miatt nem érzékelhető jól. A választott osztályközök szélessége is befolyásolhatja a képet. A valós csúcsok elmosódhatnak vagy épp fordítva, teljesen elkülönülnek, ami nem annyira jól tükrözi a valóságot, mert nyilván a mérési időpontok között is vannak alkalmazásmegnyitások, csak relatíve sokkal kisebb gyakorisággal.
Gépjárművek sebességeloszlása egy útszakaszon
A második példában autók sebességeloszlását vizsgáljuk egy útszakaszon, ahol a sebességkorlát 100 km/h. Egy sebességmérő kamera egy napon keresztül rögzíti az elhaladó autók sebességét. A mért sebességérték folytonos valószínűségi változónak tekinthető. A mért sebességértékeket, a programkódot, valamint a becsült sűrűségfüggvény és hisztogram képernyőképeit alább mutatjuk.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from kde_tools import Kernel, plot_kde_curve, plot_histogram, compute_kde_curve # Példa: Autók sebességeloszlásának vizsgálata egy útszakaszon, ahol a sebességkorlát 100 km/h. # Autók sebességeloszlása (km/h) speeds = [ 68, 71, 75, 65, 72, 69, 73, 67, 70, 74, 69, 70, 72, 74, 68, 70, 71, 73, 75, 66, 105, 91, 95, 93, 98, 96, 92, 108, 94, 97, 99, 90, 94, 96, 93, 97, 102, 95, 92, 98, 90, 99, 91, 79, 81, 85, 76, 82, 80, 84, 103, 86, 88, 83, 87, 85 ] xs, ys = compute_kde_curve(speeds, Kernel.TRIWEIGHT, 1.5) plot_kde_curve(xs, ys, 'Sebesség (km/h)') plot_histogram(speeds, x_label='Sebesség (km/h)') |

A KDE szépen mutatja a sebességek folytonos eloszlását a két kiemelkedő sebességcsoporttal, amelyeket a lassabban haladó járművek (pl. teherautók) és a gyorsabbak (pl. személyautók) produkálnak. Ez utóbbiak viszonylag közel vannak a sebességkorláthoz. A mérési eredmények néhány szabálytalan járművet is mértek, amelyek a sebességkorlát felett közlekedtek. A hisztogram szögletessége nem tükrözi a sebesség folytonos jellegét. Ezen kívül a sebességkorlát feletti értékeket azonos valószínűséggel tünteti fel. Ez azonban nem felel meg a valóságnak. Ugyanis minél nagyobb mértékben haladják meg a sebességkorlátot a járművezetők, annál nagyobb a büntetés kockázata, ezért a nagyobb sebességek valószínűsége a valóságban kisebb. E tekintetben a KDE realisztikusabb képet fest.
Lövéspontosság vizsgálata
Utolsó példaként tegyük fel, hogy egy lőtéren, adott fegyverrel leadott lövések pontosságát akarjuk megállapítani. A lövések céltól való vízszintes eltérését rögzítjük centiméterben. Meg akarjuk tudni, hogy milyen valószínűséggel esik lövés egy adott távolságra a célpont közepétől. Mivel a céltól való eltérés bármekkora valós szám lehet, a találati pontok szóródása folytonos eloszlásúnak tekinthető, ami nem illeszkedik jól a hisztogramhoz. Viszont a KDE módszerrel meghatározott folytonos sűrűségfüggvénnyel jól vizsgálható, hogy a találatok eltérésében milyen tendencia figyelhető meg. A céltól való vízszintes eltéréseket, a programkódot, valamint a becsült sűrűségfüggvény és hisztogram képernyőképeit alább lehet látni.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from kde_tools import Kernel, plot_kde_curve, plot_histogram, compute_kde_curve # Példa: Célzás pontossága egy lőtéren # A találatok eltérése a cél közepétől centiméterben (a cél közepétől jobbra pozitív, balra negatív érték). shots = [-3.1, -2.8, -2.5, -1.8, -1.4, -1.2, -1.1, -0.9, -0.7, -0.3, 0.0, 0.1, 0.2, 0.3, 0.3, 0.4, 0.6, 0.7, 0.9, 1.1, 1.4, 1.7, 1.8, 2.0, 2.1, 2.2, 2.3, 2.5, 2.6, 2.8, -2.4, -2.3, -2.1, -1.9, -1.7, -1.5, -1.3, -1.0, -0.8, -0.6, -0.4, -0.2, 0.1, 0.3, 0.5, 0.7, 0.8, 1.0, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.3, 2.4, 2.6, 2.7, 3.0, -2.9, -2.7, -2.6, -2.2, -2.0, -1.6, -1.1, -0.5, -0.3, 0.0, 0.2, 0.4, 0.6, 0.9, 1.1, 1.3, 1.5, 1.7, 1.9, 2.1, 2.3, 2.4, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.1, 3.2, -3.0, -2.8, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0] xs, ys = compute_kde_curve(shots, Kernel.EPANECHNIKOV, 1.4) plot_kde_curve(xs, ys, 'A találat vízszintes eltérése a céltól (cm)') plot_histogram(shots, x_label='A találat vízszintes eltérése a céltól (cm)') |

Bár a hisztogramból is megállapítható, hogy a fegyver kissé jobbra hord, de a céltól jobbra vagy balra mért távolabbi (2.5 centimétert meghaladó) eltérések egyre kisebb valószínűségét a KDE módszerrel becsült sűrűségfüggvény jobban mutatja.
Ebben a bejegyzésben a nem-paraméteres valószínűségi sűrűségbecslés egyik széles körben alkalmazott módszerét, a magfüggvényes megközelítést mutattuk be Python kódpéldákkal. A kapcsolódó programozási eszközökről a Python tudásépítés lépésről lépésre című e-könyvben bővebben is lehe olvasni a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet „Matematikai számítások támogatása” alfejezetében, valamint a „Különleges” osztálydefiníciók fejezet „Felsorolástípus” alfejezetben.