Pandas & NumPy Academy · Oppitunti

Putken vaiheiden testaaminen assertion-tarkistuksilla

Lisätkää rivimäärien tarkistukset, null-arvojen assertion-tarkistukset ja odotettujen sarakkeiden tarkistukset jokaiseen vaiheeseen, jotta virheet havaitaan heti.

Oppitunti 3/413 vaihetta

Putken vaiheiden testaaminen assertion-tarkistuksilla on ilmainen Pandas & NumPy Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Pandas & NumPy Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Pandas & NumPy Academy-kurssilla on yhteensä 4 oppituntia.

Miksi putken vaiheet kannattaa testata?

Virheittä suorittuva datan käsittelyputki voi silti tuottaa huomaamatta virheellisen tuloksen: väärä suodatin voi poistaa rivejä, sarake voidaan kertoa väärällä kertoimella tai yhdistäminen voi monistaa rivejä, koska liitoksen avain ei ollut yksikäsitteinen. Ainoa tapa havaita tällaiset hiljaiset virheet on lisätä assertioita, jotka tarkistavat datan odotetut ominaisuudet putken jokaisessa vaiheessa. Assertiot muuttavat loogiset virheet selkeiksi ja välittömästi näkyviksi virheiksi.

import pandas as pd
import numpy as np

# A transform that looks correct but has a bug:
def compute_revenue_buggy(df):
    # BUG: unit_price should be multiplied, not added
    df['revenue'] = df['quantity'] + df['unit_price']
    return df

# Without assertions, this runs silently with wrong numbers.

Rivimäärätarkistukset

Assertioikaa jokaisen suodatusvaiheen jälkeen, että tuloksena oleva rivimäärä on odotetulla välillä. Liian pieni rivimäärä tarkoittaa, että suodatin oli liian tiukka; liian suuri rivimäärä tarkoittaa, että yhdistäminen monisti tietueita. Ilmaiskaa odotettu vaihteluväli syötteen murto-osana: esimerkiksi tyhjien arvojen poistaminen ei saisi hyvälaatuisessa datassa poistaa yli 30 % riveistä. Tämä suojaus havaitsee ylemmän tason lähteiden odottamattomat datan laadun muutokset.

def drop_nulls_guarded(df, required_cols, max_drop_fraction=0.3):
    before = len(df)
    result = df.dropna(subset=required_cols)
    after = len(result)
    drop_fraction = (before - after) / before
    assert drop_fraction <= max_drop_fraction, \
        f'dropna removed {drop_fraction:.1%} of rows (limit {max_drop_fraction:.1%})'
    return result

Sarakkeiden olemassaolon tarkistukset

Assertioikaa jokaisen muunnosfunktion jälkeen, että kaikki odotetut tulossarakkeet ovat olemassa. Jos uudelleennimeämisvaihe käyttää vahingossa väärää avainta, tuloksena oleva sarake puuttuu — ja virhe havaitaan vasta paljon myöhemmin, kun jokin toinen vaihe yrittää käyttää sitä. Heti muunnoksen jälkeen tehtävä assertio havaitsee ongelman sen lähteellä sen sijaan, että se ilmenisi kolme vaihetta myöhemmin hämmentävänä KeyError-virheenä.

def compute_revenue(df):
    df = df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

    # Guard: check that the new column exists and is non-null
    assert 'revenue' in df.columns, 'revenue column not created'
    assert df['revenue'].notna().all(), 'revenue has unexpected NaNs'
    return df

Arvoalueiden assertiot

Assertioikaa liikevaihtosarakkeen laskemisen jälkeen, että sen arvot eivät ole negatiivisia. Päivämäärien jäsentämisen jälkeen assertioikaa, että ne kuuluvat odotettuun vuosiväliin. Luokkien koodauksen jälkeen assertioikaa, ettei odottamattomia koodeja esiinny. Jokainen assertio on datan sopimus, joka dokumentoi odotetun toiminnan ja havaitsee rikkomukset varhain. Kirjoittakaa ne print-lauseiden sijaan assertioiksi, jotta ne aiheuttavat virheen automaattisissa putkisuorituksissa.

def validate_computed_columns(df):
    assert (df['revenue'] >= 0).all(), \
        f'Negative revenue: {df[df["revenue"] < 0]["revenue"].head().tolist()}'
    assert (df['quantity'] > 0).all(), \
        'Non-positive quantity found'
    assert df['revenue'].between(0, 1_000_000).all(), \
        'Revenue out of plausible range'
    print('Value range checks passed.')

Monistumisen estävät suojaukset yhdistämisen jälkeen

Yleinen datavirhe on monta-moneen-yhdistäminen, joka vahingossa monistaa rivejä. Assertioikaa jokaisen pd.merge()-kutsun jälkeen, että rivimäärä on odotettu — tavallisesti sen, ettei se ylitä vasemman DataFramen rivimäärää vasemmanpuoleisessa liitoksessa. Assertioikaa myös avainsarakkeen yksikäsitteisyys silloin, kun sen pitäisi olla yksikäsitteinen, jotta havaitsette vahingossa syntyvät ristiliitokset heti.

def safe_merge(left, right, on, how='left'):
    before = len(left)
    result = left.merge(right, on=on, how=how)
    after = len(result)

    if how == 'left':
        assert after == before, \
            f'Left join increased rows from {before} to {after} — check key uniqueness in right df'
    return result

Null-arvojen assertio kriittisten vaiheiden jälkeen

Tietyt sarakkeet eivät saa missään vaiheessa sisältää null-arvoja. Assertioikaa df['key_col'].notna().all() jokaisen sellaisen vaiheen jälkeen, joka voi vahingossa lisätä null-arvoja — esimerkiksi yhdistämisen jälkeen, jos kaikkia rivejä ei saada yhdistettyä (jolloin yhdistettyihin sarakkeisiin syntyy NaN-arvoja), tai map()-kutsun jälkeen, jos se palauttaa NaN-arvon kartoittamattomille arvoille. Tehkää näistä assertioista niiden muunnosfunktioiden kaksi viimeistä riviä, jotka käsittelevät kyseisiä sarakkeita.

NOT_NULL_AFTER_TRANSFORM = ['order_id', 'revenue', 'category']

def post_transform_checks(df):
    for col in NOT_NULL_AFTER_TRANSFORM:
        null_count = df[col].isna().sum()
        assert null_count == 0, \
            f'{col}: {null_count} unexpected NaN values after transform'
    print('Null checks passed.')
    return df

Testikokonaisuuden rakentaminen putken vaiheille

Kirjoittakaa jokaiselle putken funktiolle yksikkötestit käyttämällä pieniä, käsin rakennettuja DataFrameja, jotka eristävät testattavan logiikan. Jokaisen testin tulee valmistella minimaalinen syöte, kutsua funktiota ja assertioida tuloksen ominaisuudet. Pythonin sisäänrakennettu assert riittää yksinkertaisissa putkissa; suuremmissa projekteissa käyttäkää pytest-työkalua kaikkien testien automaattiseen suorittamiseen ennen käyttöönottoa.

def test_compute_revenue():
    test_df = pd.DataFrame({
        'quantity': [2, 3],
        'unit_price': [10.0, 5.0]
    })
    result = compute_revenue(test_df)

    assert 'revenue' in result.columns
    assert result['revenue'].tolist() == [20.0, 15.0]
    assert result['revenue'].dtype == float
    print('test_compute_revenue PASSED')

test_compute_revenue()

Reunatapausten testaaminen

Hyvät testit kattavat normaalin käyttötapauksen lisäksi myös reunatapaukset: kokonaan null-arvoja sisältävän syötteen, tyhjän DataFramen, yksirivisen DataFramen ja äärimmäisiä arvoja sisältävät sarakkeet. Tyhjän DataFramen tulee palauttaa tyhjä DataFrame, ei aiheuttaa virhettä. Yhden rivin sisältävän DataFramen tulee laskea tulos oikein. Testatkaa nämä tapaukset erikseen, jotta putki käsittelee ne tuotannossa hallitusti, kun syötteenä saapuu poikkeuksellista dataa.

def test_empty_df():
    empty = pd.DataFrame({'quantity': [], 'unit_price': []})
    result = compute_revenue(empty)
    assert len(result) == 0, 'Empty input should produce empty output'
    assert 'revenue' in result.columns, 'Revenue column should still be created'
    print('test_empty_df PASSED')

def test_single_row():
    single = pd.DataFrame({'quantity': [1], 'unit_price': [99.0]})
    result = compute_revenue(single)
    assert result['revenue'].iloc[0] == 99.0
    print('test_single_row PASSED')

test_empty_df()
test_single_row()

Integraatiotesti: koko putki esimerkkidatalla

Yksittäisten funktioiden yksikkötestien lisäksi kirjoittakaa integraatiotesti, joka suorittaa koko putken pienellä, todellista dataa edustavalla näytteellä. Assertioikaa, että tuloksessa on odotettu määrä sarakkeita, avainsarake on yksikäsitteinen ja kokonaisliikevaihto on uskottavalla vaihteluvälillä. Tämä päästä päähän -tarkistus havaitsee vaiheiden välisen vuorovaikutuksen virheet, joita yksikkötestit eivät paljasta.

def integration_test(config):
    raw = extract(config)
    clean = transform(raw, config)

    assert set(config['required_cols']).issubset(set(clean.columns))
    assert clean['order_id'].is_unique
    assert (clean['revenue'] >= 0).all()
    assert len(clean) > 0

    print(f'Integration test PASSED. Output: {clean.shape}')

integration_test(CONFIG)

Jatkuva testaus pytestillä

Putken kasvaessa kootkaa kaikki testit tests/-hakemistoon ja suorittakaa ne komentoriviltä komennolla pytest. conftest.py-tiedosto voi luoda jaettuja testivakioita, kuten esimerkkidataFrameja. Integroikaa pytest CI/CD-putkeen, jotta jokainen koodimuutos suorittaa automaattisesti kaikki testit ennen käyttöönottoa. Epäonnistunut testi estää käyttöönoton ja estää rikkinäisen koodin päätymisen tuotantoon.

# tests/test_transform.py — example structure
# import pytest, pandas as pd
# from pipeline.transform import compute_revenue, drop_nulls

# @pytest.fixture
# def sample_df():
#     return pd.DataFrame({'quantity': [2, 3], 'unit_price': [10.0, 5.0]})

# def test_revenue(sample_df):
#     result = compute_revenue(sample_df)
#     assert result['revenue'].tolist() == [20.0, 15.0]

print('Run: pytest tests/ -v  to execute all pipeline tests')

Assertiot elävänä dokumentaationa

Jokainen putken assertio on sekä suojaus että dokumentaation osa: se ilmaisee koneellisesti luettavassa muodossa, miltä datan on näytettävä kyseisessä kohdassa. Kun uusi analyytikko liittyy projektiin ja lukee putken koodia, assertiot kertovat hänelle datan sopimukset ilman erillistä määritysdokumenttia. Käsitelkää jokaista assertiota kuten kommenttia — tehkää viestistä riittävän kuvaava, jotta sen valvoma liiketoimintasääntö on ymmärrettävissä.

# These assertions document business rules as code:
assert (df['order_date'] >= pd.Timestamp('2020-01-01')).all(), \
    'Company was founded 2020-01-01; no orders can predate this'
assert df['region'].isin({'North', 'South', 'East', 'West', 'Central'}).all(), \
    'Only 5 sales regions are valid; new regions require config update'
print('Business rules verified as assertions.')

Pikatarkistus

Testatkaa tämän oppitunnin data-analyysin käsitteiden ymmärtämistä.

Oppitunnin kertaus

Tässä oppitunnissa opitte: lisäämään rivimäärä-, sarakkeiden olemassaolo-, arvoalue- ja null-arvotarkistukset putken sisäisinä assertioina, kirjoittamaan yksikkötestit yksittäisille muunnosfunktioille ja kattamaan reunatapaukset sekä suorittamaan integraatiotestejä ja käsittelemään assertioita elävinä datan sopimusten dokumentteina. Seuraavaksi tutustumme putkisuoritusten ajastamiseen ja lokitukseen automaattista päivittäistä suoritusta varten.

Aloita maksutta

Opi Python tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
30
Oppitunnit
120

Usein kysytyt kysymykset

Onko oppitunti ”Putken vaiheiden testaaminen assertion-tarkistuksilla” ilmainen?

Kyllä – oppitunnin ”Putken vaiheiden testaaminen assertion-tarkistuksilla” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Pandas & NumPy Academy-kurssin, päivitä CoddyKit PROhon. Pandas & NumPy Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Putken vaiheiden testaaminen assertion-tarkistuksilla”?

Lisätkää rivimäärien tarkistukset, null-arvojen assertion-tarkistukset ja odotettujen sarakkeiden tarkistukset jokaiseen vaiheeseen, jotta virheet havaitaan heti. Harjoittelet Pandas & NumPy Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Pandas & NumPy Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Pandas & NumPy Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Putken vaiheiden testaaminen assertion-tarkistuksilla”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Pandas & NumPy Academy-oppitunnilla?

Kyllä. Jokainen Pandas & NumPy Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Muunnosvaiheiden jäsentäminen funktioiksi
  2. Putkien parametrien määrittäminen config-sanakirjoilla
  3. Putken vaiheiden testaaminen assertion-tarkistuksilla
  4. Putkiajojen ajastus ja lokitus
← Takaisin: Pandas & NumPy Academy