A Northstar adatszerződésben egy rendelés sora a predikció
megfigyelési egysége. Tegyük fel, hogy a distance_km,
basket_minor és quantity_sum három oszlopból
kiválasztottunk öt rendelést, és NumPy-tömbben tároljuk. Az elemzés
előtt tudnunk kell, hogy a táblázatos adatból hogyan lesz tömb: hány
dimenzió, milyen alak, melyik tengely mentén összegzünk, és milyen
numerikus típust használunk.
A konkrét bemenet egy -as mátrix:
A sorok rendeléseket, az oszlopok a fenti három feature-t jelentik. A mértékegység: km, EUR-cent, darab.
Induló becslés, mielőtt kódot írunk: mekkora az
ndim és a shape? Becsüld meg az
oszlopátlagokat: az első oszlop átlaga
és
között lesz, a másodiké
körül, a harmadiké
és
között. Írd le azt is, milyen alakú eredményt vársz az
oszlopátlag és a sorátlag művelettől.
A skalár (scalar) egyetlen szám. NumPy-ban a 0-dimenziós tömb:
s = np.array(3.2)
s.ndim # 0
s.shape # ()A vektor itt egy rendezett számlista, például
.
Ez egy 1-dimenziós tömb, shape=(3,):
v = np.array([12.4, 2990.0, 2.0])
v.ndim # 1
v.shape # (3,)A vektor most még csak számlista; a geometriai jelentést később, a lineáris algebra fejezetben kapja meg.
Az ndim a dimenziók száma. A
shape egy egész számokból álló tuple,
amely megadja az egyes tengelyek hosszát. Az
-as
mátrixra: ndim=2, shape=(5, 3). A tömbbeli
elemek száma a shape elemeinek szorzata:
.
A NumPy sor-major (C-rendű) tárolást használ: a
memóriában először az első sor elemei következnek, utána a második soré.
Ez akkor válik fontossá, amikor a reshape műveletet
vizsgáljuk.
A tengely (axis) az aggregáció irányát jelöli. A szabály, amelyet érdemes mechanikusan alkalmazni: a megadott tengely eltűnik az eredményből.
Ha A alakja
és A.mean(axis=0)-t számolunk, akkor a 0. tengely, vagyis a
sorok tűnnek el, az eredmény alakja
.
Ez az oszloponkénti átlag: minden oszlopra a rögzített
index mellett átlagolunk az
sorokon:
A A.mean(axis=1) esetén az 1. tengely, vagyis az
oszlopok tűnnek el, az eredmény alakja
.
Ez a soronkénti átlag:
Ugyanez a szabály igaz az összegre, minimumra, maximumra és más redukciós műveletekre.
A dtype (data type) a tömbben tárolt
elemek típusa. A leggyakoribb kezdeti értékek:
np.array([1, 2, 3]).dtype # int64
np.array([1.0, 2.0, 3.0]).dtype # float64
np.array([True, False]).dtype # bool
np.array(["a", "b"]).dtype # <U1, egykarakteres Unicode stringA tömb létrehozásakor a NumPy egységes típust választ: ha egy
listában vegyesen van egész és valós, akkor float64 lesz.
Ha szám mellé string keveredik, akkor a tömb stringgé válik, és a
numerikus műveletek meghiúsulnak. A dtype a memóriaigényt is
meghatározza: egy float64 elem 8 bájt, egy
float32 elem 4 bájt, de a float32 kisebb
pontosságú.
A reshape megváltoztatja a shape-et, de
az elemszámot és a C-rendű sorrendet nem:
a = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3)
a.reshape(6) # shape (6,): [1, 2, 3, 4, 5, 6]
a.reshape(3, 2) # shape (3, 2)
a.reshape(-1) # shape (6,): a -1 automatikusan kiszámolt hosszA -1 hasznos, ha az egyik dimenzió hosszát a másikból
akarjuk kiszámoltatni.
A példa a Northstar feature-hármasát használja. A kódot a
projektgyökérben egy s017_pelda.py fájlba írjuk, futtatása
uv run python s017_pelda.py a lab környezetből. A NumPy 2.x
verziót feltételezünk; a np.float64 és
np.float32 elnevezés ebben a verzióban érvényes, a régi
np.float kerülendő.
import numpy as np
# 5 rendelés, 3 rendeléskor ismert feature.
# Oszlopok: distance_km, basket_minor, quantity_sum.
orders = np.array(
[
[12.4, 2990.0, 2.0],
[8.1, 5990.0, 4.0],
[21.7, 1290.0, 1.0],
[5.0, 8790.0, 6.0],
[14.9, 3490.0, 3.0],
],
dtype=np.float64,
)
print("ndim:", orders.ndim)
print("shape:", orders.shape)
print("dtype:", orders.dtype)
column_mean = orders.mean(axis=0) # oszloponkénti átlag
row_mean = orders.mean(axis=1) # soronkénti átlag
print("column_mean shape:", column_mean.shape)
print("column_mean:", column_mean)
print("row_mean shape:", row_mean.shape)
print("row_mean:", row_mean)
flat = orders.reshape(-1)
print("flat shape:", flat.shape)
print("flat első hat elem:", flat[:6])Szemléltető kimenet (a kapcsolódó példaprojektben még nem ellenőrzött; a számokat levezetés és kézi kontroll követi):
ndim: 2
shape: (5, 3)
dtype: float64
column_mean shape: (3,)
column_mean: [ 12.42 4510. 3.2 ]
row_mean shape: (5,)
row_mean: [1001.46666667 2000.7 437.56666667 2933.66666667 1169.3 ]
flat shape: (15,)
flat első hat elem: [ 12.4 2990. 2. 8.1 5990. 4. ]
Az orders tömb ndim=2,
shape=(5, 3), dtype=float64. A
mean(axis=0) az első tengelyt tünteti el, ezért az eredmény
alakja (3,). A kapott három érték rendre a távolság, a
kosárérték és a darabszám átlaga:
A mean(axis=1) a második tengelyt tünteti el, ezért az
eredmény alakja (5,). Minden sor három elemét átlagolja. Az
első sor:
.
Ez az érték kevert mértékegységű, mert km, cent és
darab számtani átlagát veszi; itt a sorátlag csak shape-gyakorlat,
üzletileg nem értelmezhető. Az oszlopátlag viszont mértékegység-helyes,
mert minden oszlop ugyanazt a mennyiséget méri.
A reshape(-1) a
elemet C-sorrendben egyetlen vektorrá fűzi: előbb az első sor, majd a
második, és így tovább. Az első hat elem valóban
.
A shape-invariáns, amelyet minden redukcióra ellenőrizhetünk:
Például (5, 3) esetén axis=0 után
(3,), axis=1 után (5,).
Alak- és típusinvariáns. A NumPy axis értelmezése mindig
a bemeneti tömb dimenzióihoz kötött; egy kétdimenziós
(n, p) adatmátrix esetében az axis=0
oszloponként, az axis=1 soronként redukál. Az
ndarray dtype-ja a tárolt reprezentációt
határozza meg, nem automatikusan a valós világban értelmes
mértékegységet.
Ellenpróba: egész típusú tömbön történő műveletnél
ellenőrizd a túlcsordulást és a kimeneti típust, az üres tengelyen
végzett átlagolásnál pedig a figyelmeztetést vagy nan-t.
Minden mintapéldához rögzítsd a várt shape-et és
dtype-ot.
1. Értelmezés. Az
-as
orders tömbre mondd meg: mi az ndim, a
shape, és mit jelent képlettel a sum(axis=0)?
Mi lesz a kapott tömb shape-e, és mennyi a
sum(axis=0) első eleme?
2. Implementálás. Hozz létre egy -es tömböt:
B = np.array([[1, 10], [2, 20], [3, 30], [4, 40]])Számítsd ki az oszlopátlagot, a sorátlagot és a
reshape(-1) eredményét. Ellenőrizd mindhárom eredmény
shape-ét és értékeit kézzel.
3. Hibadiagnózis. Az alábbi kód egy elemző szándéka szerint „soronkénti átlagot” akar számolni:
M = np.array([[1, 2], [3, 4]])
sor_atlag = M.mean(axis=0)
print(sor_atlag) # [2. 3.]Mi a hiba? Milyen axis kell a soronkénti átlaghoz, és mi
lenne a helyes eredmény? Magyarázd el a kimenet alakját is.
4. Megváltozott feltétel. Az eredeti
orders tömbhöz adj egy negyedik oszlopot,
item_rows-t, a következő értékekkel:
[1, 2, 1, 3, 2]. Az új tömb alakja
.
Számítsd ki az új sorátlagokat, és add meg az item_rows
oszlop átlagát. Mi az új sorátlagok shape-e, és miért
változott meg az értékük az eredeti sorátlagokhoz képest?
1. megoldás. ndim=2,
shape=(5, 3). A sum(axis=0) az első tengelyt
tünteti el, tehát oszloponkénti összeg:
Az eredmény shape=(3,). Az első elem az első oszlop
összege:
Tehát a várt tömb [62.1, 22550.0, 16.0].
2. megoldás. A kód:
B = np.array([[1, 10], [2, 20], [3, 30], [4, 40]])
B.shape # (4, 2)
B.mean(axis=0) # (2,): oszlopátlag [2.5, 25]
B.mean(axis=1) # (4,): sorátlag [5.5, 11, 16.5, 22]
B.reshape(-1) # (8,): [1, 10, 2, 20, 3, 30, 4, 40]Kézi ellenőrzés: az első oszlop átlaga
;
a másodiké
.
A sorátlagok:
,
,
,
.
A reshape(-1) C-rendben soronként fűzi az elemeket.
3. megoldás. A hiba, hogy axis=0 az
oszlopok szerinti átlagot adja, nem a soronkéntit. A M
mátrix:
axis=0 az első tengelyt tünteti el, ezért az eredmény
alakja (2,), és az oszlopátlagok [2, 3]. A
soronkénti átlaghoz axis=1 kell, amely a második tengelyt
tünteti el, az eredmény alakja (2,), értéke
[1.5, 3.5]. A kimenet alakja mindkét esetben
(2,), de az axis választás dönti el, hogy mit
átlagolunk.
4. megoldás. Az új tömb:
orders2 = np.array(
[
[12.4, 2990.0, 2.0, 1.0],
[8.1, 5990.0, 4.0, 2.0],
[21.7, 1290.0, 1.0, 1.0],
[5.0, 8790.0, 6.0, 3.0],
[14.9, 3490.0, 3.0, 2.0],
],
dtype=np.float64,
)
orders2.mean(axis=1)
# Szemléltető kimenet: shape (5,)
# [ 751.35 1501.025 328.425 2201.0 877.475]
orders2[:, 3].mean()
# Szemléltető kimenet: 1.8A sorátlagok shape=(5,), mert a második tengely (most
már 4 elem) tűnik el. Az első sor:
.
Az item_rows oszlop átlaga
.
Az értékek azért változtak, mert a korábbi három elemhez egy negyedik,
más mértékegységű elem is bekerült; az összesítés továbbra is kevert
mértékegységű, tehát a sorátlag most is csak shape-gyakorlat.
A leggyakoribb tévedés: „az axis=0 azt jelenti,
soronként haladok.” A NumPy szabálya nem a bejárás irányáról, hanem az
eltűnő tengelyről szól.
Ellenpélda a -as tömbre:
A.mean(axis=0) # [2.5, 3.5, 4.5], shape (3,) -> oszloponkénti átlag
A.mean(axis=1) # [2.0, 5.0], shape (2,) -> soronkénti átlagHa a magyar mondat úgy szól, hogy „rendelésenkénti átlag”, akkor a
rendelés a sor, tehát a soroknak meg kell maradniuk; a törölt tengely az
oszlop, azaz axis=1. Ha viszont „feature-onkénti átlag”
kell, akkor az oszlopok maradnak meg, és a sorok tűnnek el, azaz
axis=0. Az axis számozás a shape tuple pozíciója, nem a
művelet irányának emberi leírása.
Ez a hiba a Northstar elemzésben is számít: az orders
tömbön a sor a megfigyelési egység. Ha véletlenül axis=0-t
használsz, akkor feature-átlagot kapsz; ha axis=1-et, akkor
rendelésenkénti átlagot. A két eredmény más méretű és más jelentésű.
shape=(10, 4), akkor mi az
ndim, és mi lesz a sum(axis=0) eredményének
shape-e?shape=(3,) vektor és a
shape=(3, 1) oszlopmátrix között? Próbáld ki
np.array([1, 2, 3]) és
np.array([[1], [2], [3]]) alakkal.np.array([1, 2.5, 3]) listára, és miért?reshape(-1) eredménye egy (2, 3)
alakú tömbön, és milyen sorrendben követik az elemek?A NumPy-tömb dimenzióit az ndim, alakját a
shape adja meg; a dtype a tárolt elemek
egységes típusa. A redukciók, például a mean vagy a
sum, a megadott tengelyt tüntetik el: axis=0 a
sorokat, axis=1 az oszlopokat törli, ezért a kimenet alakja
rendre az oszlopok vagy a sorok száma. A reshape az
elemszám megtartásával, C-sorrendben rendezi át a tömböt. A sor- és
oszlopátlag kézzel ellenőrizhető, és a shape-invariáns segít elkerülni a
félrecsúszott tengelyválasztást. A következő alfejezet a broadcasting,
az indexelés és a másolás szabályait építi erre az alak- és
tengelymodellre.