# beslisboom.py - een beslisboom van nul af, getraind op juni en juli, getoetst op augustus
import csv

KENMERKEN = ["temperatuur_C", "vocht_pct", "licht_klx"]


def lees(naam):
    with open(naam, newline="") as f:
        return [{"x": [float(r[k]) for k in KENMERKEN], "y": int(r["slap"]), "tijd": r["tijdstip"]}
                for r in csv.DictReader(f)]


def onzuiverheid(rijen):
    """2 p (1 - p), met p het deel 'slap': 0 als alle labels gelijk zijn, 0,5 bij half om half."""
    p = sum(r["y"] for r in rijen) / len(rijen)
    return 2 * p * (1 - p)


def beste_splitsing(rijen, min_blad):
    """(onzuiverheid, kenmerk, grens) van de beste vraag 'kenmerk <= grens?', of None als er geen is."""
    beste = None
    for k in range(len(KENMERKEN)):
        waarden = sorted(set(r["x"][k] for r in rijen))
        for a, b in zip(waarden, waarden[1:]):
            grens = (a + b) / 2                            # midden tussen twee gemeten waarden
            ja = [r for r in rijen if r["x"][k] <= grens]
            nee = [r for r in rijen if r["x"][k] > grens]
            if len(ja) < min_blad or len(nee) < min_blad:
                continue
            o = (len(ja) * onzuiverheid(ja) + len(nee) * onzuiverheid(nee)) / len(rijen)
            if beste is None or o < beste[0]:
                beste = (o, k, grens)
    return beste


def bouw(rijen, diepte, min_blad=5):
    """Een boom als woordenboek: een blad {'label', 'n'} of een vraag met een ja- en een nee-tak."""
    s = beste_splitsing(rijen, min_blad)
    if diepte == 0 or s is None or s[0] >= onzuiverheid(rijen):
        ja = sum(r["y"] for r in rijen)
        return {"label": int(ja > len(rijen) - ja), "n": len(rijen)}
    _, k, grens = s
    ja = bouw([r for r in rijen if r["x"][k] <= grens], diepte - 1, min_blad)     # de functie roept zichzelf op
    nee = bouw([r for r in rijen if r["x"][k] > grens], diepte - 1, min_blad)
    if "label" in ja and "label" in nee and ja["label"] == nee["label"]:
        return {"label": ja["label"], "n": len(rijen)}      # twee gelijke bladen: de vraag is overbodig
    return {"kenmerk": k, "grens": grens, "ja": ja, "nee": nee}


def voorspel(boom, x):
    while "label" not in boom:
        boom = boom["ja"] if x[boom["kenmerk"]] <= boom["grens"] else boom["nee"]
    return boom["label"]


def toon(boom, inspring=""):
    if "label" in boom:
        print(inspring + ("slap" if boom["label"] else "niet slap"), f"({boom['n']} inspecties)")
    else:
        print(inspring + f"{KENMERKEN[boom['kenmerk']]} <= {boom['grens']:.1f}?")
        toon(boom["ja"], inspring + "  ja:  ")
        toon(boom["nee"], inspring + "  nee: ")


def juist(boom, rijen):
    return sum(voorspel(boom, r["x"]) == r["y"] for r in rijen) / len(rijen)


rijen = lees("slap.csv")
train = [r for r in rijen if r["tijd"] < "2026-08"]         # juni en juli: leren
test = [r for r in rijen if r["tijd"] >= "2026-08"]         # augustus: toetsen
if __name__ == "__main__":
    boom = bouw(train, diepte=2)
    toon(boom)
    print(f"train: {len(train)} inspecties, {juist(boom, train):.0%} juist")
    print(f"test:  {len(test)} inspecties, {juist(boom, test):.0%} juist")
