- Je controleert dat beschrijvende metadata aanwezig zijn.
- Je scheidt kenmerken en doelvariabele en weigert ongeldige metingen.
Je wilt voorspellen of een jonge plant volgende week bloeit. Een tabel bevat hoogte en uren licht, maar zonder meetmoment weet je niet of de hoogte vóór of ná de bloei gemeten is. Meer rekenkracht kan dat ontbrekende verhaal niet reconstrueren.
Een datacontract (data contract) legt vast wat de gegevens betekenen en welke waarden je accepteert. We gebruiken fictieve kasmetingen in centimeters. De afspraken horen bij deze oefening; een andere plantensoort of meetmethode kan andere grenzen nodig hebben.
1. Geef de tabel een leesbaar etiket
Bij een zak zaden wil je weten welke soort erin zit en wanneer je mag zaaien. Gegevens hebben ook zo'n etiket nodig. Onze metadata, gegevens óver de metingen, noemen bron, eenheid, doelgroep en meetmoment.
Dit etiket beschrijft de hoogtekolom in centimeters; de lichtkolom heeft afzonderlijk uren als eenheid.
In Python past dit in een dictionary: een verzameling namen met bijbehorende waarden, geschreven tussen accolades. De naam heet een sleutel; een dubbele punt scheidt sleutel en waarde. Tekst staat tussen aanhalingstekens.
meta = {
"bron": "eigen kas",
"eenheid": "cm",
"doelgroep": "jonge planten",
"meetmoment": "dag 7"
}
print(meta["eenheid"]) # cm
print(meta.get("bron")) # eigen kas
print(meta.get("datum")) # None
Vier velden zijn gevuld, dus dit etiket voldoet aan onze aanwezigheidscontrole. Vier ingevulde teksten bewijzen nog niet dat de beschrijving waar is: iemand moet bijvoorbeeld controleren of de meetlat werkelijk centimeters aangaf. “Jonge planten” vertelt bovendien voor wie je wilt voorspellen, niet automatisch welke planten toevallig gemeten zijn.
meta["eenheid"] vraagt een bestaande sleutel op. .get(...) is handiger bij controle: ontbreekt de sleutel, dan krijg je None in plaats van een fout. Je kent None al als “geen uitkomst”.
De functie type(waarde) vertelt de soort waarde: str is tekst, int een geheel getal en float een kommagetal. True en False zijn booleans, de Pythonwaarden voor ja/nee. Voor één veld controleer je:
waarde = meta.get("eenheid")
print(type(waarde) == str) # True
print(waarde.strip() == "") # False
De vergelijking != betekent “niet gelijk aan”, als tegenhanger van ==.
.strip() verwijdert witruimte aan de randen: " ".strip() wordt de lege tekst "". Die methode werkt op tekst; controleer daarom eerst het type. Maak metadata_ok(meta) voor alle vier vereiste sleutels: geef bij het eerste ontbrekende, niet-tekstuele of lege veld False, en pas na alle controles True.
2. Een ingevuld vak kan toch ongeldig zijn
Onze hoogte moet een gewoon Pythongetal tussen 0 en 300 centimeter zijn, inclusief beide grenzen. 25.5 en 0 mogen dus door; 301, de tekst "25" en een ontbrekende waarde niet. Dit is een oefenafspraak, geen universele grens voor planten.
h noemt de gemeten hoogte; ≤ betekent “kleiner dan of gelijk aan”. Ook een numerieke waarde kan onbruikbaar zijn: float("nan") maakt NaN, “not a number”, en float("inf") positieve oneindigheid. Geen van beide is een eindige hoogte.
import math
print(math.isfinite(25.5)) # True
print(math.isfinite(float("nan")))# False
print(math.isfinite(float("inf")))# False
print(type(True) == int) # False
import math maakt de wiskundebibliotheek beschikbaar; math.isfinite controleert of een getal eindig is. Veelgemaakte fout: True accepteren omdat Python ermee kan rekenen alsof het 1 is. Met type(v) controleer je het precieze type; True heeft type bool en hoort hier niet bij int of float.
type(v) not in [int, float] vraagt of het type buiten de twee toegestane mogelijkheden valt. not in betekent “komt niet voor in”; zonder not vraag je juist of iets wel in een lijst voorkomt.
Maak geldige_hoogte(v) in deze volgorde: controleer type, dan de onder- en bovengrens, daarna eindigheid. Zo wijs je ook een enorm geheel getal meteen af zonder het eerst via math naar een float om te zetten. Geef een boolean terug; ongeldige waarden zijn een verwacht resultaat, geen reden om de verwerking te laten crashen.
3. Houd de vraag los van het antwoord
Dit zijn twee geldige, fictieve rijen. Hoogte en licht zijn bekend op dag 7; bloei wordt pas de volgende week vastgesteld. Label 1 betekent bloeit, label 0 betekent niet.
| Plant | Hoogte | Licht | Bloeit |
|---|---|---|---|
| Eerste | 12 | 5 | 1 |
| Tweede | 8 | 2 | 0 |
De kenmerken (features) zijn de invoer; de doelvariabele (target) is het te voorspellen antwoord. Kies bewust kenmerkenvolgorde ["licht", "hoogte"]. Dan wordt de invoermatrix X [[5,12],[2,8]] en de antwoordenlijst y [1,0].
rows = [
{"hoogte":12, "licht":5, "bloeit":1},
{"hoogte":8, "licht":2, "bloeit":0}
]
features = ["licht", "hoogte"]
target = "bloeit"
X = []
y = []
for i in range(len(rows)):
rij = []
for j in range(len(features)):
rij.append(rows[i][features[j]])
X.append(rij)
y.append(rows[i][target])
print(X) # [[5, 12], [2, 8]]
print(y) # [1, 0]
rows[i] kiest één dictionary; de tweede haakjes kiezen daarin een kolomnaam. De buitenste lus loopt langs planten, de binnenste volgt de afgesproken kenmerkenvolgorde. De bekende matrixlijsten krijgen zo hun betekenis.
Controleer vóór die lussen if target in features:; in vraagt of de doelnaam in de kenmerkenlijst voorkomt. Stop in dat geval met raise ValueError("Doel staat in kenmerken"): raise meldt hier een ongeldige opdracht en beëindigt de functie. Anders zou het model het antwoord als invoer krijgen.
Maak deel_xy(rows, features, target) en geef return (X, y): een tuple, een paar resultaten in vaste volgorde. De oefenrijen bevatten alle gevraagde kolomnamen. Je controleert hier de scheiding, niet opnieuw de meetkwaliteit.
Wat neem je mee?
- Metadata maken betekenis, herkomst en meetmoment expliciet; aanwezigheid bewijst nog geen waarheid.
- Controleer eerst type en bereik, daarna eindigheid; nul kan een geldige meting zijn.
- Houd kenmerkenvolgorde vast en sluit de doelvariabele uit van de invoer.
Een afgekeurde meting vraagt onderzoek. In de volgende les zie je hoe je ontbrekende waarden zichtbaar houdt en een invulkeuze controleerbaar maakt.