Basis

Deze pagina bevat uitvoerbare code.

Wat een tekst over zichzelf zegt

Een tekst is voor een computer eerst een lange rij tekens. Tel je die tekens op de goede manier, dan komt er iets uit dat je vooraf niet zag: hoe groot de woordenschat van de schrijver is, welk woord hij het vaakst gebruikt, hoeveel woorden hij maar één keer nodig had.

Daar hangt meer aan vast dan het lijkt. Leesbaarheidsscores werken zo, plagiaatdetectie werkt zo, en in 2013 werd op deze manier vastgesteld dat de onbekende debutant Robert Galbraith in werkelijkheid J.K. Rowling was: haar woordgebruik verried haar.

Je bouwt in deze opgave het gereedschap dat dat soort vragen beantwoordt.

De kern: een dictionary

Om te tellen hoe vaak elk woord voorkomt heb je iets nodig dat een woord aan een getal koppelt. Dat is precies wat een dictionary is.

Voor de woorden ["spam", "spam", "taart", "spam"]:

{"spam": 3, "taart": 1}

Je loopt de woorden één voor één langs. Ken je het woord nog niet, dan zet je het erin met de waarde 1. Ken je het al, dan tel je er één bij op:

if word in counts:
    counts[word] = counts[word] + 1
else:
    counts[word] = 1

Dat is de hele truc. De rest van deze opgave is eromheen.

De teksten

In assets/teksten/ staan twee bestanden.

kort.txt is één zin, zodat je met de hand kunt narekenen of je functie klopt:

Ik wil taarten en 42 en spam. Ik krijg toch spam en taarten voor de vakantie? Ik wil 42 taarten!

vuurtoren.txt is een stuk van ruim tweehonderd woorden. Daar zie je pas iets interessants.

Wat je gaat maken

Stap

Functie

Doet

1

read_text

de tekst uit een bestand lezen

2

word_list

de tekst opdelen in losse woorden

3

count_words

tellen hoe vaak elk woord voorkomt

4

most_frequent

het meest gebruikte woord vinden

5

words_used_once

tellen hoeveel woorden maar één keer voorkomen

6

report

alles samen afdrukken als verslag

Stap 1: read_text(filename)

Leest het bestand met die naam en geeft de hele inhoud terug als één string.

Aanroep

Resultaat

len(read_text("assets/teksten/kort.txt"))

97

read_text("assets/teksten/kort.txt")[0:6]

"Ik wil"

Dit deed je in week 6 al. Anders dan toen lees je nu het hele bestand in één keer in plaats van regel voor regel, want voor tellen maakt het niet uit waar de regels ophouden.

Hint

Binnen het with-blok geeft file.read() je de hele inhoud als één string.

# jouw oplossing
assert len(read_text("assets/teksten/kort.txt")) == 97
assert read_text("assets/teksten/kort.txt")[0:6] == "Ik wil"

Stap 2: word_list(text)

Geeft een lijst van de woorden in de tekst, allemaal in kleine letters en zonder leestekens.

Aanroep

Resultaat

word_list("Ik wil taarten!")

["ik", "wil", "taarten"]

word_list("Spam, spam.")

["spam", "spam"]

Twee dingen moeten weg, en allebei om dezelfde reden: anders telt hetzelfde woord dubbel.

Hoofdletters. "De" aan het begin van een zin en "de" in het midden zijn hetzelfde woord.

Leestekens. Zonder ingrijpen levert "spam." een ander woord op dan "spam".

Hint

text.lower() maakt van elke letter een kleine letter. Loop daarna de tekens langs en vervang elk leesteken door een spatie voordat je split() gebruikt.

Neem als leestekens in elk geval .,!?;: mee.

# jouw oplossing
assert word_list("Ik wil taarten!") == ["ik", "wil", "taarten"]
assert word_list("Spam, spam.") == ["spam", "spam"]
assert len(word_list(read_text("assets/teksten/kort.txt"))) == 20
assert len(word_list(read_text("assets/teksten/vuurtoren.txt"))) == 227

Stap 3: count_words(words)

Geeft een dictionary die elk woord koppelt aan het aantal keren dat het voorkomt.

Aanroep

Resultaat

count_words(["spam", "spam", "taart"])

{"spam": 2, "taart": 1}

count_words([])

{}

Dit is de kern van de opgave, en de code ervoor staat hierboven al.

# jouw oplossing
assert count_words(["spam", "spam", "taart"]) == {"spam": 2, "taart": 1}
assert count_words([]) == {}
assert count_words(word_list(read_text("assets/teksten/kort.txt")))["taarten"] == 3

Korter met .get()

Nu je .get() uit het college kent, mag count_words ook zo:

counts[word] = counts.get(word, 0) + 1

Dat is dezelfde functie, alleen zonder de if ... else: bestaat word nog niet, dan geeft .get(word, 0) de standaardwaarde 0 terug, en tel je er gewoon één bij op. Beide vormen zijn goed; kies wat voor jou het leesbaarst is.

Stap 4: most_frequent(counts)

Geeft het woord dat het vaakst voorkomt.

Aanroep

Resultaat

most_frequent({"spam": 2, "taart": 1})

"spam"

most_frequent(count_words(word_list(read_text("assets/teksten/vuurtoren.txt"))))

"een"

Hint

for word in counts: loopt de sleutels van een dictionary langs, en met counts[word] heb je het bijbehorende aantal. Onthoud het beste woord tot nu toe en het aantal dat erbij hoort, zoals je bij een hoogste getal ook zou doen.

Let op gelijkspel

In kort.txt komen meerdere woorden even vaak voor. Welk van die woorden je functie teruggeeft hangt af van hoe je het gelijkspel afhandelt; de test hieronder gebruikt daarom vuurtoren.txt, waar dat niet speelt.

# jouw oplossing
assert most_frequent({"spam": 2, "taart": 1}) == "spam"
assert most_frequent(count_words(word_list(read_text("assets/teksten/vuurtoren.txt")))) == "een"

Stap 5: words_used_once(counts)

Geeft het aantal woorden dat precies één keer voorkomt.

Aanroep

Resultaat

words_used_once({"spam": 2, "taart": 1})

1

words_used_once(count_words(word_list(read_text("assets/teksten/vuurtoren.txt"))))

94

Dit getal zegt meer dan je zou denken. Een tekst waarin bijna elk woord één keer voorkomt is een tekst die zich niet herhaalt, en dat is een van de dingen waaraan je een schrijver herkent.

# jouw oplossing
assert words_used_once({"spam": 2, "taart": 1}) == 1
assert words_used_once({}) == 0
assert words_used_once(count_words(word_list(read_text("assets/teksten/vuurtoren.txt")))) == 94

Stap 6: report(filename)

Leest het bestand en drukt een verslag af. Geeft niets terug.

Voor assets/teksten/vuurtoren.txt:

Woorden:            227
Verschillende:      134
Meest gebruikt:     een
Eenmalig gebruikt:   94

Alles wat je hiervoor hebt gemaakt komt hier samen. Deze functie rekent zelf niets uit; hij roept aan en drukt af.

Hint

Het aantal verschillende woorden hoeft geen eigen functie: len() op een dictionary geeft het aantal sleutels.

# jouw oplossing
report("assets/teksten/vuurtoren.txt")

Uitproberen

Zet zelf een tekst in assets/teksten/ en haal hem door report. Een verhaal, een verslag, een hoofdstuk uit een boek dat je leest.

Vergelijk daarna twee teksten van verschillende schrijvers. Zie je verschil in het aantal eenmalig gebruikte woorden?

Tot slot

Je hebt nu een dictionary die woorden aan aantallen koppelt. In het werkcollege maak je er een die woorden aan hun opvolgers koppelt, en daarmee kun je tekst laten schrijven die klinkt als de schrijver die je erin stopte.

Dezelfde structuur, een andere vraag.

En één ding om over na te denken. Dat woordgebruik een schrijver verraadt is handig bij het opsporen van plagiaat, en het is precies wat de auteur van een pseudoniem liever niet had gehad. Hetzelfde gereedschap, twee tegengestelde belangen.