Ga naar inhoud

Gevorderd gebruik

Deze pagina bundelt de gevorderde onderwerpen die je niet nodig hebt voor een gewone prognose: de tool aanroepen vanuit Python (notebooks, cloud), hyperparameters afstemmen, en het model evalueren met scalaire metrieken. Voor de dagelijkse workflow — zie de Snelstart en Draaien & CLI.

Heb je dit nodig?

Wil je gewoon een prognose draaien via de command line? Dan kun je deze pagina overslaan. Ze is bedoeld voor gebruikers die de tool in een eigen (cloud)pipeline inbouwen, of die de modellen willen afstemmen en evalueren.

Gebruik als Python-package

Naast de CLI kun je studentprognose ook direct vanuit Python-scripts importeren. Dit is handig voor geautomatiseerde pipelines, notebooks, of cloudworkflows waarbij de data al in-memory beschikbaar is.

Uitvoerbaar voorbeeld — implementatienotebook

Voor een direct inpasbaar startpunt voor MS Fabric, Databricks of Azure: zie notebooks/implementatie.ipynb. Vereist alleen pip install studentprognose — geen repo-clone nodig.

Beschikbare bouwstenen

from studentprognose import (
    load_configuration,            # laad configuration.json (of package defaults)
    load_filtering,                # laad filtering JSON (of package defaults)
    load_data,                     # laad data vanaf schijf als DataFrames
    run_pipeline_cli,              # volledige CLI-pipeline (accepteert argv-lijst)
    run_pipeline_from_dataframes,  # pipeline met DataFrames in-memory
    build_dashboard_from_dataframes,  # interactieve HTML-dashboards uit DataFrames
    evaluate_predictions,          # output → scalaire evaluatiemetrieken per model
    pivot_metrics,                 # metrieken → model × jaar/week-matrix (backtest)
    to_mlflow_metrics,             # metrieken afvlakken voor mlflow.log_metrics (Fabric)
    PipelineConfig,                # configuratie-dataclass voor de pipeline
    DataOption,                    # enum: INDIVIDUAL / CUMULATIVE / BOTH_DATASETS
    StudentYearPrediction,         # enum: FIRST_YEARS / VOLUME
)

Het model evalueren

evaluate_predictions zet de pipeline-output om in scalaire metrieken (MAE, MAPE, WAPE, RMSE, bias, R²) per model; pivot_metrics vat een backtest over meerdere jaren samen tot een model × jaar-matrix; en to_mlflow_metrics levert ze klaar voor MLflow in MS Fabric / Databricks. Zie Het model evalueren.

Minimaal werkend voorbeeld (bestandsgebaseerd)

from studentprognose import run_pipeline_cli

# Zelfde als de CLI — start de volledige pipeline inclusief ETL
run_pipeline_cli(["studentprognose", "-d", "c", "-y", "2025", "-w", "10"])

# Of sla ETL over als de data al verwerkt is
run_pipeline_cli(["studentprognose", "--noetl", "-d", "c", "-y", "2025", "-w", "10"])

Cloud-gebruik (data al in-memory)

Gebruik run_pipeline_from_dataframes als je de data al geladen hebt, bijvoorbeeld vanuit Azure Blob Storage of Amazon S3. De ETL-stap wordt volledig overgeslagen.

import pandas as pd
from studentprognose import run_pipeline_from_dataframes, DataOption

# Laad data uit cloud-opslag (voorbeeld met Azure SDK)
# from azure.storage.blob import BlobServiceClient
# blob_data = blob_client.download_blob().readall()
# df_cum = pd.read_csv(io.BytesIO(blob_data), sep=";", skiprows=[1])

# Of lokaal voor testen
df_cum = pd.read_csv("vooraanmeldingen_cumulatief.csv", sep=";", skiprows=[1])

result = run_pipeline_from_dataframes(
    year=2025,
    week=10,
    data_cumulative=df_cum,
    dataset=DataOption.CUMULATIVE,
    save_output=False,  # geen lokale uitvoerbestanden aanmaken (ook geen tussenresultaat)
)

if result is not None:
    print(result[["Croho groepeernaam", "Weighted_ensemble_prediction"]].head())

De functie accepteert ook een eigen configuratiedict, zodat je geen bestandssysteem nodig hebt:

from studentprognose import run_pipeline_from_dataframes, DataOption
from studentprognose.config import load_defaults

# Begin met package-defaults en pas aan
config = load_defaults()
config["numerus_fixus"] = ["B Geneeskunde", "B Tandheelkunde"]

result = run_pipeline_from_dataframes(
    year=2025,
    week=10,
    data_cumulative=df_cum,
    configuration=config,
    dataset=DataOption.CUMULATIVE,
)

year/week moeten binnen je trainingsdata vallen

run_pipeline_from_dataframes controleert — net als de CLI — of year en week voorkomen in de meegegeven DataFrames. Valt een van beide buiten bereik, dan krijg je een heldere ValueError met de wél beschikbare range, in plaats van een stille None of een onverwachte kernel-crash in een notebook.

# Stel: je data loopt t/m 2025
run_pipeline_from_dataframes(
    year=2030,            # buiten bereik
    week=10,
    data_cumulative=df_cum,
    dataset=DataOption.CUMULATIVE,
)
# ValueError: year=2030 valt buiten de beschikbare trainingsdata.
#   Beschikbare data: jaren 2018-2025, weken 1-52.
#   Pas year/week aan binnen deze range, of voeg trainingsdata toe.

Pas year/week aan binnen de range, of voeg aanvullende trainingsdata toe.

Dashboards genereren vanuit DataFrames

De interactieve Plotly-dashboards (die de CLI met --dashboard maakt) kun je vanuit een notebook of cloud-run genereren met build_dashboard_from_dataframes. Dit is een aparte functie die je ná (of in plaats van) run_pipeline_from_dataframes aanroept, met dezelfde in-memory DataFrames. Ze draait de pipeline (zonder iets naar schijf te schrijven) en schrijft daarna de dashboards weg naar <cwd>/data/output/visualisations/.

from studentprognose import build_dashboard_from_dataframes, DataOption

output_dir = build_dashboard_from_dataframes(
    year=2025,
    week=10,
    data_cumulative=df_cum,
    data_student_numbers=sc,   # nodig voor de realisatie-/conversiegrafieken
    dataset=DataOption.CUMULATIVE,
    configuration=config,
)
print(f"Dashboards geschreven naar {output_dir}")
# -> <cwd>/data/output/visualisations/{individual,cumulative,final}/dashboard.html

Losse functie, geen extra parameter

Het dashboard is bewust een aparte functie en géén parameter op run_pipeline_from_dataframes: zo blijft de voorspelfunctie ongewijzigd en is de dashboard-stap los te draaien en te testen. Gebruik cwd om de uitvoermap te sturen. De year/week-rangecontrole is identiek aan die van run_pipeline_from_dataframes; komt geen enkele rij door de filters, dan krijg je een heldere ValueError in plaats van een leeg dashboard.

Hyperparameter tuning

Het cumulatieve spoor is twee-traps en je kunt beide trappen afstemmen op je eigen data:

Trap Wat Wat tuning kiest Vastgelegd in
Stap 1 — SARIMA extrapoleert de vooraanmeldcurve ARIMA-ordes (p,d,q)(P,D,Q,s) forecaster_params.sarima
Stap 2 — regressor vertaalt de curve naar inschrijvingen hyperparameters (default XGBoost) regressor_params.<naam>

Beide gebruiken een tijd-bewuste zoektocht (geen random k-fold — dat lekt toekomst) met dezelfde MAPE-metriek als de benchmark, en laten het operationele voorspelpad standaard onaangeroerd. Zie XGBoost → Hyperparameter tuning (regressor) en SARIMA → Orde-selectie voor de methodologie.

Via de CLI — zoeken en vastleggen

studentprognose tune -d c -w 12                    # default: regressor (stap 2)
studentprognose tune -d c -w 12 --tune-target sarima   # alleen SARIMA (stap 1)
studentprognose tune -d c -w 12 --tune-target both     # beide trappen

Dit draait de zoektocht op je geladen data, print per trap een overzicht van alle geteste sets met hun MAPE (de best presterende set gemarkeerd met ), en geeft een kant-en-klaar config-snippet terug. Plak dat snippet in configuration.json om de gevonden waarden vast te leggen (zie Configuratie → vastleggen). Vastgelegde waarden zijn reproduceerbaar: ze worden bij elke run gebruikt zonder opnieuw te tunen.

Via de Python-API — tunen en voorspellen in één keer

run_pipeline_from_dataframes heeft een tune-parameter (standaard False) waarmee je kiest welke trap(pen) getuned worden:

result = run_pipeline_from_dataframes(
    year=2025,
    week=10,
    data_cumulative=df_cum,
    data_student_numbers=data_studentcount,
    dataset=DataOption.CUMULATIVE,
    tune="both",  # tune stap 1 (SARIMA) én stap 2 (regressor), daarna voorspellen
)

De ondersteunde waarden:

tune= Wat wordt getuned
False (default) niets — gebruikt vastgelegde/default-waarden
True of "regressor" alleen de regressor (stap 2)
"sarima" alleen SARIMA-ordes (stap 1)
"both" beide trappen, elk apart gelogd
{"regressor": {...}, "sarima": {...}} beide/één trap met eigen zoekruimte (waarde weglaten of None = ingebouwde grid)

Elke gekozen trap draait de zoektocht éénmaal, voorspelt met de beste waarden en koppelt ze terug in de configuratie zodat je ze kunt vastleggen. Het overzicht (met op de winnaar) gaat naar de console; de functie retourneert het voorspellings-DataFrame, niet het tuning-resultaat.

Bij tune="both" zie je twee aparte tabellen — één per trap, elk met een eigen :

           MAPE   Folds  Parameters
  ─────────────────────────────────
  ✓      0.0912       6  {"order": [1, 1, 1], "seasonal_order": [1, 1, 0, 52]}
         0.1041       6  {"order": [1, 0, 1], "seasonal_order": [1, 1, 1, 52]}

Beste parameters voor 'sarima' (MAPE=0.0912, 6 kandidaten):
Plak dit in je configuration.json om de ordes vast te leggen:
{ "model_config": { "forecaster_params": { "sarima": { ... } } } }

           MAPE   Folds  Parameters
  ─────────────────────────────────
  ✓      0.1424      12  {"learning_rate": 0.25, "n_estimators": 200, "max_depth": 5}
         0.1487      12  {"learning_rate": 0.1, "n_estimators": 200, "max_depth": 3}

Beste parameters voor 'xgboost' (MAPE=0.1424, 12 kandidaten):
Plak dit in je configuration.json om de parameters vast te leggen:
{ "model_config": { "regressor_params": { "xgboost": { ... } } } }

Tuning is opt-in

Standaard (tune=False) gebruikt de pipeline de waarden uit model_config.regressor_params / forecaster_params of de modeldefaults — snel en reproduceerbaar. Zet tune alleen aan wanneer je bewust wilt afstemmen; het maakt de run langzamer en, zonder de uitkomst vast te leggen, niet-deterministisch.

Het model evalueren

De per-rij MAE_*/MAPE_*-kolommen in de output zijn handig om één rij te lezen, maar voor modelevaluatie wil je geaggregeerde, scalaire metrieken (één getal per model). Daarvoor levert het pakket evaluate_predictions:

from studentprognose import run_pipeline_from_dataframes, evaluate_predictions, DataOption

result = run_pipeline_from_dataframes(
    year=2023, week=12,
    data_cumulative=df_cum, data_student_numbers=df_sc,
    dataset=DataOption.CUMULATIVE, save_output=False,
)

metrics = evaluate_predictions(result, week=12)
print(metrics)

De functie vergelijkt elke voorspelkolom met de gerealiseerde Aantal_studenten en geeft per model één rij terug met:

Metriek Betekenis Interpretatie
n aantal meegetelde (opleiding × herkomst)-rijen klein n → metriek is ruisgevoelig
mae Mean Absolute Error gemiddelde afwijking in studenten
mape Mean Absolute Percentage Error (fractie) elke opleiding telt even zwaar — kleine opleidingen domineren
wape Weighted APE = Σ\|fout\| / Σ\|werkelijk\| (fractie) instellingsbrede procentuele fout, robuust tegen kleine opleidingen
rmse Root Mean Squared Error straft grote uitschieters zwaarder
bias gemiddelde voorspeld − werkelijk positief = structurele overschatting
r2 determinatiecoëfficiënt aandeel verklaarde variantie

mape en wape zijn fracties (0.08 betekent 8%). De metrieken gebruiken dezelfde primitieven en numerus-fixus-uitsluiting als de MAE_*-kolommen, dus ze zijn consistent met de rest van de output.

Evalueren kan alleen via een backtest

Een model evalueren betekent voorspellingen vergelijken met de gerealiseerde instroom. Voor het lopende, nog niet afgeronde collegejaar bestaat die realisatie nog niet — Aantal_studenten is dan leeg en evaluate_predictions geeft een foutmelding. Evalueer daarom een afgerond collegejaar (bijv. voorspel year=2023 terwijl je de realisatie van 2023 als data_student_numbers meegeeft). De modellen trainen sowieso alleen op jaren vóór het voorspeljaar, dus zo'n backtest lekt geen toekomstinformatie.

Geef altijd week mee bij cumulatieve output

In het cumulatieve spoor draagt alleen de peilweekrij de SARIMA_cumulative-voorspelling; latere weken bevatten enkel de vooraanmeldcurve (met NaN als voorspelling). Zonder week=-filter mengt Prognose_ratio bovendien meerdere weken. Geef de gebruikte peilweek mee (evaluate_predictions(result, week=12)) voor een zuivere, één-rij-per-opleiding vergelijking tussen modellen.

Segmenteer met group_by (bijv. group_by="Examentype" of group_by=["Examentype", "Herkomst"]) om te zien waar een model structureel afwijkt.

Backtesten over meerdere jaren (pivot_metrics)

Eén afgerond jaar is één datapunt: de instroom schommelt jaar-op-jaar om redenen buiten het model. Voor een eerlijk beeld backtest je daarom meerdere afgeronde jaren op dezelfde peilweek, evalueer je per jaar met group_by="Collegejaar", en draai je het resultaat met pivot_metrics tot een model × jaar-matrix:

import pandas as pd
from studentprognose import run_pipeline_from_dataframes, evaluate_predictions, pivot_metrics, DataOption

WEEK = 10
frames = [
    run_pipeline_from_dataframes(
        year=jaar, week=WEEK, data_cumulative=df_cum,
        data_student_numbers=df_sc, dataset=DataOption.CUMULATIVE, save_output=False,
    )
    for jaar in (2021, 2022, 2023)            # afgeronde jaren met realisatie
]

metrics = evaluate_predictions(pd.concat(frames, ignore_index=True),
                               week=WEEK, group_by="Collegejaar")
print(pivot_metrics(metrics, value="wape", over="Collegejaar").round(3))
                    2021   2022   2023   mean    min    max  n_groups
prediction
Prognose_ratio     0.078  0.071  0.069  0.073  0.069  0.078         3
SARIMA_cumulative  0.063  0.058  0.066  0.062  0.058  0.066         3

De mean-kolom is het gemiddelde over de jaren; min/max tonen de spreiding. Ligt de jaar-op-jaar spreiding in dezelfde orde als het verschil tussen modellen, trek dan geen harde conclusie uit één jaar. Kies met value= een andere metriek ("mae", "mape", …) en met over= een andere as (bijv. over="Weeknummer" voor de accuraatheid-over-tijd-curve bij één jaar).

Metrieken loggen in MS Fabric / Databricks (MLflow)

Beide platforms hebben MLflow als ingebouwde experiment-tracker. to_mlflow_metrics vlakt het resultaat af tot een dict die je direct kunt loggen, zodat de metrieken in de Fabric/Databricks ML-experiment-UI verschijnen en over runs (jaar/peilweek) vergelijkbaar zijn:

import mlflow
from studentprognose import evaluate_predictions, to_mlflow_metrics

metrics = evaluate_predictions(result, week=WEEK)

with mlflow.start_run(run_name=f"prognose_{YEAR}_wk{WEEK}"):
    mlflow.log_params({"year": YEAR, "week": WEEK, "dataset": "cumulatief"})
    mlflow.log_metrics(to_mlflow_metrics(metrics))
    mlflow.log_table(metrics, "metrics.json")  # volledige tabel als artifact

mlflow zit standaard in de Fabric- en Databricks-runtime; in een Fabric-notebook worden runs automatisch aan het gekoppelde experiment gehangen. Wil je geen MLflow gebruiken, dan kun je het metrics-DataFrame ook gewoon naar een Lakehouse/Delta-tabel wegschrijven voor je eigen monitoring.