Az Adatteszt és numerikus teszt alfejezetben
felépített northstar modul rendelkezik adattesztekkel, de a
features.py hibás account_age_days függvényt
tartalmaz: a signup_date timezone-naiv, az
order_ts viszont UTC-aware, ezért a kivonás futásidőben
TypeError-t dob. Ebben az alfejezetben nem maga a
hibajavítás a fő cél, hanem az, hogyan lehet a javítást review-zhatóvá
tenni: kis, áttekinthető diff, a probléma és a validáció rövid leírása,
statikus eszközökön átmenő kód és emberi ellenőrzésre alkalmas
összefoglaló.
A bemenet az Adatteszt és numerikus teszt
labor src/northstar/features.py fájlja, a megfigyelési
egység egy rendeléshez tartozó order_ts és
signup_date pár. Az eredmény a kettő különbsége egész
napokban, az elvárt ellenőrzés pedig egy determinisztikus pytest eset és
a formai ellenőrzések. A lab/ADATOK.md szerint
signup_date naptári dátum, order_ts
UTC-időbélyeg; a javításnak ezt a szerződést kell kodifikálnia.
Induló feladat: Mielőtt a konkrét javítást látná, becsülje meg, mit tapasztal a felhasználó a hibás függvény futtatásakor, és miért nem elég a linter a hiba elkerüléséhez. Becslése után dolgozza végig a minimális példát.
A kódminőségi eszközöket négy rétegként érdemes szétválasztani: formatter, linter, type checker és code review. Az első három automatikus, az utolsó emberi.
check parancsa ilyen. A linter nem tudja, hogy a
distance_km rossz mértékegységet kap-e; azt az
adatvalidáció és a teszt ellenőrzi.Egy kézi ellenőrzés az automatikus eszközök értelmezésére:
# A linter F401-et ad, mert a pd nincs használva.
import pandas as pd
def late_target(delivery_days):
# A linter zöld, pedig a delivery_days a valós címkéből képzett,
# és prediktorként használva leakage-t okozna.
return delivery_days > 7A formatter és linter ezen a kódon nem jelez, a típusellenőrző sem
feltétlenül. A függvény mégis tudományosan hibás, mert utólag ismert
információt használ. A linter csak a pd import hiányát
jelzi, a leakage-t nem.
A kezdőlabor lab/ könyvtárában dolgozunk. A
projektstruktúra az Adatteszt és numerikus teszt
alfejezetből ismert; az adatfájlokat a lab/ADATOK.md rögzíti. A példa a
src/northstar/features.py hibás függvényét javítja.
A hibás kód:
# src/northstar/features.py
import pandas as pd
def account_age_days(order_ts: pd.Timestamp, signup_date: pd.Timestamp) -> int:
"""Return days between signup date and order timestamp."""
return (order_ts - signup_date).daysA formatter és a linter itt nem jelez, a mypy sem feltétlenül; a
pytest viszont TypeError-t ad, mert a naive
signup_date és tz-aware order_ts kivonása a
pandasban futásidőben hibás.
A javított kód:
# src/northstar/features.py
from __future__ import annotations
import pandas as pd
def account_age_days(order_ts: pd.Timestamp, signup_date: pd.Timestamp) -> int:
"""Return whole days between signup date and order timestamp.
The contract in lab/ADATOK.md says signup_date is a calendar date and
order_ts is UTC. The signup date is therefore interpreted as UTC midnight.
"""
signup_utc = pd.Timestamp(signup_date).tz_localize("UTC")
delta = order_ts - signup_utc
return int(delta.days)A hozzá tartozó teszt:
# tests/test_features.py
from pandas import Timestamp
from northstar.features import account_age_days
def test_account_age_days_interprets_signup_as_utc_midnight():
result = account_age_days(
order_ts=Timestamp("2025-02-15T23:30:00+00:00"),
signup_date=Timestamp("2025-01-01"),
)
assert result == 45A parancsok a lab/ munkakönyvtárból futtathatók; Linux
shell és Windows PowerShell esetén is a uv a projekt
pyproject.toml-ját keresi:
cd lab
uv sync --group dev
uv run pytest tests/test_features.py
uv run ruff check src tests
uv run ruff format --check src tests
uv run mypy srcA uv sync --group dev a pyproject.toml
verziótartományait oldja fel; a felhasználó által létrehozott
uv.lock után a futtatás --locked opcióval
őrizhető. A fenti kimenetek ezért szemléltető, elméletileg várt
eredmények, nem a jelen szöveg által futtatott mérési jegyzék. A várt
viselkedés: a pytest zöld, a ruff check hibátlan, a
formatter stílusellenőrzése sikeres, a mypy az annotációkat
elfogadja.
A pyproject.toml minőségi eszközökért felelős
részlete:
[project]
name = "northstar-lab"
version = "0.1.0"
requires-python = ">=3.12"
[project.optional-dependencies]
dev = ["pytest>=8.0", "ruff>=0.5", "mypy>=1.10"]
[tool.ruff]
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]
[tool.mypy]
python_version = "3.12"A select listában az E és F a
hibaszabályokat, az I az importrendezést, az
UP a Python-verziófüggő egyszerűsítéseket jelenti; a valós
konfiguráció a labor saját lab/pyproject.toml-ja szerint
alakul.
A review-zható változtatás leírója:
Problem: account_age_days raised TypeError when order_ts was UTC-aware.
Cause: signup_date was a naive calendar date; pandas refuses aware-naive subtraction.
Fix: interpret signup_date as UTC midnight and keep order_ts unchanged.
Validation: tests/test_features.py::test_account_age_days_interprets_signup_as_utc_midnight
pytest, ruff check, ruff format --check, mypy src.
Manual check: Timestamp("2025-02-15T23:30+00:00") - Timestamp("2025-01-01 00:00+00:00")
equals 45 days and 23:30; .days returns 45.
Risk: the UTC midnight interpretation is an explicit change; if the business team
uses local midnight, the feature is off by the offset. The reviewer must confirm.
A diff:
@@ -1,8 +1,16 @@
import pandas as pd
def account_age_days(order_ts: pd.Timestamp, signup_date: pd.Timestamp) -> int:
- return (order_ts - signup_date).days
+ signup_utc = pd.Timestamp(signup_date).tz_localize("UTC")
+ delta = order_ts - signup_utc
+ return int(delta.days)A formatter a from __future__ import annotations és az
import sorrendjét is rendezi; a linter a hiányzó sort jelzi, ha
elfelejtik.
A lokális pre-commit hook a commit pillanatában
akadályozza a nyilvánvaló hibákat; a központi CI a pull request szintjén
futtatja ugyanezeket, és a review előtt ad automatikus bizonyítékot.
A .pre-commit-config.yaml példa:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0 # example, pin to the reviewed version
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0 # example, pin to the reviewed version
hooks:
- id: mypy
args: [src]
pass_filenames: falseA rev értékek itt elrendezést mutatnak; a projektnek a
saját, ellenőrzött verziójához kell igazítania őket. A telepítés a
uv run pre-commit install parancs után a
git commit alkalmával futtatja a hookokat. A pre-commit nem
helyettesíti a CI-t: a hookok kikerülhetők, a CI viszont központilag
naplózza a futást.
A minimális CI workflow:
name: ci
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv sync --locked --group dev
- run: uv run pytest
- run: uv run ruff check src tests
- run: uv run ruff format --check src tests
- run: uv run mypy srcA --locked csak akkor futtatható, ha az
uv.lock a repóban van; a kezdőlaborban ezt a felhasználó
hozza létre. Az action verziók is ellenőrzést igényelnek a projekt adott
időponti függőségi politikája szerint.
A függvény a determinisztikus bemenetre 45 napot ad.
Kézi kontroll: 2025-02-15 23:30 UTC -
2025-01-01 00:00 UTC = 45 nap 23:30, a
Timedelta.days egész napokra levág, így 45. A
javítás megszünteti a TypeError-t; az automatikus eszközök
formailag zölden haladnak. A teszt invariáns: a bemeneti
signup_date mindig Timestamp naptári dátum, az
order_ts mindig tz-aware, az eredmény nemnegatív egész. A
függvény skalárt ad vissza, ezért DataFrame-shape nem változik; a dtype
int. A statisztikai értelmezés itt nincs, mert nem vezetünk
be új matematikát; a feature-re ható tudományos döntést a review hagyja
jóvá.
Statikus típusellenőrzés nem bizonyítja a modellezési vagy statisztikai helyességet; a review ellenőrizze az estimandot, az időhelyes feature-öket és a splitet is.
Értelmezés: a linter F401 hibát ad, ha az
import pandas as pd nincs használva. Magyarázza meg, miért
nem ellenőrzi a linter, hogy a target_late oszlop
prediktorként szerepel-e az orders táblában. Adjon olyan
features.py részletet, ahol a linter zöld, de a leakage
miatt a modell mesterségesen jó lesz.
Megvalósítás: egészítse ki az
account_age_days docstringjét azzal, hogy milyen hibát
jelez, ha order_ts nem tz-aware. Írja be a típusannotációt
a def day_floor(timestamp: pd.Timestamp) -> pd.Timestamp
függvényhez, amely a nap kezdetére kerekít, és futtassa elméletileg a
ruff check és mypy parancsokat.
Hibakeresés: a ruff check a
következő hibát adja: F821 Undefined name 'pd'. A
features.py aktuális változatában az
import pandas as pd sor a fájl végére került. Hogyan
észleli a linter a hibát? A mypy is jelezné-e? Indokolja, és adja meg a
javított fájlt.
Transzfer: a Northstar segment
értéke consumer vagy business. A
build_features függvényben a hibás
is_business = (segment == "biz").astype(int) kifejezés a
business kategóriát sosem találja el. A teszt mégis átmegy,
mert a fixture-ben segment="biz" szerepel. Készítsen
review-összefoglalót, amely a cél definícióját kéri számon, nem a
lintert, és javítsa a tesztet.
delivery_days a rendelés után ismert, ezért a Feature store és adatkontraktus szükségessége
alfejezetben megismert időhelyességi szerződésből kizárt prediktor.
Példa:def build_features(orders):
# A linter zöld, de a delivery_days a valós címkéből képzett.
orders["late_hint"] = (orders["delivery_days"] > orders["promised_days"]).astype(int)
return orders[["distance_km", "late_hint"]]A linter nem ismeri az időrendet; a cél helyességét a teszt adatszerződés és az emberi review ellenőrzi.
def account_age_days(order_ts: pd.Timestamp, signup_date: pd.Timestamp) -> int:
"""Return whole days between signup date and order timestamp.
Args:
order_ts: UTC timestamp of the order.
signup_date: calendar date of signup, interpreted as UTC midnight.
Returns:
Non-negative whole days elapsed.
Raises:
ValueError: if order_ts is not timezone-aware.
"""
if order_ts.tzinfo is None:
raise ValueError("order_ts must be timezone-aware")
signup_utc = pd.Timestamp(signup_date).tz_localize("UTC")
return int((order_ts - signup_utc).days)
def day_floor(timestamp: pd.Timestamp) -> pd.Timestamp:
"""Return the timestamp normalized to the start of its day."""
return timestamp.normalize()A ruff check és mypy src elméletileg várt
kimenete All checks passed! és
Success: no issues found; a teszt numerikus várt értéke a
korábbi 45, mert a day_floor a
2025-02-15 00:00-t ad.
A linter a fájlban a pd név használatakor nem talál
megfelelő importot a modul szintjén, ezért F821 hibát ad. A mypy
ugyanezt jelzi, mert a nevet a statikus környezetben nem látja; a linter
viszont kifejezetten a kód szerkezeti hibáira fókuszál, ezért gyakran
egyszerűbb réteg. A javítás az import pandas as pd
mozgatása a fájl elejére, a
from __future__ import annotations után; a javított fájlban
a linter és a mypy is hibátlan.
A review összefoglaló:
Request: the business_flag feature is always 0 in staging.
Hypothesis: segment == "biz" never matches the ADATOK.md value "business".
Evidence: the unit test uses a fixture segment="biz" that does not follow
the data contract; therefore the test passes while production fails.
Fix: update the fixture to "business", change the expression to
(segment == "business"), and add a contract test that compares the
distinct segment values in customers.csv with {consumer, business}.
A cél definíciója az adatszerződésben van, nem a linterben. A javítás
után a teszt a valódi kategóriaértéket használja, és a hibás
biz érték már nem szerepel sem a kódban, sem a
fixture-ben.
A leggyakoribb tévhit, hogy a „lintermentesség” tudományos
helyességet jelent. A formatter, linter és típusellenőrző a kód
mechanikai és statikus tulajdonságait ellenőrzi; nem a céldefiníciót, a
leakage-t vagy a következtetést. Ellenpélda: a Northstar
starter_experiment.py-ban ha valaki a
delivery_days oszlopot prediktorként használja, a
ruff check és a mypy nem panaszkodik, pedig a
modell a célváltozóhoz tartozó, a rendelés pillanatában ismeretlen
információt használ, ami a Feature store és
adatkontraktus szükségessége kontraktus megsértése. Ugyanígy a GLM: bináris és számlálási cél alfejezetben
tárgyalt odds ratio kódolása lehet lintertiszta; ha a
kategória-referenciát rosszul választjuk, az értelmezés téves. A
review-nak ezért mindig a tudományos kérdést is leltárba kell vennie: a
cél definícióját, a predictorok időhelyességét, a splitet és a
következtetés határait. A linter segít a hibák csökkentésében, de nem
helyettesíti az emberi ítéletet.
Önellenőrzés: Ha egy diff csak formázási
változtatásokat tartalmaz, a review-nak nem szabad a céldefiníciót
jóváhagyni. Ha a diff a signup_date értelmezését
változtatja, a review-nak a lab/ADATOK.md
UTC-éjfél szerződését kell hivatkoznia.
A formatter, linter és típusellenőrző három automatikus réteg,
amelyek a kód formáját, statikus hibáit és típusegyezését javítják; az
üzleti definíciót és a tudományos következtetést nem bizonyítják. A
példában az account_age_days UTC-éjfél értelmezését
teszttel, Ruffal, mypy-val és review-leíróval tettük ellenőrizhetővé. A
reviewer a céldefiníciót, az adatszerződést, az időbeli helyességet és a
validációt kéri számon; a lintmentesség csak belépési küszöb. A
következő alkalmazásban a Feldolgozási gráf és
idempotencia a feldolgozási gráfok idempotenciáját és adatminőségi
kapuját építi ezekre az automatikus ellenőrzésekre.