← Back to CoursesStemExpert
StemExpert
Intermediate · Handboek · 28. Functies, lijsten en data in Python
StemExpert · Brecht Corbeel · schoolium.me
StemExpert
I28. Functies, lijsten en data in Python
IIntermediate · deel I7 · Programmeren

Handboek · hoofdstuk 28Functies, lijsten en data in Python

Eigen functies, lijsten en woordenboeken, en een week meetdata van de klimaatmodule tot een besluit

Een datalogger schrijft elke paar minuten een regel met metingen. Na een week zijn dat er honderden: te veel om met de hand te bekijken, maar voor Python een paar regels code. In dit hoofdstuk leer je je eigen functies schrijven, veel waarden bewaren in lijsten en woordenboeken, een CSV-bestand inlezen en er gemiddelden, minima, maxima en grafieken uit halen. Aan het eind analyseer je een week in de klimaatmodule.

9× uitleg3× uitgewerkt voorbeeld1× proef1× ontleed1× naslag1× verhaal1× het geheel7× code16 opdrachten in het werkboek± 9 lestijden
Een week meten levert honderden getallen op; een programma maakt er een besluit van.
Een week meten levert honderden getallen op; een programma maakt er een besluit van.
Na dit hoofdstuk
Verhaal · 28.1

Vijf landen te weinig

1/24

In 2010 publiceerden twee bekende Amerikaanse economen een studie over staatsschulden. Hun besluit: landen met een schuld boven 90 % van hun jaarlijkse productie groeiden gemiddeld niet meer, zelfs licht achteruit. Politici overal ter wereld haalden het cijfer aan.

In 2013 kreeg Thomas Herndon, een student aan de universiteit van Massachusetts, als oefening de opdracht om een bekende studie opnieuw uit te rekenen. Hij koos deze, kwam niet op dezelfde getallen uit, en vroeg het rekenblad van de auteurs op. Daarin vond hij onder meer een formule voor een gemiddelde die over een te kort bereik liep: vijf landen telden niet mee, Australië, Oostenrijk, België, Canada en Denemarken. Samen met andere keuzes in de analyse (welke jaren meetelden, hoe landen gewogen werden) werd de gemiddelde groei bij een hoge schuld ongeveer +2,2 % per jaar in plaats van −0,1 %. De auteurs erkenden de rekenbladfout, maar verdedigden hun besluit.

Een rekenblad is niet slecht, maar een formule in cel F67 zie je niet staan. In een programma staat elke stap zichtbaar als tekst, test je met len() hoeveel waarden er echt meetellen, en kan iemand anders dezelfde berekening op dezelfde data opnieuw draaien. Dat heet reproduceerbaar werken, en het is de kern van goed onderzoek (hoofdstuk 2).

Waarom dit hoofdstuk ertoe doet

De klimaatmodule meet temperatuur, vochtigheid, verbruik en zonnestroom. Die metingen zijn alleen iets waard als je ze betrouwbaar samenvat: per dag, per dagdeel, met gemiddelden en grafieken. Dat doe je hier met functies, lijsten en woordenboeken, en straks met de echte datalogger van hoofdstuk 30.

Een berekening opnieuw uitvoeren is de beste controle: zo werd de fout in het rekenblad gevonden.
Een berekening opnieuw uitvoeren is de beste controle: zo werd de fout in het rekenblad gevonden.
Uitleg · 28.2

Wat je al kan, en waar dit hoofdstuk naartoe gaat

2/24
wat je al kanwat er nu bijkomt
variabelen, if, for en while; ingebouwde functies als print(), round() en len() (hoofdstuk 27)zelf functies schrijven, met parameters en een returnwaarde
een rij waarden tussen haakjes in een for-lus (hoofdstuk 27)lijsten die je aanvult, doorzoekt, sorteert en in stukken knipt; woordenboeken
gemiddelde, bereik en uitschieters (hoofdstuk 1); een rekenblad en een trendlijn (hoofdstuk 3)dezelfde statistiek in code, voor honderden metingen in één keer
een grafiek met assen, grootheid, eenheid en schaal (F4)grafieken laten tekenen met matplotlib

In hoofdstuk 27 schreef je programma's van boven naar onder, met een variabele voor elke waarde. Voor een week meetdata werkt dat niet meer: 56 rijen met elk vijf waarden zijn 280 getallen. Drie nieuwe bouwstenen lossen dat op. Een functie geeft een berekening een naam, zodat je ze telkens opnieuw kan gebruiken. Een lijst bewaart veel waarden onder één naam. Een woordenboek koppelt een naam (een sleutel) aan een waarde. Samen met de csv-module om bestanden te lezen en matplotlib om te tekenen, heb je alles om van meetdata een besluit te maken.

bestandde meetweekals CSVinlezenmet decsv-modulebewarenlijsten enwoordenboekenrekenenfuncties:gemiddelde,min, maxtonentabel engrafiek(matplotlib)elke stap is een paar regels code, die je voor de volgende week gewoon opnieuw draait
Vijf stappen van bestand tot besluit; dit hoofdstuk doet ze alle vijf.
Uitleg · 28.3

Functies: een berekening met een naam

3/24

Je gebruikte al veel functies: print(), round(), len(). Een eigen functie maak je met def:

def energie(vermogen, uren):
    """Energie in Wh van een toestel met dit vermogen (W) dat zoveel uren aanstaat."""
    return vermogen * uren

De eerste regel is de kop: het woord def, de naam, de parameters tussen haakjes en een dubbelepunt. Daaronder het ingesprongen lichaam. De tekst tussen drie aanhalingstekens is de docstring: wat de functie doet, met eenheden. Met return geeft de functie een waarde terug en stopt ze.

Een def voert nog niets uit; het maakt de functie alleen klaar. Pas bij een aanroep zoals energie(190, 16) draait het lichaam: de argumenten 190 en 16 worden de waarden van de parameters vermogen en uren, en de aanroep zelf krijgt de waarde 3040.

Wanneer maak je een functie?

Als je dezelfde berekening twee keer typt, of als een stuk code een duidelijke eigen taak heeft (een ADC-waarde omrekenen, een gemiddelde nemen). Een goede functie doet één ding, heeft een naam die zegt wat (een werkwoord of wat ze teruggeeft), en een docstring. Test ze met invoer waarvan je de uitkomst kent.

argumenten19016def energie(vermogen, uren):"""Energie in Wh ..."""return vermogen * urenreturn3040lampen = energie(190, 16) → lampen wijst naar 3040vermogenuren
De argumenten gaan erin, return geeft het resultaat terug aan wie de functie aanriep.
Code · 28.4

Je eerste eigen functies

4/24

Twee functies voor het energiebudget: energie() rekent, komma() maakt van een getal een tekst met een decimale komma (de truc uit hoofdstuk 27, nu met een naam). {x:.{decimalen}f} is een f-string in een f-string: het aantal decimalen komt zelf uit een variabele.

Pythonfuncties.py17 regelsDownload
# functies.py - eigen functies met parameters en een returnwaardedef energie(vermogen, uren):    """Energie in Wh van een toestel met dit vermogen (W) dat zoveel uren aanstaat."""    return vermogen * uren  def komma(x, decimalen=1):    """Getal als tekst met een decimale komma: komma(3.14159, 2) geeft '3,14'."""    return f"{x:.{decimalen}f}".replace(".", ",")  lampen = energie(190, 16)pomp = energie(12, 4)print(lampen, pomp)print("samen:", komma((lampen + pomp) / 1000, 2), "kWh")print("pomp per uur:", komma(energie(12, 10 / 60)), "Wh")print(energie.__doc__)

Uitvoer:

3040 48
samen: 3,09 kWh
pomp per uur: 2,0 Wh
Energie in Wh van een toestel met dit vermogen (W) dat zoveel uren aanstaat.

Een functie mag een andere functie aanroepen, en een aanroep mag in een berekening staan: komma(energie(12, 10 / 60)) rekent eerst de energie van 10 minuten pompen uit en maakt er dan een tekst van. De docstring is op te vragen met energie.__doc__, of netter met help(energie) in de shell.

Uitleg · 28.5

return is geen print

5/24

Het verschil tussen print en return is de klassieke valkuil bij functies. print toont iets op het scherm; return geeft een waarde terug aan de code die de functie aanriep. Een functie zonder return geeft de speciale waarde None ('niets') terug.

def gemiddelde_print(lijst):
    print(sum(lijst) / len(lijst))


def gemiddelde(lijst):
    return sum(lijst) / len(lijst)


a = gemiddelde_print([20, 22, 24])
b = gemiddelde([20, 22, 24])
print("a:", a, "- b:", b)
22.0
a: None - b: 22.0

De eerste functie toont 22.0, maar a is None: met a kan je niet verder rekenen. Rekenfuncties geven dus terug; alleen het hoofdprogramma toont.

Standaardwaarden. Een parameter mag een standaardwaarde krijgen, zoals decimalen=1. Laat je dat argument weg, dan geldt de standaardwaarde. Argumenten mag je ook met hun naam meegeven, in elke volgorde:

def komma(x, decimalen=1):
    return f"{x:.{decimalen}f}".replace(".", ",")


print(komma(3269.5))
print(komma(3269.5, 3))
print(komma(decimalen=0, x=3269.5))
3269,5
3269,500
3270

Lokale variabelen. Een variabele die je in een functie maakt, bestaat alleen tijdens die aanroep. Zo kunnen twee functies elk hun eigen totaal hebben zonder elkaar te storen. Wat een functie nodig heeft, geef je mee als argument; wat ze oplevert, geeft ze terug.

"Mijn functie werkt, ik zie het resultaat toch?"

Dat je een getal op het scherm ziet, betekent niet dat de functie het teruggeeft. Probeer altijd x = mijn_functie(...) en print(x). Staat er None, dan ontbreekt return.

met print()met returngemiddelde_print(...)rekent 22.0 uit en toont het ophet schermNonede aanroeper krijgt niets teruggemiddelde(...)rekent 22.0 uit en geeft hetterug22.0b = 22.0: je kan ermee verder rekenen
Links komt het resultaat alleen op het scherm, rechts komt het terug in een variabele.
Uitgewerkt voorbeeld · 28.6

Een functie voor de vochtsensor

6/24

De capacitieve vochtsensor van de meetkas geeft een ruwe ADC-waarde. Bij de ijking (F25) gaf ze 600 in droge potgrond en 300 in natte, en daartussen is het verband lineair: %. Schrijf een functie die een ruwe waarde omzet in een vochtpercentage, ook voor waarden buiten de ijking.

Gegeven
  • ijkpunten: ruw 600 → 0 %, ruw 300 → 100 %
  • verband: vocht = (600 − ruw) / 3
Gevraagd
  • een functie vocht_procent(ruw) met docstring
  • een test met gekozen waarden
Oplossing
  1. 1
    Kop en docstring. Eén parameter (de ruwe waarde), één returnwaarde (het vocht in %). De docstring vermeldt de ijking: wie de sensor later opnieuw ijkt, weet waar de getallen vandaan komen.
  2. 2
    Buiten de ijking. Een ruwe waarde van 640 (grond droger dan bij de ijking, of een los draadje) geeft −13 %, en 250 geeft 117 %. Die bestaan niet. Begrenzen doe je met max(0.0, ...) (niet onder 0) en min(100.0, ...) (niet boven 100).
  3. 3
    Testen. Kies waarden waarvan je de uitkomst kent: de twee ijkpunten, de drempel van 30 % (ruw 510), een waarde in het midden, en twee buiten de ijking.
    # vochtsensor.py - van ruwe ADC-waarde naar bodemvocht in procent
    def vocht_procent(ruw):
        """Bodemvocht (%) uit de ruwe ADC-waarde van de capacitieve sensor.
    
        IJking (F25): 600 in droge potgrond, 300 in natte. Begrensd tot 0-100 %.
        """
        vocht = (600 - ruw) / 3
        return min(100.0, max(0.0, vocht))
    
    
    for ruw in (600, 510, 405, 300, 250, 640):
        print(ruw, "->", round(vocht_procent(ruw), 1), "%")
    600 -> 0.0 %
    510 -> 30.0 %
    405 -> 65.0 %
    300 -> 100.0 %
    250 -> 100.0 %
    640 -> 0.0 %
Antwoord
De functie geeft (600 − ruw) / 3, begrensd tot 0-100 %. De drempel van 30 % hoort bij ruw 510, zoals in F25.
Klopt dit? De uitkomst voor de ijkpunten klopt (0 en 100 %), 405 ligt precies tussen 510 en 300 en geeft 65 %. Eén kanttekening: door het begrenzen zie je een losse sensor niet meer. In een echte regeling test je daarom ook apart of de ruwe waarde plausibel is (bijvoorbeeld tussen 250 en 650).
Uitleg · 28.7

Lijsten: veel waarden onder één naam

7/24

Een lijst zet je tussen vierkante haken, met komma's tussen de elementen. Elk element heeft een index: zijn plaats, vanaf 0. Met een negatieve index tel je van achteren: [-1] is het laatste element. Hier de hoogste temperatuur van elke dag van de meetweek:

>>> maxima = [28.1, 25.1, 21.1, 22.8, 27.4, 28.6, 25.1]
>>> maxima[0]
28.1
>>> maxima[5]
28.6
>>> maxima[-1]
25.1
>>> len(maxima)
7
>>> 28.6 in maxima
True
>>> maxima.index(28.6)
5

Een lijst kan groeien en veranderen. Je begint vaak met een lege lijst en voegt met append telkens een meting toe; een element vervang je via zijn index:

>>> metingen = []
>>> metingen.append(19.4)
>>> metingen.append(21.0)
>>> metingen
[19.4, 21.0]
>>> metingen[0] = 19.6
>>> metingen
[19.6, 21.0]
>>> len(metingen)
2

Een lijst mag waarden van elk type bevatten, ook teksten: dagen = ["ma", "di", ...]. Twee lijsten met evenveel elementen (dagen en maxima) horen vaak samen: het element met index 5 in de ene lijst hoort bij index 5 in de andere.

Index 0 is het eerste element

Een lijst met 7 elementen heeft de indexen 0 tot en met 6. maxima[7] bestaat niet en geeft een IndexError. Dit is dezelfde afspraak als bij range(7).

28.10ma-725.11di-621.12wo-522.83do-427.44vr-328.65za-225.16zo-1indexmaximadagnegatief
Boven de index vanaf 0, onder de negatieve index vanaf het einde.
Uitleg · 28.8

Slicing: een stuk uit een lijst of een tekst

8/24

Met lijst[a:b] neem je een stuk (een slice): van index a tot, maar niet met, index b. Laat je a weg, dan begin je vooraan; laat je b weg, dan loop je tot het einde. Een derde getal is de stap, zoals bij range(). Het resultaat is een nieuwe lijst; de oude blijft ongewijzigd.

>>> maxima = [28.1, 25.1, 21.1, 22.8, 27.4, 28.6, 25.1]
>>> maxima[1:4]
[25.1, 21.1, 22.8]
>>> maxima[:3]
[28.1, 25.1, 21.1]
>>> maxima[5:]
[28.6, 25.1]
>>> maxima[-2:]
[28.6, 25.1]
>>> maxima[::2]
[28.1, 21.1, 27.4, 25.1]

Een tekst (str) is ook een reeks, van tekens. Daarom werkt slicing ook op tekst, en dat is handig bij datalogbestanden, waar de datum en het uur samen in één tekst staan:

>>> tijdstip = "2026-04-20 06:00"
>>> tijdstip[:10]
'2026-04-20'
>>> tijdstip[11:13]
'06'
>>> int(tijdstip[11:13])
6
>>> len(tijdstip)
16

Met tijdstip[:10] groepeer je straks de metingen per dag, met int(tijdstip[11:13]) per uur.

28.10ma25.11di21.12wo22.83do27.44vr28.65za25.16zoindexmaximadagmaxima[1:4]: index 1, 2 en 34 is de stop: doet niet mee
maxima[1:4] neemt de elementen met index 1, 2 en 3; de stopindex 4 hoort er net niet meer bij.
Code · 28.9

Rekenen met lijsten: len, sum, min, max en sorted

9/24

Voor lijsten met getallen heeft Python ingebouwde functies die de statistiek van hoofdstuk 1 in één regel doen. Het gemiddelde is sum(l) / len(l). sorted() geeft een nieuwe, gesorteerde lijst (met reverse=True van groot naar klein); l.index(x) geeft de plaats van x, en met die index zoek je in een tweede lijst op welke dag het was.

Pythonweek.py11 regelsDownload
# week.py - rekenen met een lijst: de hoogste temperatuur van elke dagdagen = ["ma", "di", "wo", "do", "vr", "za", "zo"]maxima = [28.1, 25.1, 21.1, 22.8, 27.4, 28.6, 25.1]        # graden Celsius print("aantal dagen:", len(maxima))print("gemiddeld maximum:", round(sum(maxima) / len(maxima), 1))print("laagste:", min(maxima), "- hoogste:", max(maxima))i = maxima.index(max(maxima))print("warmste dag van de week:", dagen[i])print("gesorteerd:", sorted(maxima))print("drie warmste:", sorted(maxima, reverse=True)[:3])

Uitvoer:

aantal dagen: 7
gemiddeld maximum: 25.5
laagste: 21.1 - hoogste: 28.6
warmste dag van de week: za
gesorteerd: [21.1, 22.8, 25.1, 25.1, 27.4, 28.1, 28.6]
drie warmste: [28.6, 28.1, 27.4]
schrijfwijzebetekenisvoorbeeld met maximauitkomst
l[i]element met index i (vanaf 0)maxima[2]21.1
l[-1]laatste elementmaxima[-1]25.1
l[a:b]stuk van index a tot (niet met) bmaxima[1:4][25.1, 21.1, 22.8]
len(l)aantal elementenlen(maxima)7
sum(l), min(l), max(l)som, kleinste, grootstemax(maxima)28.6
sorted(l)nieuwe, gesorteerde lijstsorted(maxima)[:3][21.1, 22.8, 25.1]
l.index(x)index van de eerste xmaxima.index(25.1)1
l.count(x)hoe vaak x voorkomtmaxima.count(25.1)2
x in lzit x in de lijst?30.0 in maximaFalse
[f(x) for x in l if ...]nieuwe lijst uit een oude[t for t in maxima if t > 26][28.1, 27.4, 28.6]
l.append(x)x achteraan toevoegenverandert de lijst zelf
l.copy()een echte kopiezonder copy() krijg je een tweede naam voor dezelfde lijst
sorted() of .sort()

sorted(maxima) maakt een nieuwe lijst en laat maxima ongewijzigd. maxima.sort() sorteert de lijst zelf en geeft None terug. Schrijf dus nooit maxima = maxima.sort(): dan is je lijst weg.

Uitleg · 28.10

Lussen over lijsten: ophopen, tellen, filteren

10/24

Een for-lus loopt niet alleen over range(), maar over elke lijst: de lusvariabele krijgt elke ronde het volgende element. Vier patronen komen steeds terug. Ophopen: een totaal vóór de lus, in de lus erbij tellen. Tellen of filteren: in de lus een if. Twee lijsten tegelijk: met zip(), en met enumerate() krijg je er een volgnummer bij. Een nieuwe lijst maken uit een oude: met append in een lus, of korter met een lijstomschrijving (list comprehension) tussen vierkante haken.

# patronen.py - vier manieren om over een lijst te lopen
dagen = ["ma", "di", "wo", "do", "vr", "za", "zo"]
maxima = [28.1, 25.1, 21.1, 22.8, 27.4, 28.6, 25.1]

totaal = 0                               # 1. ophopen
for t in maxima:
    totaal = totaal + t
print("som:", round(totaal, 1))

aantal = 0                               # 2. tellen met een voorwaarde
for t in maxima:
    if t > 26:
        aantal = aantal + 1
print("dagen boven 26 graden:", aantal)

for dag, t in zip(dagen, maxima):        # 3. twee lijsten tegelijk
    if t < 23:
        print("koele dag:", dag, t)

for nummer, dag in enumerate(dagen[:3], start=1):
    print(nummer, dag)

kelvin = [round(t + 273.15, 2) for t in maxima]     # 4. een nieuwe lijst
print(kelvin)
som: 178.2
dagen boven 26 graden: 3
koele dag: wo 21.1
koele dag: do 22.8
1 ma
2 di
3 wo
[301.25, 298.25, 294.25, 295.95, 300.55, 301.75, 298.25]

De lijstomschrijving [round(t + 273.15, 2) for t in maxima] leest als 'voor elke t in maxima: t in kelvin'. Met een if erachter filter je: [t for t in maxima if t > 26] geeft alleen de warme dagen.

Kies de eenvoudigste vorm

Een gewone lus met append is altijd goed. Een lijstomschrijving is korter, maar schrijf ze alleen als ze op één regel leesbaar blijft.

"ma"28.1"di"25.1"wo"21.1"do"22.8"vr"27.4"za"28.6"zo"25.1dagenmaximazip(dagen, maxima) geeft paren: ("ma", 28.1), ("di", 25.1), ...
zip koppelt het eerste element van de ene lijst aan het eerste van de andere, enzovoort.
Uitgewerkt voorbeeld · 28.11

Gemiddelde en maximum zelf programmeren

11/24

sum(), len() en max() doen het werk voor je, maar hoe werken ze? Schrijf zelf een functie voor het gemiddelde en een voor de grootste waarde, test ze op de zonnestroom per dag van de meetweek, en vergelijk met de ingebouwde functies.

beste = eerste waardenog een waardew?neegeef beste terugjaw > beste?jabeste = wnee
Het algoritme van max(): een voorlopige beste, die bij elke grotere waarde vervangen wordt.
Gegeven
  • zonnestroom per dag (Wh), maandag tot zondag: 7465, 5105, 1547, 3077, 6949, 7717, 4676
Gevraagd
  • functies gemiddelde(waarden) en grootste(waarden)
  • een trace van grootste()
Oplossing
  1. 1
    Gemiddelde. Het patroon ophopen: totaal = 0, in de lus elke waarde erbij, na de lus delen door het aantal. Een lege lijst zou een deling door nul geven; de docstring zegt dat ze niet leeg mag zijn.
  2. 2
    Grootste. Hetzelfde idee als bij het sorteren van kaarten in F22, waar je telkens de kleinste zocht: neem de eerste waarde als voorlopig beste en vergelijk elke volgende waarde ermee; is ze groter, dan wordt zij de nieuwe beste. waarden[1:] slaat de eerste over, want die is al de beste.
  3. 3
    Het programma:
    # statistiek.py - gemiddelde en maximum zelf uitrekenen
    def gemiddelde(waarden):
        """Rekenkundig gemiddelde van een lijst getallen (de lijst mag niet leeg zijn)."""
        totaal = 0
        for w in waarden:
            totaal = totaal + w
        return totaal / len(waarden)
    
    
    def grootste(waarden):
        """Grootste waarde van een lijst, zonder max()."""
        beste = waarden[0]
        for w in waarden[1:]:
            if w > beste:
                beste = w
        return beste
    
    
    zon = [7465, 5105, 1547, 3077, 6949, 7717, 4676]        # Wh per dag, maandag tot zondag
    print("gemiddeld:", round(gemiddelde(zon)), "Wh - met sum/len:", round(sum(zon) / len(zon)))
    print("grootste:", grootste(zon), "Wh - met max():", max(zon))
    gemiddeld: 5219 Wh - met sum/len: 5219
    grootste: 7717 Wh - met max(): 7717
  4. 4
    Trace van grootste(). Beste begint op 7465.
    ww > beste?beste daarna
    5105nee7465
    1547nee7465
    3077nee7465
    6949nee7465
    7717ja7717
    4676nee7717
Antwoord
Beide functies geven dezelfde uitkomst als de ingebouwde: gemiddeld 5219 Wh per dag, hoogstens 7717 Wh (zaterdag).
Klopt dit? Test ook de randgevallen: een lijst met één element (dan is dat het maximum), een lijst waarin het maximum vooraan of helemaal achteraan staat, en negatieve getallen. Wie beste = 0 als begin kiest in plaats van de eerste waarde, krijgt bij een lijst met alleen negatieve temperaturen 0 als maximum: een klassieke fout.
Uitleg · 28.12

Woordenboeken: opzoeken met een sleutel

12/24

In een lijst zoek je een waarde op met een getal (de index). In een woordenboek (dictionary, type dict) zoek je op met een sleutel, meestal een tekst. Je schrijft het tussen accolades, met paren sleutel: waarde. Een voorbeeld is de pintabel van de Arduino Mega van de klimaatmodule:

>>> pinnen = {"A0": "NTC reservoir", "A1": "LDR", "A2": "pH-module", "A3": "batterijdeler"}
>>> pinnen["A2"]
'pH-module'
>>> pinnen["D9"] = "pomp (MOSFET)"
>>> len(pinnen)
5
>>> "A5" in pinnen
False
>>> pinnen.get("A5", "vrij")
'vrij'

Over alle paren loop je met items():

for pin, onderdeel in pinnen.items():
    print(pin, "->", onderdeel)
A0 -> NTC reservoir
A1 -> LDR
A2 -> pH-module
A3 -> batterijdeler
D9 -> pomp (MOSFET)
schrijfwijzebetekenis
d = {"A0": "NTC", "A1": "LDR"}woordenboek met sleutels en waarden
d["A0"]waarde opzoeken (KeyError als de sleutel ontbreekt)
d["D9"] = "pomp"toevoegen of vervangen
d.get("A5", "vrij")opzoeken met een standaardwaarde
"A5" in dbestaat de sleutel?
for k, w in d.items():over alle paren lopen
d.keys(), d.values(), len(d)sleutels, waarden, aantal paren

Een woordenboek is ook de handigste manier om metingen per groep te verzamelen: de sleutel is de datum (of het dagdeel), de waarde een lijst met de metingen van die groep. Dat doe je verderop met de meetweek.

Lijst of woordenboek?

Een lijst als de volgorde telt en de waarden van dezelfde soort zijn (de temperatuur elk uur). Een woordenboek als je iets opzoekt met een naam (welke sensor op A2 zit, hoeveel watt de pomp verbruikt).

sleutelwaarde"A0""NTC reservoir""A1""LDR""A2""pH-module""A3""batterijdeler""D9""pomp (MOSFET)"pinnen["A2"]geeft "pH-module"
Elke sleutel komt één keer voor en wijst naar één waarde.
Code · 28.13

Habitat: het energiebudget met een woordenboek en functies

13/24

De rekenhulp van hoofdstuk 27 rekende één maand tegelijk; voor een andere maand moest je twee regels aanpassen. Met een woordenboek voor de verbruikers, een lijst met de twaalf PVGIS-waarden en drie functies rekent hetzelfde programma het hele jaar door. In de for-lus over items() pakt naam, (vermogen, uren) elk paar meteen uit.

Pythonbudget_maanden.py42 regelsDownload
# budget_maanden.py - de rekenhulp van hoofdstuk 27 met een woordenboek, lijsten en functiesVERBRUIKERS = {                          # naam: (vermogen in W, uren per dag)    "groeilampen": (190, 16),    "pomp": (12, 4),    "sensoren": (3.0, 24),    "ventilatie dag": (5, 16),    "ventilatie nacht": (2, 8),    "CO2-klep": (6, 0.25),    "BMS": (0.5, 24),}MAANDEN = ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec']WH_PER_WP = [1.41, 2.11, 2.99, 3.69, 3.58, 3.54, 3.45, 3.31, 3.29, 2.5, 1.66, 1.25]PIEK = 4 * 350                           # Wp  def verbruik_per_dag(lichturen=16):    """Verbruik van de module in Wh per dag, als de groeilampen 'lichturen' uur branden."""    totaal = 0    for naam, (vermogen, uren) in VERBRUIKERS.items():        if naam == "groeilampen":            uren = lichturen        totaal = totaal + vermogen * uren    return totaal  def zon_per_dag(maand):    """Zonnestroom in Wh per dag in maand 1 tot 12 (PVGIS, Brussel, 60 graden zuid)."""    return PIEK * WH_PER_WP[maand - 1]  def max_lichturen(maand):    """Hoeveel lichturen de zonnestroom alleen kan betalen (hoogstens 16)."""    for uren in range(16, -1, -1):        if verbruik_per_dag(uren) <= zon_per_dag(maand):            return uren    return 0  print("maand   zon  saldo  lichturen")for m in range(1, 13):    saldo = zon_per_dag(m) - verbruik_per_dag()    print(f"{MAANDEN[m - 1]:5} {zon_per_dag(m):5.0f} {saldo:+6.0f} {max_lichturen(m):6d}")
−2000−1500−1000−50005001000150020002500saldo zonnestroom min verbruik (Wh per dag)−1296−316916189617421686156013641336230−946−1520janfebmrtaprmeijunjulaugsepoktnovdec
Van november tot februari is er een tekort bij 16 lichturen, de rest van het jaar een overschot.

Uitvoer:

maand   zon  saldo  lichturen
jan    1974  -1296      9
feb    2954   -316     14
mrt    4186   +916     16
apr    5166  +1896     16
mei    5012  +1742     16
jun    4956  +1686     16
jul    4830  +1560     16
aug    4634  +1364     16
sep    4606  +1336     16
okt    3500   +230     16
nov    2324   -946     11
dec    1750  -1520      8

De kolom lichturen is precies wat de rekenhulp van hoofdstuk 27 één maand per keer gaf. Het verschil: een verbruiker toevoegen is nu één regel in het woordenboek, en elke functie is apart te testen. max_lichturen gebruikt return midden in een lus: zodra de voorwaarde klopt, stopt de functie.

Uitleg · 28.14

CSV: een tabel als tekst

14/24

Een CSV-bestand (comma-separated values) is een tabel als gewone tekst: één rij per regel, de kolommen gescheiden door komma's. De eerste regel is meestal de kop met de namen van de kolommen. Elk rekenblad kan CSV openen en bewaren, elke datalogger kan het schrijven, en je leest het met een teksteditor. Daarom is het het meest gebruikte formaat voor meetdata.

Het bestand van de klimaatmodule heeft vijf kolommen: het tijdstip (datum en uur, zoals '2026-04-20 06:00'), de temperatuur in °C en de vochtigheid in % (gemiddelden over de drie uur die op dat tijdstip beginnen), en het verbruik en de zonnestroom in Wh in diezelfde drie uur. De eenheid staat in de naam van de kolom: temperatuur_C, verbruik_Wh. Een lege cel betekent: geen meting.

Een CSV uit een Nederlandstalig rekenblad

In het Nederlands is de komma het decimaalteken. Een rekenblad met Belgische of Nederlandse instellingen bewaart 'CSV' daarom vaak met een puntkomma tussen de kolommen en een komma in de getallen: 2026-04-20 06:00;19,9;78;571;126, en soms schrijft het de datum anders (20/04/2026 6:00). Kijk zo'n bestand eerst na in een teksteditor. Lees het met csv.DictReader(bestand, delimiter=";") en zet elk getal om met float(tekst.replace(",", ".")). Bewaart het rekenblad als 'CSV UTF-8', dan staat er vaak een onzichtbaar teken vooraan het bestand; open het dan met encoding="utf-8-sig", anders heet je eerste kolom niet 'tijdstip'.

Noem je programma nooit csv.py

Heet je eigen bestand csv.py, dan laadt import csv jouw bestand in plaats van de module, en krijg je rare foutmeldingen.

als tekst (in een teksteditor)tijdstip,temperatuur_C,vocht_pct,verbruik_Wh,zon_Wh2026-04-20 00:00,18.6,84,22,02026-04-20 03:00,18.5,83,22,02026-04-20 06:00,19.9,78,571,1262026-04-20 09:00,23.6,71,172,1758als tabel (in een rekenblad)tijdstipTvochtverbr.zon04-20 00:0018.68422004-20 03:0018.58322004-20 06:0019.97857112604-20 09:0023.6711721758de kop wordt de sleutels van DictReader
Dezelfde vijf regels: links zoals ze in het bestand staan, rechts zoals een rekenblad ze toont.
Code · 28.15

Het bestand: een week in de klimaatmodule

15/24

Dit is het volledige bestand: van maandag 20 tot zondag 26 april 2026, om de drie uur een regel, 56 rijen onder de kop. Met de knop Download bewaar je het als klimaatmodule_week.csv, in dezelfde map als je programma's. Kijk er eerst zelf naar: welke dagen waren zonnig? En waar ontbreekt een meting?

tekstklimaatmodule_week.csv57 regelsDownload
tijdstip,temperatuur_C,vocht_pct,verbruik_Wh,zon_Wh2026-04-20 00:00,18.6,84,22,02026-04-20 03:00,18.5,83,22,02026-04-20 06:00,19.9,78,571,1262026-04-20 09:00,23.6,71,172,17582026-04-20 12:00,28.0,63,90,27992026-04-20 15:00,28.1,63,88,22912026-04-20 18:00,23.7,69,482,4912026-04-20 21:00,20.1,79,216,02026-04-21 00:00,18.5,84,22,02026-04-21 03:00,17.8,84,22,02026-04-21 06:00,19.4,79,578,952026-04-21 09:00,22.3,77,283,13052026-04-21 12:00,25.1,66,169,17762026-04-21 15:00,25.1,66,225,15402026-04-21 18:00,22.1,75,506,3892026-04-21 21:00,19.7,81,216,02026-04-22 00:00,18.2,83,22,02026-04-22 03:00,17.7,82,22,02026-04-22 06:00,19.2,83,595,252026-04-22 09:00,19.7,78,525,3142026-04-22 12:00,,78,454,6112026-04-22 15:00,21.1,76,483,4842026-04-22 18:00,20.3,80,574,1132026-04-22 21:00,19.1,83,216,02026-04-23 00:00,18.5,83,22,02026-04-23 03:00,17.8,83,22,02026-04-23 06:00,19.6,81,587,582026-04-23 09:00,20.9,78,431,6982026-04-23 12:00,22.8,73,317,11722026-04-23 15:00,22.7,76,371,9422026-04-23 18:00,21.1,77,551,2072026-04-23 21:00,19.1,84,216,02026-04-24 00:00,18.9,80,22,02026-04-24 03:00,18.4,83,22,02026-04-24 06:00,19.9,81,571,1252026-04-24 09:00,23.4,71,189,16902026-04-24 12:00,27.4,65,90,26642026-04-24 15:00,27.1,64,113,20002026-04-24 18:00,23.4,73,487,4702026-04-24 21:00,19.5,83,216,02026-04-25 00:00,19.1,83,22,02026-04-25 03:00,18.5,84,22,02026-04-25 06:00,20.4,80,569,1322026-04-25 09:00,24.0,71,138,18962026-04-25 12:00,28.6,61,90,29982026-04-25 15:00,28.2,63,88,21862026-04-25 18:00,24.1,72,478,5052026-04-25 21:00,20.4,81,216,02026-04-26 00:00,18.7,82,22,02026-04-26 03:00,18.2,85,22,02026-04-26 06:00,19.6,81,582,812026-04-26 09:00,22.6,72,289,12802026-04-26 12:00,25.1,68,156,18292026-04-26 15:00,24.6,71,310,11942026-04-26 18:00,22.1,74,530,2922026-04-26 21:00,19.2,84,216,0

Wat je al ziet zonder te rekenen: 's nachts is het verbruik 22 Wh per blok (sensoren, pomp, ventilator, batterijbeheer) en de zonnestroom 0. Overdag bepalen de groeilampen het verbruik, en die dimmen als er daglicht is: op een zonnige middag zakt het verbruik tot 90 Wh per blok. Woensdag 22 april om 12 uur is de temperatuurcel leeg: de sensor gaf die keer geen geldige waarde. Een programma moet dat opvangen, en mag die cel zeker niet als 0 °C meetellen.

Code · 28.16

CSV inlezen met de csv-module

16/24

Met with open(...) as bestand: open je het bestand; na het ingesprongen blok sluit Python het vanzelf. csv.DictReader leest de kop en geeft daarna elke rij als een woordenboek, met de kolomnamen als sleutels: rij["zon_Wh"]. Alle waarden zijn tekst, zoals bij input(): zet ze om met int() of float(). continue springt meteen naar de volgende ronde van de lus, zodat een lege temperatuur niet omgezet wordt.

Pythoninlezen.py25 regelsDownload
# inlezen.py - de metingen van een week inlezen met de csv-moduleimport csv tijden = []temperaturen = []verbruik = []zon = []ontbrekend = 0 with open("klimaatmodule_week.csv", newline="", encoding="utf-8") as bestand:    for rij in csv.DictReader(bestand):        verbruik.append(int(rij["verbruik_Wh"]))        zon.append(int(rij["zon_Wh"]))        if rij["temperatuur_C"] == "":           # de sensor gaf geen waarde            ontbrekend = ontbrekend + 1            continue        tijden.append(rij["tijdstip"])        temperaturen.append(float(rij["temperatuur_C"])) print("rijen:", len(verbruik), "- met temperatuur:", len(temperaturen), "- ontbrekend:", ontbrekend)print("eerste meting:", tijden[0], temperaturen[0])print("gemiddelde temperatuur:", round(sum(temperaturen) / len(temperaturen), 1))print("laagste:", min(temperaturen), "- hoogste:", max(temperaturen))print("het warmst op", tijden[temperaturen.index(max(temperaturen))])print("in de hele week: verbruik", sum(verbruik), "Wh, zonnestroom", sum(zon), "Wh")

Uitvoer (met het bestand in dezelfde map):

rijen: 56 - met temperatuur: 55 - ontbrekend: 1
eerste meting: 2026-04-20 00:00 18.6
gemiddelde temperatuur: 21.4
laagste: 17.7 - hoogste: 28.6
het warmst op 2026-04-25 12:00
in de hele week: verbruik 14552 Wh, zonnestroom 36536 Wh

De lijsten tijden en temperaturen blijven even lang, omdat ze samen gevuld worden, ná de test op een lege cel. Zo hoort het tijdstip met index i altijd bij de temperatuur met index i, en vind je met temperaturen.index(max(temperaturen)) het moment van het maximum.

csv.reader: elke rij als lijst

Met csv.reader(bestand) krijg je elke rij als lijst van teksten, ['2026-04-20 00:00', '18.6', '84', '22', '0'], en is de kop gewoon de eerste rij. DictReader is leesbaarder (rij["zon_Wh"] in plaats van rij[4]) en blijft werken als er later een kolom bijkomt.

Uitgewerkt voorbeeld · 28.17

Wanneer was het te warm, en wat deed de zon met het verbruik?

17/24

Sla verdraagt warmte slecht; boven ongeveer 26 °C wil de beheerder van de klimaatmodule dat opvolgen. Zoek in de meetweek alle blokken boven 26 °C. Onderzoek ook of de module minder verbruikt als de zon schijnt, zoals je zou verwachten van lampen die dimmen.

04008001200160020002400280032000100200300400500600700zonnestroom in het blok van 3 uur (Wh)verbruik in dat blok (Wh)blokken van 6 tot 21 uur (lampen aan)
Elk punt is een blok van drie uur met de lampen aan: hoe meer zon, hoe lager het verbruik.
Gegeven
  • klimaatmodule_week.csv
  • lampen aan van 6 tot 22 uur: de blokken van 6, 9, 12, 15 en 18 uur hebben drie volle lichturen
Gevraagd
  • de warme blokken en hun aantal uren
  • het gemiddelde verbruik per blok bij veel en bij weinig zon
Oplossing
  1. 1
    Filteren. Een lege lijst warm, en in de lus een if met twee voorwaarden: de cel is niet leeg en de temperatuur is hoger dan 26. De volgorde telt: door and wordt float() niet meer uitgevoerd als de cel leeg is.
  2. 2
    Het uur uit de tekst. int(rij["tijdstip"][11:13]) geeft het uur. Alleen de blokken van 6 tot en met 18 uur tellen mee voor de vergelijking: 's nachts is de zon 0 én zijn de lampen uit, dat zou de vergelijking scheeftrekken.
  3. 3
    Twee groepen. Veel zon: meer dan 1500 Wh in drie uur (gemiddeld 500 W op 1400 Wp). Weinig zon: minder dan 500 Wh. De blokken daartussen laat je weg, zodat de groepen duidelijk verschillen.
    # te_warm.py - wanneer was het warmer dan 26 graden, en wat deed de zon met het verbruik?
    import csv
    
    warm = []
    veel_zon = []
    weinig_zon = []
    with open("klimaatmodule_week.csv", newline="", encoding="utf-8") as bestand:
        for rij in csv.DictReader(bestand):
            uur = int(rij["tijdstip"][11:13])
            if rij["temperatuur_C"] != "" and float(rij["temperatuur_C"]) > 26:
                warm.append(rij["tijdstip"])
            if 6 <= uur <= 18:                        # blokken waarin de lampen drie uur mogen branden
                if int(rij["zon_Wh"]) > 1500:
                    veel_zon.append(int(rij["verbruik_Wh"]))
                elif int(rij["zon_Wh"]) < 500:
                    weinig_zon.append(int(rij["verbruik_Wh"]))
    
    print("boven 26 graden:", len(warm), "blokken, of", 3 * len(warm), "uur:")
    for t in warm:
        print("  ", t)
    print(f"verbruik per blok bij veel zon: {sum(veel_zon) / len(veel_zon):.0f} Wh ({len(veel_zon)} blokken)")
    print(f"verbruik per blok bij weinig zon: {sum(weinig_zon) / len(weinig_zon):.0f} Wh ({len(weinig_zon)} blokken)")
    boven 26 graden: 6 blokken, of 18 uur:
       2026-04-20 12:00
       2026-04-20 15:00
       2026-04-24 12:00
       2026-04-24 15:00
       2026-04-25 12:00
       2026-04-25 15:00
    verbruik per blok bij veel zon: 134 Wh (12 blokken)
    verbruik per blok bij weinig zon: 546 Wh (15 blokken)
Antwoord
6 blokken, dus 18 uur boven 26 °C, telkens van 12 tot 18 uur op de zonnige dagen (maandag, vrijdag en zaterdag). Bij veel zon verbruikt de module per blok veel minder dan bij weinig zon: de lampen dimmen.
Klopt dit? Vergelijk met de grafiek: de punten met veel zonnestroom liggen onderaan (laag verbruik). Toch is een verband in data nog geen bewijs van een oorzaak (hoofdstuk 39). Hier kennen we de oorzaak wel: de regeling dimt de lampen op het daglicht (hoofdstuk 25). Let ook op de maat: een blok is een gemiddelde over drie uur; een korte piek van 30 °C binnen dat blok zie je in deze data niet.
Code · 28.18

De week per dag samenvatten

18/24

Een woordenboek verzamelt de rijen per dag: de sleutel is de datum (rij["tijdstip"][:10]), de waarde een lijst met de rijen van die dag. Bestaat de sleutel nog niet, dan maak je eerst een lege lijst. Een functie vat één dag samen en geeft vijf waarden tegelijk terug; bij de aanroep pak je ze uit in vijf variabelen.

Pythonper_dag.py24 regelsDownload
# per_dag.py - de week per dag samenvattenimport csv per_dag = {}                                  # datum -> lijst met de rijen van die dagwith open("klimaatmodule_week.csv", newline="", encoding="utf-8") as bestand:    for rij in csv.DictReader(bestand):        datum = rij["tijdstip"][:10]          # "2026-04-20 06:00" -> "2026-04-20"        if datum not in per_dag:            per_dag[datum] = []        per_dag[datum].append(rij)  def samenvatting(rijen):    """Gemiddelde, laagste en hoogste temperatuur, verbruik en zonnestroom van een dag."""    temp = [float(r["temperatuur_C"]) for r in rijen if r["temperatuur_C"] != ""]    verbruik = sum([int(r["verbruik_Wh"]) for r in rijen])    zon = sum([int(r["zon_Wh"]) for r in rijen])    return sum(temp) / len(temp), min(temp), max(temp), verbruik, zon  print("datum       T gem  T min  T max  verbruik    zon   saldo")for datum, rijen in per_dag.items():    gem, laag, hoog, verbruik, zon = samenvatting(rijen)    print(f"{datum} {gem:5.1f} {laag:6.1f} {hoog:6.1f} {verbruik:9d} {zon:6d} {zon - verbruik:+7d}")
012345678energie per dag (kWh)madiwodovrzazoverbruikzonnestroom
Zonnige dagen leveren tot vier keer meer dan de module verbruikt; op de regendag is er een tekort.

Uitvoer:

datum       T gem  T min  T max  verbruik    zon   saldo
2026-04-20  22.6   18.5   28.1      1663   7465   +5802
2026-04-21  21.2   17.8   25.1      2021   5105   +3084
2026-04-22  19.3   17.7   21.1      2891   1547   -1344
2026-04-23  20.3   17.8   22.8      2517   3077    +560
2026-04-24  22.2   18.4   27.4      1710   6949   +5239
2026-04-25  22.9   18.5   28.6      1623   7717   +6094
2026-04-26  21.3   18.2   25.1      2127   4676   +2549

Wat de tabel vertelt: op zonnige dagen (maandag, vrijdag, zaterdag) is het warmst, verbruikt de module het minst (de lampen dimmen) en blijft er het meest zonnestroom over. Alleen op de regendag (woensdag) is het saldo negatief: dan moet de batterij bijspringen. De temperatuur van woensdag is een gemiddelde over zeven blokken, omdat er één ontbreekt; ze telt dus iets minder zwaar mee dan de andere dagen.

Proef · 28.19

Van meting tot bestand: het klaslokaal een lesuur lang

19/24
Onderzoeksvraag

Hoe verandert de temperatuur in het klaslokaal tijdens een lesuur, en wat doet een open raam?

Materiaal
  • een thermometer met een resolutie van 0,1 °C (of een temperatuursensor op een microcontroller)
  • een horloge of de klok van de computer
  • een teksteditor (Kladblok, of de editor van Thonny)
  • Python met het programma klaslokaal.py
05101520253035404518,519,019,520,020,521,021,5tijd sinds 8:30 (min)temperatuur (°C)raam openklaslokaal
De temperatuur stijgt met de klas, en zakt snel als het raam opengaat.
Werkwijze
  1. 1Hang de thermometer op 1,5 m hoogte, uit de zon en weg van de verwarming.
  2. 2Noteer om de 5 minuten de temperatuur, een lesuur lang. Zet na 30 minuten een raam wijd open en noteer het tijdstip.
  3. 3Typ de metingen in een teksteditor als CSV: eerst de kop tijd,temperatuur_C, dan één regel per meting, met een decimale punt. Bewaar als klaslokaal.csv in UTF-8.
  4. 4Draai het programma: het leest de kolom met een eigen functie en toont gemiddelde, minimum, maximum en de verandering per 5 minuten.
  5. 5Maak met matplotlib een grafiek (pas grafiek_week.py aan) en zet een verticale lijn op het moment dat het raam openging (plt.axvline(30)).
Waarneming

Een voorbeeld van één klas (klaslokaal.csv):

tijd,temperatuur_C
08:30,19.2
08:35,19.5
08:40,19.8
08:45,20.2
08:50,20.5
08:55,20.9
09:00,21.1
09:05,19.8
09:10,19.4
09:15,19.6
# klaslokaal.py - de temperatuur in het klaslokaal, een lesuur lang
import csv


def lees_kolom(naam, kolom):
    """Alle niet-lege waarden van een kolom uit een CSV-bestand, als kommagetallen."""
    waarden = []
    with open(naam, newline="", encoding="utf-8") as bestand:
        for rij in csv.DictReader(bestand):
            if rij[kolom] != "":
                waarden.append(float(rij[kolom]))
    return waarden


temp = lees_kolom("klaslokaal.csv", "temperatuur_C")
print("metingen:", len(temp))
print(f"gemiddeld {sum(temp) / len(temp):.1f} graden, laagst {min(temp)}, hoogst {max(temp)}")
verschil = [round(temp[i + 1] - temp[i], 1) for i in range(len(temp) - 1)]
print("verandering per 5 minuten:", verschil)
print("grootste daling:", min(verschil))

Uitvoer:

metingen: 10
gemiddeld 20.0 graden, laagst 19.2, hoogst 21.1
verandering per 5 minuten: [0.3, 0.3, 0.4, 0.3, 0.4, 0.2, -1.3, -0.4, 0.2]
grootste daling: -1.3
Verklaring

Een klas van twintig leerlingen verwarmt het lokaal: elke persoon geeft ongeveer 100 W warmte af (hoofdstuk 14 rekende met ±116 W voor een bewoner). Dat is 2 kW, zoveel als een elektrische kachel. Met het raam open komt koude lucht binnen en zakt de temperatuur in vijf minuten meer dan een graad. Daarna stijgt ze weer licht: de muren en meubels hebben warmte opgeslagen en geven die af (hoofdstuk 18). De functie lees_kolom is bruikbaar voor elk CSV-bestand: voor de klimaatmodule roep je ze aan met een andere bestandsnaam en kolomnaam. Dat is de winst van een goede functie.

Uit de zon, weg van de verwarming, op 1,5 m hoogte: zo meet je de temperatuur van het lokaal.
Uit de zon, weg van de verwarming, op 1,5 m hoogte: zo meet je de temperatuur van het lokaal.
Code · 28.20

Grafieken met matplotlib

20/24

matplotlib is de bekendste Python-bibliotheek voor grafieken. Je installeert ze één keer (in een terminal: pip install matplotlib, in Thonny via Tools, Manage packages). De module pyplot importeer je met de korte naam plt. Het programma leest de temperaturen en de tijd in dagen, en tekent ze als lijn met punten. plt.savefig() bewaart de grafiek als afbeelding voor je verslag; plt.show() opent ze in een venster.

Pythongrafiek_week.py21 regelsDownload
# grafiek_week.py - de temperatuur van een week in een grafiekimport csvimport matplotlib.pyplot as plt dagen = []              # tijd in dagen sinds maandag 0 uurtemperaturen = []with open("klimaatmodule_week.csv", newline="", encoding="utf-8") as bestand:    for i, rij in enumerate(csv.DictReader(bestand)):        if rij["temperatuur_C"] != "":            dagen.append(i * 3 / 24)          # rij i begint i * 3 uur na maandag 0 uur            temperaturen.append(float(rij["temperatuur_C"])) plt.plot(dagen, temperaturen, marker="o", label="temperatuur (gemiddelde per 3 uur)")plt.axhline(26, linestyle="--", color="gray")plt.xlabel("tijd (dagen sinds maandag 20 april, 0 uur)")plt.ylabel("temperatuur (°C)")plt.title("Klimaatmodule, week van 20 april 2026")plt.grid(True)plt.legend()plt.savefig("temperatuur_week.png", dpi=150)plt.show()
012345671618202224262830tijd (dagen sinds maandag 20 april, 0 uur)temperatuur (°C)26 °Ctemperatuur (gemiddelde per 3 uur)
Dezelfde gegevens als in grafiek_week.py, hier getekend in de stijl van dit boek.

De code geeft dezelfde grafiek als hiernaast: dezelfde punten, dezelfde assen, de hulplijn op 26 °C (plt.axhline). matplotlib gebruikt haar eigen kleuren en schrijft getallen met een decimale punt. De grafieken in dit boek zijn met dezelfde gegevens getekend in de huisstijl, zodat ze ook op papier en in het donkere thema leesbaar zijn. Het ontbrekende punt van woensdagmiddag is gewoon overgeslagen: de lijn loopt van 9 naar 15 uur.

Een staafdiagram maak je met plt.bar. Twee reeksen naast elkaar schuif je elk 0,2 opzij, met een breedte van 0,4; plt.xticks zet de namen van de dagen onder de staven. De getallen komen uit per_dag.py (in kWh):

# energie_week.py - verbruik en zonnestroom per dag als staafdiagram
import matplotlib.pyplot as plt

dagen = ["ma", "di", "wo", "do", "vr", "za", "zo"]
verbruik = [1.66, 2.02, 2.89, 2.52, 1.71, 1.62, 2.13]        # kWh per dag, uit per_dag.py
zon = [7.46, 5.11, 1.55, 3.08, 6.95, 7.72, 4.68]
x = range(len(dagen))
plt.bar([i - 0.2 for i in x], verbruik, width=0.4, label="verbruik")
plt.bar([i + 0.2 for i in x], zon, width=0.4, label="zonnestroom")
plt.xticks(x, dagen)
plt.ylabel("energie per dag (kWh)")
plt.legend()
plt.show()

Die code tekent het staafdiagram van het blad 'De week per dag samenvatten'.

Een grafiek die zegt wat hij toont

Elke as krijgt een grootheid en een eenheid, elke reeks een label in de legende. Een titel is handig op het scherm; in een verslag komt de uitleg in het onderschrift.

Ontleed · 28.21

Een goede grafiek ontleed

21/24

Dezelfde grafiek, met bij elk onderdeel de regel matplotlib die het tekent. Een grafiek zonder één van deze onderdelen is moeilijker te lezen, of zegt zelfs iets anders dan de data (hoofdstuk 39).

012345671618202224262830tijd (dagen sinds maandag 20 april, 0 uur)temperatuur (°C)temperatuur (gemiddelde per 3 uur)
Elk onderdeel van een grafiek heeft een eigen regel code.
Wijs een nummer aan: de figuur zoomt in. Klik om vast te zetten.
  1. 1
    Datapunten en lijn

    plt.plot(dagen, temperaturen, marker="o"): punten op de gemeten waarden, verbonden door een lijn omdat de tijd doorloopt. Het hoogste punt: zaterdag 12 uur, 28,6 °C.

  2. 2
    Hulplijn

    plt.axhline(26, linestyle="--"): een grens die de lezer helpt, hier 26 °C.

  3. 3
    x-as met grootheid en eenheid

    plt.xlabel(...): wat staat er horizontaal, en vanaf wanneer telt de tijd?

  4. 4
    y-as met grootheid en eenheid

    plt.ylabel("temperatuur (°C)"). De as begint hier bij 16 °C, niet bij 0: dat mag, als je het ziet aan de getallen.

  5. 5
    Legende

    label="..." in plt.plot en daarna plt.legend(): nodig zodra er meer dan één reeks is.

  6. 6
    Raster

    plt.grid(True): lichte lijnen om waarden af te lezen. De stap (1 dag, 2 °C) kies je zo dat de getallen rond zijn.

Uitleg · 28.22

Foutmeldingen en valkuilen bij lijsten, woordenboeken en functies

22/24

Met lijsten, woordenboeken en functies komen er nieuwe foutmeldingen bij. Lees ze zoals in hoofdstuk 27: van onder naar boven. Nieuw is dat een traceback door een functie heen loopt: dan staan er twee plaatsen, de aanroep en de regel in de functie. De echte meldingen van Python 3.13:

IndexError. Drie elementen hebben de indexen 0, 1 en 2. De laatste is temperaturen[-1] of temperaturen[len(temperaturen) - 1].

temperaturen = [19.4, 21.0, 23.5]
print(temperaturen[3])
Traceback (most recent call last):
  File "fouten.py", line 2, in <module>
    print(temperaturen[3])
          ~~~~~~~~~~~~^^^
IndexError: list index out of range

KeyError. De sleutel bestaat niet. Test eerst met in, of gebruik pinnen.get("A2", "vrij"). Bij een CSV-bestand is het vaak een tikfout in de kolomnaam.

pinnen = {"A0": "NTC", "A1": "LDR"}
print(pinnen["A2"])
Traceback (most recent call last):
  File "fouten.py", line 2, in <module>
    print(pinnen["A2"])
          ~~~~~~^^^^^^
KeyError: 'A2'

AttributeError. Een lijst kent geen push (dat is JavaScript); in Python is het append.

metingen = [19.4, 21.0]
metingen.push(23.5)
Traceback (most recent call last):
  File "fouten.py", line 2, in <module>
    metingen.push(23.5)
    ^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'push'

TypeError. De functie heeft twee parameters en krijgt maar één argument. Geef ze allebei mee, of geef de parameter een standaardwaarde.

def energie(vermogen, uren):
    return vermogen * uren


print(energie(190))
Traceback (most recent call last):
  File "fouten.py", line 5, in <module>
    print(energie(190))
          ~~~~~~~^^^^^
TypeError: energie() missing 1 required positional argument: 'uren'

ZeroDivisionError. De traceback heeft nu twee niveaus: regel 5 riep de functie aan, en in de functie (regel 2) liep het mis. Een lege lijst heeft lengte 0. Zeg in de docstring dat de lijst niet leeg mag zijn, of test het: if len(lijst) == 0: return None.

def gemiddelde(lijst):
    return sum(lijst) / len(lijst)


print(gemiddelde([]))
Traceback (most recent call last):
  File "fouten.py", line 5, in <module>
    print(gemiddelde([]))
          ~~~~~~~~~~^^^^
  File "fouten.py", line 2, in gemiddelde
    return sum(lijst) / len(lijst)
           ~~~~~~~~~~~^~~~~~~~~~~~
ZeroDivisionError: division by zero
"b = a maakt een kopie van de lijst"

Een naam is een naamkaartje (hoofdstuk 27). Na b = a hangen twee kaartjes aan dezelfde lijst: wie via b iets toevoegt, verandert ook a. Wil je een echte kopie, schrijf dan b = a.copy().

>>> a = [19.4, 21.0]
>>> b = a
>>> b.append(23.5)
>>> a
[19.4, 21.0, 23.5]
>>> c = a.copy()
>>> c.append(99)
>>> a
[19.4, 21.0, 23.5]

Bij getallen en teksten merk je dit nooit, omdat je die niet kan veranderen: x = x + 1 maakt een nieuwe waarde.

b = ac = a.copy()ab[19.4, 21.0, 23.5]één lijst, twee namenac[19.4, 21.0, 23.5][19.4, 21.0, 23.5]twee lijsten
Links wijzen a en b naar dezelfde lijst; rechts heeft c een eigen kopie.
Naslag · 28.23

Functies, lijsten, woordenboeken, CSV en matplotlib

23/24

De schrijfwijzen uit dit hoofdstuk op één blad.

schrijfwijzebetekenis
def naam(a, b=2):functie met een verplichte parameter a en een parameter b met standaardwaarde 2
"""Uitleg."""docstring: eerste regel in de functie, zegt wat ze doet
return waardegeeft de waarde terug en stopt de functie; zonder return krijg je None
x = naam(5), naam(5, b=3)aanroepen, met argumenten op volgorde of met hun naam
return a, b en x, y = naam(...)twee waarden teruggeven en uitpakken
schrijfwijzebetekenisvoorbeeld met maximauitkomst
l[i]element met index i (vanaf 0)maxima[2]21.1
l[-1]laatste elementmaxima[-1]25.1
l[a:b]stuk van index a tot (niet met) bmaxima[1:4][25.1, 21.1, 22.8]
len(l)aantal elementenlen(maxima)7
sum(l), min(l), max(l)som, kleinste, grootstemax(maxima)28.6
sorted(l)nieuwe, gesorteerde lijstsorted(maxima)[:3][21.1, 22.8, 25.1]
l.index(x)index van de eerste xmaxima.index(25.1)1
l.count(x)hoe vaak x voorkomtmaxima.count(25.1)2
x in lzit x in de lijst?30.0 in maximaFalse
[f(x) for x in l if ...]nieuwe lijst uit een oude[t for t in maxima if t > 26][28.1, 27.4, 28.6]
l.append(x)x achteraan toevoegenverandert de lijst zelf
l.copy()een echte kopiezonder copy() krijg je een tweede naam voor dezelfde lijst
schrijfwijzebetekenis
d = {"A0": "NTC", "A1": "LDR"}woordenboek met sleutels en waarden
d["A0"]waarde opzoeken (KeyError als de sleutel ontbreekt)
d["D9"] = "pomp"toevoegen of vervangen
d.get("A5", "vrij")opzoeken met een standaardwaarde
"A5" in dbestaat de sleutel?
for k, w in d.items():over alle paren lopen
d.keys(), d.values(), len(d)sleutels, waarden, aantal paren
schrijfwijzebetekenis
import csvde csv-module laden
with open(naam, newline="", encoding="utf-8") as f:bestand openen; het sluit vanzelf na het blok
csv.DictReader(f)elke rij als woordenboek met de kop als sleutels
csv.reader(f)elke rij als lijst van teksten
delimiter=";", encoding="utf-8-sig"voor een CSV uit een Nederlandstalig rekenblad
import matplotlib.pyplot as pltmatplotlib laden (eenmalig installeren: pip install matplotlib)
plt.plot(x, y, marker="o", label="...")lijngrafiek met punten
plt.bar(x, hoogte, width=0.4), plt.scatter(x, y)staafdiagram, puntenwolk
plt.xlabel(), plt.ylabel(), plt.title()asopschriften (grootheid en eenheid) en titel
plt.legend(), plt.grid(True), plt.axhline(26)legende, raster, horizontale hulplijn
plt.savefig("naam.png", dpi=150), plt.show()bewaren als afbeelding, tonen in een venster
Het geheel · 28.24

Waar data in Python vandaan komt en waar ze heen gaat

24/24

Met functies, lijsten en bestanden kan je programma omgaan met echte hoeveelheden data. Dat verbindt programmeren met onderzoeken en meten (een meting is pas iets waard als je ze betrouwbaar samenvat) en met data en AI (een model leert uit precies zulke tabellen). Wiskundig oefen je gemiddelden, sommen en verbanden; technisch lees je het energiebudget en de klimaatregeling af uit echte cijfers.

Tabel en grafiek: grootheid, eenheid, schaal. matplotlib volgt dezelfde regels.

Gemiddelde, bereik en uitschieters; in code voor honderden metingen tegelijk.

Een verband in data zoeken; het rekenblad dat je hier door code vervangt.

Het energiebudget en de PVGIS-waarden achter budget_maanden.py.

De ruwe ADC-waarden die vocht_procent() omrekent, en het dimmen van de lampen.

Variabelen, keuzes, lussen en foutmeldingen: de basis onder dit hoofdstuk.

De datalogger die zulke CSV-bestanden schrijft, en ze in Python visualiseren.

De juiste grafiek kiezen, misleidende grafieken, en correlatie tegenover oorzaak.

Standaardafwijking, betrouwbaarheidsintervallen en toetsen op zulke data.

Functies en data samen in klassen, en functies testen met unit tests.

numpy rekent met hele lijsten tegelijk: sneller en korter dan een lus.

HabitatDe klimaatmodule heeft nu een analyseprogramma voor haar metingen: inlezen, per dag samenvatten, warme uren opsporen en grafieken maken. In de voorbeeldweek van april verbruikte ze 14,6 kWh en leverden de panelen 36,5 kWh; alleen de regendag gaf een tekort (1344 Wh). Het dimmen van de lampen scheelt op een zonnige dag ruim een kilowattuur. In hoofdstuk 30 komt de echte datalogger die dit bestand schrijft; opdracht 28.12 is de analyse voor je portfolio.
Samenvatting

In het kort

Begrippen

Wat je nu kent

functie
Een stuk code met een naam dat je kan aanroepen; het krijgt argumenten mee en geeft met return een waarde terug.
parameter
De naam in de functiedefinitie die bij een aanroep de waarde van een argument krijgt, zoals vermogen in energie(vermogen, uren).
returnwaarde
Wat een functie met return teruggeeft aan wie haar aanriep; zonder return is dat None.
docstring
De tekst tussen drie aanhalingstekens op de eerste regel van een functie, die zegt wat ze doet.
lijst
Een geordende reeks waarden onder één naam, tussen vierkante haken; het eerste element heeft index 0.
slicing
Een stuk uit een lijst of tekst nemen met l[a:b]: van index a tot, maar niet met, index b.
woordenboek (dictionary)
Een verzameling paren sleutel: waarde, waarin je een waarde opzoekt met haar sleutel.
CSV
Comma-separated values: een tabel als tekstbestand, één rij per regel, de kolommen gescheiden door een komma.
ontbrekende waarde
Een lege cel in een databestand: de meting ontbreekt en mag niet als nul meetellen.
matplotlib
Een Python-bibliotheek om grafieken te tekenen: lijnen, staven, puntenwolken.
Aan de slag in het werkboek16 opdrachten, van oefenen tot uitdagen