A data scientist munkája ritkán azzal kezdődik, hogy “futtassunk egy modellt”. Gyakran azzal kezdődik, hogy egy látszólag egyszerű ellenőrzés elromlik, vagy egy számról kiderül, hogy mást jelent, mint amit a neve sugall. Ebben az alfejezetben a Northstar labor segítségével egy teljes, kis diagnosztikai munkamenetet építünk fel. A cél nem egy bonyolult modell, hanem az, hogy a három leggyakoribb hibacsaládot — programozási hibát, adatértelmezési hibát és statisztikai túlkövetkeztetést — ugyanabban a feladatban külön tudjuk választani.
Konkrét helyzet. A labor a könyv projektgyökerének
lab/ mappájában van. A bemenet a
generate_data.py által előállított három tiszta CSV:
data/customers.csv (300 ügyfél),
data/orders.csv (2400 rendelés) és
data/order_items.csv (4796 rendelési tétel). A megfigyelési
egység a három fájlban rendre ügyfél, rendelés és rendelési tétel; ezt a
szemcsézettséget tilos összemosni. A kimenet egy saját futási jegyzék,
amelyben legalább a környezet, a három kontrollérték és a vizsgált
alapmutató szerepel.
Előzetes becslés a megoldás előtt. Mielőtt futtatsz
bármit, írd be a hipotézisnaplódba: szerinted a tiszta
orders.csv-ben a rendelések hány százalékánál késik a
kézbesítés a 7 napos vállalt határidőn túl? Adj egy alsó és egy felső
határt, például “4% és 12% között”. Ezt a tippet később nem pontozzuk;
az a szerepe, hogy rögzítse a várakozásodat, mielőtt a mérés
befolyásolná a memóriádat. Ez a 1.3-ban bemutatott hipotézisnapló
legegyszerűbb formája.
A diagnosztikai feladat matematikája szándékosan kicsi. A
darabszám (count) rekordok számát jelenti:
hány sor van a fejlécen kívül. Az arány
(proportion, rate) egy részsokaság darabszámának és a teljes
nevezőnek a hányadosa. Ha
a rendelések száma,
pedig azok száma, amelyeknél a target_late oszlop értéke
1, akkor a késői kézbesítés aránya
Itt
dimenzió nélküli szám, értékkészlete
.
Százalékban
szerepel, mértékegysége %. A százalékpont
(percentage point, pp) két, már százalékban megadott arány
különbsége. Ha az egyik időszakban
(azaz
),
egy másikban
(),
akkor a különbség
százalékpont, ugyanakkor a relatív növekedés
,
azaz
.
A kettő keverése az egyik legkönnyebben elkövethető hiba a
jelentésekben.
A diagnózis másik tengelyét a három hibacsalád adja:
target_late == 1 összehasonlítás soha nem igaz.A labor szándékosan zero-dependency: a
generate_data.py és a quickcheck.py csak a
Python standard library-t használja, tehát nem kell
uv-környezetet létrehoznunk. A munkakönyvtár a
lab/.
# platform: Linux/macOS vagy Windows PowerShell; munkakönyvtár: a projekt lab/ mappája
python --version
python generate_data.py
python quickcheck.pyA python --version kimenete a saját gépeden látszik; a
tantervi alap Python 3.12, de a generátor ennél régebbi Python 3-ason is
futtatható, ha a szintaxisa kompatibilis. A
generate_data.py újraírja a szállított kis CSV-ket a
rögzített seeddel. A quickcheck.py ezután a
manifest.json alapján ellenőrzi a fájlokat. A három
szerződött rekordszám a mellékelt adatszerződés szerint:
customers.csv 300, orders.csv 2400,
order_items.csv 4796. A pontos kimeneti formátum a labor
verziójától függ; a döntő az, hogy e három értéknek a manifesttel
egyeznie kell. Ebben az alfejezetben nem állítjuk, hogy most lefuttattuk
a labort: a tényleges futás a te gépeden történik, és a rögzített
kimenetet a te jegyzékedbe írod.
A quickcheck futtatása után a “kézi kontroll” azt
jelenti, hogy legalább egyszer magunk is megszámoljuk a sorokat, nem
csak elfogadjuk a kész kimenetet. A következő kis program a CSV-olvasót
használja, így platformfüggetlenül, a fejléccel nem számolva adja vissza
a rekordok számát:
from pathlib import Path
import csv
expected = {
"customers.csv": 300,
"orders.csv": 2400,
"order_items.csv": 4796,
}
for name, want in expected.items():
with (Path("data") / name).open(newline="", encoding="utf-8") as f:
reader = csv.reader(f)
header = next(reader)
got = sum(1 for _ in reader)
print(f"{name}: rows={got} columns={len(header)} expected={want} ok={got == want}")A kimenet várható alakja mindhárom fájlra ok=True; a
futási jegyzékben a generátor és a quickcheck pontos
kimenetét is rögzítsd. A hash-ellenőrzésnél nincs tolerancia: a seed és
a generátorverzió együtteséből bájtazonos állományok következnek, ezért
itt az “elég közel” nem elfogadható. Ezt a szigorúságot a 1.3 futási
jegyzékeléséből hozzuk át.
Ezután számítsuk ki az alapmutatót a rendelésszintű célváltozóra. A
target_late a tiszta orders.csv-ben 0 vagy 1:
1, ha delivery_days > promised_days, azaz a kézbesítés
késik a vállalt 7 naphoz képest. A tiszta adatállomány végén minden
kézbesítés teljesen megfigyelt, ezért a nevező mind a 2400 rendelés
lehet:
from pathlib import Path
import csv
with Path("data/orders.csv").open(newline="", encoding="utf-8") as f:
orders = list(csv.DictReader(f))
n = len(orders)
unique_order_ids = {row["order_id"] for row in orders}
late = sum(1 for row in orders if row["target_late"] == "1")
ratio = late / n
print(f"orders: {n}")
print(f"unique order_id: {len(unique_order_ids)}")
print(f"late count: {late}")
print(f"late ratio: {ratio:.4f} ({100 * ratio:.2f}%)")A fenti kódblokk kimenetét szándékosan nem tölthetjük ki a könyv
írásakor azzal a látszattal, hogy most futott. A szerződött darabszámok
viszont ismertek: orders: 2400 és
unique order_id: 2400 várható. Ha ez a kettő nem teljesül,
akkor a betöltött fájl nem az alap-adatszerződés szerinti, és a további
számítás értelmezhetetlen. A late count és a
late ratio értéke a te futtatásodból kerül a jegyzékbe;
ezek determinisztikusak a rögzített seed mellett, de itt nem adjuk meg
őket előre, hogy a becslés–futás–ellenőrzés ciklus valódi maradjon.
A három kontrollérték az adathigiénia ellenőrzése: a
fájlok a szerződött méretűek, tehát a későbbi elemzések ugyanarról a
kiindulópontról indulnak. A unique order_id szám
egyenlősége a rekordszámmal azt mutatja, hogy a tiszta
orders.csv-ben nincs duplikált rendelés; ez a későbbi
joinok előfeltétele. A késési arány ezzel szemben már
tartalmi mutató: a
pontbecslés egyetlen számmal foglalja össze a rendelésszintű késési
gyakoriságot. Ennek a pontbecslésnek a bizonytalanságát itt még nem
számszerűsítjük, de pontosan kimondjuk, hogy egyetlen szintetikus minta
aránya nem azonos a generáló folyamat bizonytalan paraméterével, és
végképp nem valós üzleti tény. A resampling-egység itt a rendelés; ha
ügyfélszintű állítást tennénk, az ügyfél lenne a mintavételi egység, a
tételszintű sor pedig soha nem adhat több mintát a rendelésekhez
képest.
Labdiagnózis és napló. Futtasd a zero-dependency labort, ellenőrizd kézzel a három kontrollértéket, és írj a hipotézisnaplóba öt olyan állítást, amelyet ez az adathalmaz nem támaszt alá. Mindegyik mellé egy mondatban indokold, miért nem.
Késési arány implementálása. Írj olyan
Python-programot, amely a data/orders.csv bemenetből kiírja
a rendelésszámot, az egyedi order_id-k számát, a
target_late == "1" sorok számát és a késési arányt
százalékban, két tizedesjeggyel. A fejléc tartalmát ne számold
rekordnak. Ellenőrizd, hogy az első két kiírt érték egyaránt
2400.
Hibadiagnózis. Egy tanuló a következő kódot
írta, és a késési darabszámra mindig 0 jött ki:
with open("data/orders.csv", newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
if row["target_late"] == 1:
late += 1Mi a hiba? Javítsd úgy, hogy a szöveges mezőt ne alakítsd át úgy, hogy a hiányzó érték rejtett hibává váljon.
Megváltozott adat. A
data/orders_raw.csv szándékosan nem a tiszta állomány: 2405
törzssort tartalmaz, benne öt extra duplikált sorral, két hiányzó
distance_km értékkel, egy negatív basket_minor
értékkel és egy nem létező customer_id-val. Adj három
konkrét auditlépést, amely ezeket elkülöníti, és indokold, miért nem
automatikusan törlünk minden gyanús sort.
Öt állítás, amelyet az adat nem támaszt alá — lehetséges megoldás.
distance_km érték nulla kilométert jelent,
ezért átlag helyettesíthető.” A hiányzó érték nem nulla; az
adatszerződés szerint ez hiba, nem nulla távolság.Megvalósítás. A fenti
target_late-kód a megoldás. Ellenőrző invariantok: a fájl
törzssorainak száma 2400; az egyedi order_id-k száma
szintén 2400;
,
ezért a kiszámolt arány
-beli.
A pontos late count a futtatásból jön; a kimenetet a futási
jegyzékbe kell rögzíteni, és a hash-ellenőrzéshez hasonlóan itt sincs
tolerancia a véletlenül elírt fájlra.
Hibadiagnózis. A csv.DictReader
minden mezőt karakterláncként ad vissza, így
row["target_late"] értéke "0" vagy
"1", nem egész szám. A "1" == 1 kifejezés
Pythonban False, ezért a számláló soha nem nő. Helyes
összehasonlítás: row["target_late"] == "1". Az
int(row["target_late"]) is működőképes, de csak akkor, ha
előtte ellenőrizzük, hogy a mező pontosan "0" vagy
"1"; különben a hiányzó vagy hibás érték
ValueError-ként derül ki egy olyan ponton, ahol már
nehezebb visszafejteni a forrássort.
Auditlépések a nyers állományra.
order_id szerint, és listázd azokat,
amelyekből egynél több sor van; ezek a duplikátumok auditrekordba
kerülnek.distance_km értékeket, és
ellenőrizd a tiszta fájl sormennyiségét is; a hiányt ne pótold nullával
csendben.basket_minor értékeket, és végezz
anti-join ellenőrzést a customers.customer_id oszlopával az
ismeretlen ügyfél megtalálásához.A leggyakoribb tévedés ezen a ponton az, hogy a szintetikus adaton
látott arányt valós üzleti bizonyítékként mondjuk fel. Ellenpélda: ha a
tiszta orders.csv-ben a távoli rendelések körében
magasabbnak látszik a késési arány, ebből még nem állítható, hogy a
valódi bolt szállítási folyamata a távolsággal romlik. A labor egy
mesterséges, paraméterezett szimuláció; a tanulság a munkamenetek
szerkezetére vonatkozik, nem a valós vállalat teljesítményére. Ez
visszautal a 1.3 hipotézisnaplójára is: a “megfigyeltem, hogy…” és a “az
adat igazolja, hogy…” közötti lépést mindig külön kell rögzíteni, és a
másodikhoz már következtetési szabály kell, nem csak egy CSV.
Önellenőrzés. Mielőtt továbblépnél, tudd megválaszolni: mi a megfigyelési egység a három fájlban külön-külön? Mi a nevező a késési arányban? Mi a különbség a 2 százalékpont és a 2 százalékos növekedés között? Meg tudod-e mondani minden elkészült állításodról, hogy adatból, feltételezésből vagy modellből következik-e? Ha valamelyikre nem, az a diagnózis következő tennivalója.
Diagnosztikai elfogadási kapu. A teljes munkamenet sikerét ne kizárólag a script nulla kilépési kódjához kösd. Külön ellenőrizd a bemeneti sémát, az egyedi rendelésazonosítókat, a hiányzások számát, a célképzés szabályát, a címke rendelkezésre állását és a kimeneti sorok számát. Az elfogadási jegyzőkönyv rögzítse az egyes állításokhoz tartozó megfigyelt értéket.
Szintetikus adaton mért késési arányból csak a generált állományra lehet közvetlenül következtetni. Valós működési ajánláshoz külön szükséges a mintavételi folyamat, az éles adatminőség és az időbeli általánosíthatóság igazolása.
A diagnosztikai munkamenet három hibacsaládot választ szét: a programozási hiba a kód és a típusok szintjén jelentkezik; az adatértelmezési hiba a szemcsézettség és a mezők jelentésének félreértéséből ered; a statisztikai hiba pedig akkor, amikor egy pontbecslést bizonytalanság és mintavételi egység nélkül tényként kezelünk. A Northstar labor három kontrollértéke a tiszta adat szerződött méretét ellenőrzi, a késési arány pedig az első valódi alapmutató. A labort a tanuló futása és jegyzéke teszi megismételhetővé; a szintetikus adat tanulási minta, nem üzleti bizonyíték. A következő fejezetben ugyanezt a fegyelmet visszük át az operációs rendszer és a terminál szintjére: a fájlok, útvonalak és hibakimenetek kezelésére.