Werkplaats / Notebook
In jouw eigen tempo
← Alle bouwprojecten
Data · PYTHON NOTEBOOK

Bouw een herbruikbare data-pipeline

Combineer imputatie, standaardisatie en categorieën; controleer onbekende invoer en maak de transformatie zichtbaar.

90 min4 cellen
De opdracht, leerdoelen en voorkennis
  • Je bouwt een fit/transform-pipeline met een vaste kolomvolgorde.
  • Je test dat nieuwe en ontbrekende waarden de trainingsparameters niet veranderen.
  • Je maakt een grafiek van werkelijk getransformeerde data.

Lees eerst: Leeg is niet hetzelfde als nul · Verander de eenheid, behoud de betekenis · Een kleur is geen rangnummer.

Je hebt geleerd om ontbrekende hoogtes in te vullen, getallen te schalen en kleuren te coderen. Nu moeten die stappen samen dezelfde betekenis houden bij een nieuwe plant. Eén vergeten kolomvolgorde of opnieuw geleerd testgemiddelde kan de hele voorbereiding veranderen.

Je bouwt een data-pipeline met een bewaarde toestand en controleert de uitvoer met een grafiek. De planten zijn fictief; dit project bewijst de voorbereiding, niet hoe goed een plantenmodel voorspelt. We gebruiken kleine eindige hoogtes, None of float-NaN voor ontbrekende metingen en tekstkleuren.

1. Begin met herhaalbare, onafhankelijke bronrijen

Een notebook bewaart variabelen tussen cellen, de afzonderlijke codeblokken. Voer ze van boven naar beneden uit: gegevens, fit, transform en audit. Verander je een eerdere cel, voer dan ook de afhankelijke latere cellen opnieuw uit; oude uitkomsten worden niet vanzelf vernieuwd.

Maak gegevens() met zes trainingshoogtes [2,4,None,8,10,12] en kleuren ["rood","blauw","rood","blauw","rood","blauw"]. Test bevat achtereenvolgens (None,"groen"), (20,"rood") en (6,"blauw"). Iedere rij is een dictionary met hoogte en kleur.

Maak de lijsten én dictionaries binnen de functie. Dan krijgt iedere aanroep eigen rijen. Dit kleinere voorbeeld laat zien waarom:

def klein_voorbeeld():
    return [
        {"hoogte":2, "kleur":"rood"},
        {"hoogte":None, "kleur":"blauw"},
        {"hoogte":6, "kleur":"rood"}
    ]

a = klein_voorbeeld()
b = klein_voorbeeld()
a[0]["hoogte"] = 999
print(b[0]["hoogte"])   # 2

Bewaar de grote oefendatasets met train, test = gegevens(). Deze schrijfwijze verdeelt het teruggegeven tuple over twee namen: eerst training, daarna test. Alleen een nieuwe buitenlijst maken is onvoldoende als de dictionaries daarbinnen gedeeld blijven.

01
CHECKPOINTLeg de twee datasets vast

2. Leer invulling, schaal en kleuren in die volgorde

We rekenen eerst het kleine voorbeeld door. De bekende hoogtes 2 en 6 geven invulwaarde 4. Na invullen staan er 2, 4 en 6: gemiddelde 4, maar de gemiddelde gekwadrateerde afwijking is nu 8/3 in plaats van 4.

s=4+0+43≈1.633

s is de schaal van de ingevulde training. Gemiddelde-imputatie voegt een punt in het midden toe en verkleint hier de spreiding. De volgorde van deze stappen hoort daarom bij je contract.

Gebruik opnieuw de ontbreekcontrole uit de datakwaliteitsles, zodat None en NaN dezelfde behandeling krijgen:

import math
def ontbreekt(waarde):
    if waarde is None:
        return True
    if type(waarde) == float:
        return math.isnan(waarde)
    return False

mini = {
    "fill": 4,
    "mean": 4,
    "scale": math.sqrt((4 + 0 + 4) / 3),
    "vocab": ["blauw", "rood"]
}
print(mini["scale"]) # ongeveer 1.633
print(ontbreekt(float("nan"))) # True

Maak fit_pipeline(rows) in de tweede cel: verzamel bekende hoogtes, leer hun gemiddelde als fill, vul een nieuwe hoogtelijst en leer daarop mean en scale. Gebruik scale 1 bij nulspreiding en leer de gesorteerde unieke kleuren uit alle trainingsrijen. Bewaar deze vier onderdelen in één dictionary.

Zonder bekende hoogte, ook bij lege training, geef je ValueError. Bewaar state = fit_pipeline(train); voor de grote dataset zijn fill en mean beide 7.2. De kleur groen mag niet in state verschijnen, omdat zij alleen in test staat.

02
CHECKPOINTLeer en bewaar alle keuzes

3. Bouw iedere rij met dezelfde kolommen

De rij krijgt deze vaste volgorde: z-hoogte, ontbreekvlag, one-hot-kolommen en onbekendvlag. De twee vlaggen worden hier numerieke kolommen met 0 of 1. Een ontbrekende hoogte met onbekende kleur groen wordt bij onze mini-toestand [0,1,0,0,1].

waarde = None
kleur = "groen"
mist = 0
if ontbreekt(waarde):
    waarde = mini["fill"]
    mist = 1
z = (waarde - mini["mean"]) / mini["scale"]
rij = [z, mist]
for i in range(len(mini["vocab"])):
    vakje = 0
    if kleur == mini["vocab"][i]:
        vakje = 1
    rij.append(vakje)
onbekend = 0
if kleur not in mini["vocab"]:
    onbekend = 1
rij.append(onbekend)
print(rij)   # [0.0, 1, 0, 0, 1]

Een booleanfunctie kun je direct achter if gebruiken: de ingesprongen regels draaien wanneer zij True teruggeeft. Maak transform_pipeline(rows, state) door dit per bronrij in een nieuwe uitvoerlijst te doen. Laat zowel bronrijen als state intact en leer hier niets opnieuw.

Bewaar beide matrices als x_train en x_test. Veelgemaakte fout: een ingevulde waarde behandelen alsof ze echt gemeten was. De ontbreekvlag bewaart dat onderscheid, ook wanneer de ingevulde z-hoogte toevallig nul is.

03
CHECKPOINTTransformeer met een vast contract

4. Teken de werkelijk berekende waarden

Een scatterplot, of spreidingsdiagram, zet voor ieder voorbeeld een punt op coördinaten x en y. Hier is x de berekende z-hoogte en dient y alleen om training en test uit elkaar te houden. De bibliotheek Matplotlib tekent die punten in de notebookuitvoer.

Dit voorbeeld gebruikt onze mini-toestand en de zojuist berekende rij:

import matplotlib.pyplot as plt
train_z = []
hoogtes = [2, 4, 6]
for i in range(len(hoogtes)):
    z = (hoogtes[i] - mini["mean"])
    train_z.append(z / mini["scale"])
plt.figure(figsize=(6, 3))
plt.scatter(train_z, [0, 0, 0],
            label="training")
plt.scatter([rij[0]], [1], label="test")
plt.yticks([0, 1], ["training", "test"])
plt.xlabel("Geschaalde hoogte")
plt.legend()
plt.tight_layout()

as plt geeft de bibliotheek een korte naam. figure begint een nieuwe figuur; scatter ontvangt even lange lijsten met x- en y-coördinaten. Label benoemt een puntenreeks, legend toont die namen en tight_layout maakt ruimte voor de teksten.

Maak audit_pipeline(train, test): fit alleen op training, transformeer beide datasets en teken hun eerste kolom. Voor de y-coördinaten herhaalt [0] * aantal nul zoveel keer; test gebruikt enen. De notebook toont de figuur automatisch.

Geef ook breedte, test_onbekend, test_ontbreekt en state terug en bewaar dit als rapport. Tel kolom 1 voor ontbrekende hoogtes en de laatste kolom voor onbekende kleuren; index -1 kiest het laatste element.

Maak voor een extra experiment aparte gegevens met train_proef, test_proef = gegevens(). Zet test_proef[1]["hoogte"] = 200 en voer audit_pipeline(train_proef, test_proef) uit: de punten verschuiven, maar de geleerde toestand blijft gelijk. De projectvariabelen train, test, x_train, x_test en rapport blijven zo bij de oorspronkelijke opdracht horen wanneer je Controleer project kiest.

04
CHECKPOINTOnderzoek het resultaat met een echte grafiek

Wat neem je mee?

  • Voer afhankelijke cellen opnieuw uit en houd de bronrijen onafhankelijk van eerdere experimenten.
  • Leer voorbereiding uitsluitend op training; behoud ontbreek- en onbekendvlaggen.
  • Controleer de berekende matrices én hun grafiek; een plot bewijst nog geen modelkwaliteit.

In de volgende track gebruik je zulke voorbereide kenmerken om zelf een voorspelmodel te leren.

Bronnen: scikit-learn — data leakage en pipelines ↗ Matplotlib — punten tekenen met scatter ↗ · Gecontroleerd 2026-10-28.

Python start zodra jij code uitvoert.
[1]

Leg de twee datasets vast

Maak de zes trainingsrijen en drie testrijen uit de opdracht. Iedere aanroep geeft nieuwe lijsten en eigen dictionaries; bewaar train en test.

Bekijk voorbeeld
def gegevens():
    train = [
        {"hoogte": 2, "kleur": "rood"},
        {"hoogte": 4, "kleur": "blauw"},
        {"hoogte": None, "kleur": "rood"},
        {"hoogte": 8, "kleur": "blauw"},
        {"hoogte": 10, "kleur": "rood"},
        {"hoogte": 12, "kleur": "blauw"}
    ]
    test = [
        {"hoogte": None, "kleur": "groen"},
        {"hoogte": 20, "kleur": "rood"},
        {"hoogte": 6, "kleur": "blauw"}
    ]
    return (train, test)


train, test = gegevens()
print("Training:", len(train), "Test:", len(test))
[2]

Leer en bewaar alle keuzes

Gebruik ontbreekt voor None én float-NaN. Leer fill uit bekende training, daarna mean/scale over ingevulde training en de gesorteerde kleuren. Geen bekende hoogte geeft ValueError; bewaar state.

Bekijk voorbeeld
import math


def ontbreekt(waarde):
    if waarde is None:
        return True
    if type(waarde) == float:
        return math.isnan(waarde)
    return False


def fit_pipeline(rows):
    known = []
    kleuren = []
    for i in range(len(rows)):
        hoogte = rows[i]["hoogte"]
        if ontbreekt(hoogte) == False:
            known.append(hoogte)
        kleuren.append(rows[i]["kleur"])
    if len(known) == 0:
        raise ValueError("Geen gemeten trainingshoogtes")
    totaal = 0
    for i in range(len(known)):
        totaal = totaal + known[i]
    fill = totaal / len(known)
    filled = []
    totaal = 0
    for i in range(len(rows)):
        hoogte = rows[i]["hoogte"]
        if ontbreekt(hoogte):
            hoogte = fill
        filled.append(hoogte)
        totaal = totaal + hoogte
    mean = totaal / len(filled)
    kwadraten = 0
    for i in range(len(filled)):
        verschil = filled[i] - mean
        kwadraten = kwadraten + verschil ** 2
    scale = math.sqrt(kwadraten / len(filled))
    if scale == 0:
        scale = 1.0
    return {"fill": fill, "mean": mean,
            "scale": scale, "vocab": sorted(set(kleuren))}


state = fit_pipeline(train)
print("Geleerde toestand:", state)
[3]

Transformeer met een vast contract

Maak nieuwe rijen met z-hoogte, ontbreekvlag, one-hot-kolommen en onbekendvlag. Alleen de gegeven state bepaalt de bewerking; bewaar x_train en x_test en verander bron/state niet.

Bekijk voorbeeld
def transform_pipeline(rows, state):
    matrix = []
    for i in range(len(rows)):
        waarde = rows[i]["hoogte"]
        mist = 0
        if ontbreekt(waarde):
            waarde = state["fill"]
            mist = 1
        z = (waarde - state["mean"]) / state["scale"]
        rij = [z, mist]
        kleur = rows[i]["kleur"]
        for j in range(len(state["vocab"])):
            vakje = 0
            if kleur == state["vocab"][j]:
                vakje = 1
            rij.append(vakje)
        onbekend = 0
        if kleur not in state["vocab"]:
            onbekend = 1
        rij.append(onbekend)
        matrix.append(rij)
    return matrix


x_train = transform_pipeline(train, state)
x_test = transform_pipeline(test, state)
print("Kolommen: z-hoogte, ontbreekt,", state["vocab"], ", onbekend")
print("Testmatrix:", x_test)
[4]

Onderzoek het resultaat met een echte grafiek

Fit op training, transformeer beide datasets en teken hun berekende z-hoogtes in een nieuwe scatterplot. Geef breedte, test_onbekend, test_ontbreekt en state terug; bewaar rapport. Gebruik het plotvoorbeeld uit de uitleg.

Bekijk voorbeeld
def audit_pipeline(train, test):
    import matplotlib.pyplot as plt
    state = fit_pipeline(train)
    a = transform_pipeline(train, state)
    b = transform_pipeline(test, state)
    train_z = []
    test_z = []
    test_onbekend = 0
    test_ontbreekt = 0
    for i in range(len(a)):
        train_z.append(a[i][0])
    for i in range(len(b)):
        test_z.append(b[i][0])
        test_ontbreekt = test_ontbreekt + b[i][1]
        test_onbekend = test_onbekend + b[i][-1]
    plt.figure(figsize=(6, 3))
    plt.scatter(train_z, [0] * len(a), label="training")
    plt.scatter(test_z, [1] * len(b), label="test", marker="x")
    plt.yticks([0, 1], ["training", "test"])
    plt.xlabel("Hoogte met opgeslagen trainingsschaal")
    plt.title("Dezelfde voorbereiding, twee datasets")
    plt.legend()
    plt.tight_layout()
    return {"breedte": len(a[0]),
            "test_onbekend": test_onbekend,
            "test_ontbreekt": test_ontbreekt,
            "state": state}


rapport = audit_pipeline(train, test)
print("Audit:", rapport)