A Northstar-rendelések között vizsgáljuk a distance_km
szállítási távolságot. A megfigyelési egység egy rendelés, a bemenet a
tisztaz orders.csv pozitív kilométer-oszlopa; a cél annak
eldöntése, hogy a ferde, jobbra elnyúló eloszlás logaritmikus skálán
jobban áttekinthető-e, és mi a teendő nulla vagy negatív értékekkel. A
kimenet egy nyers és egy log-skálás hisztogram, valamint egy dokumentált
döntés a zéró/negatív kezelésről.
Előzetes becslés: a távolságok tipikusan néhány kilométertől néhány
száz kilométerig terjednek, így a nyers hisztogram jobbra ferde lesz, a
log10-transzformált pedig közelítőleg szimmetrikusabb, mert
-szeres
szorzók azonos távolságra kerülnek. Kézzel kiszámolva: ha egy érték
,
akkor
;
ha
,
akkor
;
ha
,
akkor
;
ha
,
akkor
.
Ez már mutatja, hogy a logaritmus a szorzókat összeadássá alakítja.
Az exponenciális függvény (exponential function) a természetes alappal:
Jelölésmagyarázat:
a bemenet, mértékegység nélküli valós szám;
a kimenet, szintén mértékegység nélküli, de mindig szigorúan pozitív.
Tömbalakban: ha x egy (n,) alakú NumPy-tömb,
akkor az np.exp(x) ugyancsak (n,) alakú, és
elemenként számol. A függvény értelmezési tartománya
,
értékkészlete
.
Három tulajdonság, amit minden későbbi modellépítésben használunk:
A logaritmus (logarithm) a hatványozás
inverze: ha
,
akkor
.
Az
alapú logaritmust természetes logaritmusnak nevezzük, és
-szel
jelöljük. A Python math.log(x) és a NumPy
np.log(x) is természetes logaritmust számol; a
-es
alapú függvény külön math.log10(x) és
np.log10(x) hívás.
Az inverz tulajdonság:
Így , mert ; ; és általában . A logaritmus értelmezési tartománya csak a pozitív számok halmaza ; nullára a bal oldali határ , negatív valós számra pedig nincs valós logaritmus. Ez a legfontosabb korlát a gyakorlatban: mielőtt logaritmálunk, dokumentálnunk kell, hogy az adat csak pozitív, vagy meg kell oldani a nulla/negatív értékek kezelését másképp.
A logaritmikus skála a szorzókat additívvá alakítja:
Például az
sorozat esetén a log10 értékek
:
az eredetileg exponenciálisan növekvő ugrások azonos lépésközzé válnak.
Ezért ha egy pozitív adatsor több nagyságrendet fog át, a log-skálás
hisztogram vagy vonaldiagram sűríti a nagy értékeket, és láthatóvá teszi
a kis értékek szerkezetét.
A monoton transzformáció (monotonic transformation) olyan függvény, amely megőrzi a sorrendet. Mivel szigorúan növekvő, ha , akkor . A sorrend megmarad, de a távolságok nem: például , míg is — az eredeti és különbség helyett mindkét lépés azonos. Ez előny az ábrázolásban, de fontos következmény: a log-skálán számolt átlag nem az eredeti skála számtani átlaga, erre az alfejezet végén visszatérünk.
A logaritmus a későbbi statisztikai modellezésben is központi szerepet kap. A log-likelihood (logaritmikus likelihood) azért használatos, mert a likelihood sok valószínűség szorzata; az alatti szorzások gyorsan alulcsordulnak (underflow) a lebegőpontos ábrázolásban, míg a logaritmusok összege stabil marad:
Itt
az
-edik
megfigyelés likelihood-tényezője,
a szorzatjel,
az összegjel; az egyenlet a logaritmus szorzat-összeg azonosságának
-tagú
kiterjesztése. A cél a numerikus stabilitás:
a lebegőpontos float64 tartományban már nulla, de
körülbelül
,
kényelmesen ábrázolható. Ezzel a gondolattal a likelihood-alapú
becslésnél (Paraméter, becslő és likelihood)
találkozunk majd újra; itt elég annyi, hogy a logaritmus a szorzást
összeadássá alakítja, ami numerikusan és jelölésben is kezelhetőbb.
A logit előképe (logit preimage) a nyílt intervallumot képezi le a teljes -re:
A itt valószínűség, mértékegység nélküli; a kimenet szintén mértékegység nélküli, negatív vagy pozitív. Az „előkép” kifejezés arra utal, hogy a intervallumból érkezik, a transzformáció eredménye pedig korlátlan valós szám. A határértékek: esetén , esetén , . Ennek gyakorlati értelme: ha egy modell valószínűséget becsül, akkor a nyers lineáris prediktor a valós skálán él, és a logit inverze, a logisztikus függvény, szorítja vissza -be:
Ez a későbbi logisztikus regresszió (Lineáris és logisztikus prediktív modellek) magja; most annyi a feladatunk, hogy a logit mint transzformáció kiszámítását és tartományát megértsük.
log1p és a
numerikus stabilitásHa az adatsorban nulla érték is előfordul, a
nem alkalmazható közvetlenül, mert
nem értelmezhető. Az egyik bevett megoldás a
transzformáció, amit a NumPy a log1p
függvénnyel számol:
A log1p előnye nem csupán kényelem, hanem numerikus
pontosság: amikor
kicsi, például
,
az
lebegőpontos összeg már kerekítési hibát szenved, így a
direkt kiszámítása pontatlan. A log1p belsőleg a
sorfejtését használja, és kis
-re
pontos eredményt ad. A két kifejezés ugyanazt a matematikai értéket
célozza, de a numerikus út különböző.
Szemléltető, kézzel ellenőrizhető értékek:
| Kifejezés | Elméletileg várt érték |
|---|---|
log1p(0.0) |
|
log1p(-0.5) |
|
log1p(np.e - 1) |
, mert |
log1p(1e-16) |
,
míg log(1+1e-16) könnyen
-t
adhat float64-ben |
A log1p tehát a nulla érték kezelésének standard
építőköve, amikor a logaritmust közelítő transzformációt akarunk
pozitív, de kis értékek mellett is.
A feladat: generáljunk ferde, jobbra elnyúló pozitív adatsort, rajzoljunk nyers és log-skálás hisztogramot, és dokumentáljuk a nulla/negatív kezelést. A kód a szokásos importokkal kezdődik, a véletlengenerátort rögzítjük, hogy a munkamenet reprodukálható legyen:
import numpy as np
import matplotlib.pyplot as plt
# Rögzített seed: a szimulációs adat ugyanazt a sorrendet adja ugyanazon CPU-verzión
rng = np.random.default_rng(20261020)
# Ferde pozitív adat: lognormális eloszlás, n=5000 megfigyelés.
# A lognormális azt jelenti, hogy a természetes logaritmusa normális eloszlású.
x_positive = rng.lognormal(mean=2.0, sigma=1.1, size=5000)
# Szándékosan hozzáadunk 50 nullát és 30 negatív értéket,
# hogy a kezelést gyakoroljuk. A negatív értékeket -0.5 és -10 között
# egyenletesen sorsoljuk; ezek azt modellezik, hogy hibás vagy csonkolt
# adat érkezett, amire transzformáció előtt dönteni kell.
x_zero = np.zeros(50)
x_negative = rng.uniform(low=-10.0, high=-0.5, size=30)
# A teljes nyers adatsor; a döntéshez külön kezeljük a három csoportot.
x = np.concatenate([x_positive, x_zero, x_negative])
# 1. lépés: csak a pozitív adatra ábrázolunk, mert log(0) és log(negatív)
# valós számokon nem értelmezhető GYIK-ként szerepelő módon.
mask_positive = x > 0
x_pos_only = x[mask_positive]
# 2. lépés: a nulla értékeket log1p-vel kezeljük:
# log1p(0)=0, az eredmény továbbra is pozitív, sorrendtartó.
x_with_zero = x_zero
log1p_zero = np.log1p(x_with_zero)
# 3. lépés: a negatív értékeket külön dokumentáljuk; itt nem transzformáljuk,
# hanem hibajelentésbe gyűjtjük, mert a log1p csak ( -1, inf )-en értelmezhető.
negative_count = int(np.count_nonzero(x < 0))
print(f"Negatív értékek száma (kihagyva a log-transzformációból): {negative_count}")
# 4. lépés: ábrázolás. A pozitív adatra nyers és log10 skálájú hisztogram.
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].hist(x_pos_only, bins=60, color="steelblue", edgecolor="white", alpha=0.85)
axes[0].set_title("Nyers távolság eloszlás")
axes[0].set_xlabel("km")
axes[0].set_ylabel("Rendelések száma")
axes[1].hist(np.log10(x_pos_only), bins=60, color="darkorange", edgecolor="white", alpha=0.85)
axes[1].set_title("Log10-transzformált távolság")
axes[1].set_xlabel("log10(km)")
axes[1].set_ylabel("Rendelések száma")
plt.tight_layout()
plt.show()A fenti kód még nem automatizáltan ellenőrzött, így a kimenetet
szemléltető jellegűnek nevezzük. A várt viselkedés: a
bal oldali hisztogram jobbra elnyúló, tömeg a kis értékeknél; a jobb
oldali ábra közelítőleg szimmetrikus haranggörbe, mert a lognormális
definíció szerint éppen ezt várjuk. A np.log10 negatív vagy
nulla bemenetre RuntimeWarning-ot ad és
-inf-et vagy nan-t szolgáltat, ezért a mask
szükséges. A negative_count a példánkban
.
Kézi kontroll a logaritmusazonosságra, amit a kód ellenőrzéséhez papíron is kiszámolunk:
# Egyszerű ellenőrző vektor
x_check = np.array([1.0, 10.0, 100.0, 1000.0])
log10_check = np.log10(x_check)
# Elvárt: [0., 1., 2., 3.]
# A log1p és log(1+x) ekvivalenciája pozitív x-re:
y = np.array([0.0, 0.5, 1.0, 10.0])
diff = np.log1p(y) - np.log(1.0 + y)
# Elvárt: diff minden eleme 0 vagy 1e-16 nagyságrendű kerekítési hibaAz ellenőrzési feltétel a shape-re: x_check shape-je
(4,), a log10_check shape-je szintén
(4,), a dtype float64. Toleranciánk a kézi
kontrollnál szigorú, de a lebegőpontos művelet miatt
eltérést fogadunk el.
A lognormális eloszlás definíciója szerint a
normális, ezért a
transzformált hisztogram vártan szimmetrikus. Ez nem azt jelenti, hogy a
nyers adat hibás; csak azt, hogy a skálaváltás megmutatja a mögöttes
multiplikatív struktúrát. A nulla értékeket log1p-vel
-ba
képeztük le, ami megőrzi a nullák megkülönböztetését; ha egy nullát és
egy kis pozitív értéket szeretnénk összehasonlítani, a
log1p az eredeti sorrendet megtartja. A negatív értékeket
kihagytuk, mert a valós logaritmus tartománya
;
ez a döntés a munkanaplóba írandó, mert később a negatív távolság
adatminőségi hibát jelezhet.
Kiemelt ellenőrzési pont: a transzformált tömb dtype-ja
float64, mert az egész értékek logaritmusa is általában
irracionális lebegőpontos szám. A np.log10(1000) pontosan
,
de a np.log10(10) belső ábrázolása kerekítési hibát
tartalmazhat; ezért a fenti kontrollt np.allclose-zal
érdemes futtatni, atol=1e-15 mellett.
A log-skálán vett átlag értelmezése külön figyelmet érdemel. Ha pozitív megfigyelések, akkor az exp-transzformált log-átlag a geometriai átlag (geometric mean):
Ez nem a szokásos számtani átlag. Például esetén a számtani átlag , a geometriai átlag
Itt
mértékegység nélküli; a képletben
minden
-re,
és a tömb alakja (n,). A geometriai átlag a multiplikatív
folyamatok „középértéke”: ha egy folyamat minden lépésben
valahányszorosára nő, akkor az átlagos szorzó ennyi. A futási példánkban
a lognormális eloszlású távolságok esetén a geometriai átlag rendre
kisebb, mint a számtani, mert a nagy ritka értékek a számtani átlagot
jobban felfelé húzzák.
1. feladat — Értelmezés. Magyarázd el, miért nincs értelmezve és , és add meg a következő értékeket négy tizedesjegyre: , , , .
2. feladat — Megvalósítás. Írj Python-függvényt
log_shifted(x, c) néven, amely a log1p
függvénnyel kiszámolja a
értéket, feltéve, hogy
minden elemre. A függvény ellenőrizze, hogy a bemeneti tömb minden eleme
kielégíti-e a feltételt, és ha nem, ValueError-t dobjon.
Próbáld ki a [-0.5, 0.0, 1.0, 10.0] bemenetre
mellett, és ellenőrizd kézzel: az eredmény
[ln(0.5), 0, ln(2), ln(11)].
3. feladat — Hibadiagnózis. Egy munkatárs a havi kosárértékeket akarta elemezni, és a következőt írta: „Kiszámítom a log-értékek átlagát, majd visszaexponálom, az lesz a havi átlagos kosár.” Miért helytelen ez a megfogalmazás? Adj konkrét ellenpéldát két pozitív számmal, ahol a számtani átlag és az exp-átlagos-log érték különbözik, és nevezd meg a helyes kifejezést a kapott értékre.
4. feladat — Megváltozott feltételű továbbvitel. Az adatsorba negatív értékek is kerülnek, például nettó profit, amely lehet negatív. Dolgozz ki transzformációs konvenciót: adj képletet, amely negatív -re is definiált, megőrzi a sorrendet, és -ban folytonos. Képlet:
Ezt gyakran signed_log-nak nevezik. Implementáld
NumPy-ban, és ellenőrizd a következő értékeken:
;
add meg a várt eredményt négy tizedesjegyre.
1. megoldás. A logaritmus a hatványozás inverze: pontosan akkor, ha . A -es alapú hatvány bármely valós kitevőre pozitív, tehát soha nem és soha nem negatív; így az egyenletnek nincs megoldása -ra vagy -ra. Az értékek: ; ; ; . Ellenőrzés: , , , ; mind a négy számítás az inverz definícióból következik, tolerancia nélkül pontos, mert a kitevők egészek.
2. megoldás. A függvény:
import numpy as np
def log_shifted(x, c):
x = np.asarray(x, dtype=float)
if np.any(x + c <= 0):
raise ValueError("A logaritmus argumentuma minden elemre pozitív kell legyen.")
return np.log1p(x + c - 1.0)A log1p(z) definíciója
,
ezért
,
tehát a log1p(x + c - 1.0) hívás pontos eredményt ad. A
megadott bemenetre
mellett a kimenet
.
Ellenőrzés:
;
a többi érték a természetes logaritmus táblázatából adódik. A shape
(4,), a dtype float64.
3. megoldás. Az a geometriai átlag, nem a számtani. Konkrét ellenpélda: esetén a számtani átlag ; a log-átlag ; visszaexponálva . A két eredmény különbözik, mert a logaritmus nem lineáris transzformáció, ezért az átlag nem vihető át rajta. A munkatársnak vagy geometriai átlagnak kell neveznie az eredményt, és azt a szorzóközpontú folyamatokra használni, vagy a havi átlagos kosárértékhez a nyers skálán kell számtani átlagot számolnia. A tanulság: a log-skála sorrendtartó, de a távolságokat torzítja, így a statisztikai összegzők is megváltoznak.
4. megoldás. A signed_log
implementáció:
import numpy as np
def signed_log(x):
x = np.asarray(x, dtype=float)
result = np.zeros_like(x)
pos = x >= 0
neg = x < 0
result[pos] = np.log1p(x[pos])
result[neg] = -np.log1p(-x[neg])
return resultA várt értékek négy tizedesjegyre:
A transzformáció szimmetrikus:
,
sorrendtartó, és
-ban
folytonos. A log1p használata a
körüli stabilitást is biztosítja. Ez a konvenció megőrzi a negatív
értékek előjelét, és csak akkor alkalmazható, ha az elemek nagyobbak
-nél
a pozitív oldalon és hasonlóan a negatív oldalon; általános
-re
a
-eltolásos
változat is építhető, de akkor a választott
-t
a munkanaplóban dokumentálni kell.
A leggyakoribb hiba: a logaritmikus skálán vett átlagot egyszerűen exponáljuk, és azt gondoljuk, hogy az eredeti skála átlagát kapjuk vissza. Az előző ellenpélda is ezt mutatja: esetén az eredeti számtani átlag , a log-úton kapott . A hiba forrása, hogy a logaritmus nem lineáris, ezért az átlag és a transzformáció sorrendje nem felcserélhető: . A helyes kapcsolat a Jensen-egyenlőtlenség következménye, amely szerint a konkáv függvényre , pozitív szórású adatnál szigorú egyenlőtlenséggel. A gyakorlatban tehát mindig dönteni kell: ha az eredeti fizikai skála számtani átlaga a cél, akkor a log-skálán számolt modellből való visszatérés nem elegendő; a modell pontosítása és a predikció vissza-transzformációja külön lépést igényel (a későbbi fejezetekben a lognormális modellnél látjuk majd a korrekciót). Ez a tévedés szorosan kapcsolódik a Jelölésből végrehajtható számítás alfejezetben tárgyalt jelölési disciplínához: a formula megmondja a sorrendet, csak követni kell.
A pozitív adatok logaritmizálása a skálát változtatja meg, de nem
teszi automatikusan normálissá az eloszlást. A log1p(x) a
log(x)-tól különböző transzformáció, amely a nulla értéket
is kezeli; az eredményt ennek megfelelően kell értelmezni.
Az exponenciális függvény és a logaritmus egymás inverze; a
logaritmus a szorzást összeadássá, a szorzókülönbségeket azonos
lépésekké alakítja. A természetes logaritmus csak pozitív számokra
értelmezhető, ezért nulla értéknél a log1p, negatív
értéknél pedig vagy kihagyás, vagy signed_log szükséges. A
logit a
valószínűségi skálát képezi a valós egyenesre, előkészítve a logisztikus
modellezést. A log-skálán számolt átlag a geometriai átlag, nem a
számtani; ennek fel nem ismerése tipikus hibás értelmezés. A következő
alfejezetben a határértéket mint közelítési folyamatot vizsgáljuk meg,
ami a numerikus közelítés és a derivált alapfogalma lesz.