Bij het bestuderen van relaties tussen hersenfunctie en taal ontdekten psychologen van de universiteit van Alberta (Canada) samen met taalkundigen van de universiteit van Tübingen (Duitsland) dat mensen het vrijwel unaniem eens zijn over de grappigheid van onzinnige woorden. Een overzicht van enkele onzinnige woorden die in dit onderzoek als extreem humoristisch voorspeld werden:
woord | 1-grammen | 2-grammen | 3-grammen |
---|---|---|---|
howaymb | 1.06771 | 0.09682 | 0.00699 |
proffic | 1.33591 | 0.19497 | 0.03164 |
subvick | 1.21500 | 0.14689 | 0.02015 |
quarban | 1.38082 | 0.19559 | 0.02078 |
supplic | 1.50944 | 0.20794 | 0.02595 |
octeste | 2.00986 | 0.44417 | 0.06772 |
tatinse | 2.06316 | 0.47488 | 0.10412 |
De studie toont aan dat hoe kleiner de kans dat een willekeurige opeenvolging van letters een bestaand (Engels) woord vormt, hoe grappiger die combinatie van letters overkomt op mensen. Hoe zou dat komen? Misschien signaleert humor onszelf en anderen wel dat er vermoedelijk iets niet klopt, maar dat er geen gevaar dreigt voor onze veiligheid.
In de studie wordt de mate van grappigheid van een reeks letters als volgt gekwantificeerd. Neem een corpus van bestaande woorden (bijvoorbeeld woorden.txt1) en tel hoe vaak elk $$n$$-gram voorkomt (een $$n$$-gram is een opeenvolging van $$n$$ letters). Zo heeft het woord banaan bijvoorbeeld de volgende bigrammen (2-grammen): ba, an, na, aa en an. Merk op dat het bigram an tweemaal in dit woord voorkomt en de bigrammen ba, na en aa slechts eenmaal. Het aantal voorkomens van een $$n$$-gram $$\text{x}$$ in een gegeven corpus wordt voorgesteld als $$O(\text{x})$$. Zo tellen we bijvoorbeeld 118845 voorkomens van de letter (1-gram) a in het corpus uit het bestand woorden.txt2 waardoor $$O(\text{a}) = 118845$$, 2506 voorkomens van het bigram qu waardoor $$O(\text{qu}) = 2506$$ en 722 voorkomens van het trigram (3-gram) que waardoor $$O(\text{que}) = 722$$.
De waarschijnlijkheid van een $$n$$-gram $$\text{x}$$ (voorgesteld als $$P(\text{x})$$) wordt geschat door $$O(\text{x})$$ te delen door het totaal aantal $$n$$-grammen in het corpus. Op die manier kunnen we voor het corpus uit het bestand woorden.txt3 bijvoorbeeld bepalen dat $$P(\text{a}) = 0.07567$$, $$P(\text{qu}) = 0.001793$$ en $$P(\text{que}) = 0.0005894$$.
De entropie van een $$n$$-gram $$\text{x}$$ (voorgesteld als $$E(\text{x})$$) wordt berekend als \[ E(\text{x}) = - P(\text{x}) \log_2 P(\text{x})\] Hierbij geldt per definitie dat $$E(\text{x}) = 0$$ als $$P(\text{x}) = 0$$, wat consistent is met de limiet \[ \lim_{p \rightarrow 0+} p \log_2 p = 0 \]
Voor een gegeven $$n \in \mathbb{N}_0$$ wordt de grappigheid van een reeks letters $$s_1s_2s_3\ldots s_m$$ (met $$m \geq n$$) dan berekend als de som van de entropie van alle $$n$$-grammen in de reeks letters \[ E(s_1\ldots s_n) + E(s_2\ldots s_{n+1}) + E(s_3\ldots s_{n+2}) + \cdots \]
Gevraagd wordt:
Schrijf een functie ngrammen waaraan een getal $$n \in \mathbb{N}_0$$ (int) en de locatie van een tekstbestand (str) moeten doorgegeven worden. Het tekstbestand moet een corpus van bestaande woorden bevatten, waarbij elk woord enkel uit letters bestaat en op een afzonderlijke regel staat. De functie moet een dictionary (dict) teruggeven waarvan de sleutels gevormd worden door alle $$n$$-grammen die in het gegeven corpus voorkomen. Hierbij wordt geen onderscheid gemaakt tussen hoofdletters en kleine letters en worden kleine letters gebruikt in de sleutels. De dictionary moet elk $$n$$-gram $$\text{x}$$ afbeelden op het aantal voorkomens $$O(\text{x})$$ van het $$n$$-gram in het gegeven corpus.
Schrijf een functie waarschijnlijkheid waaraan twee argumenten moeten doorgegeven worden: i) een $$n$$-gram $$\text{x}$$ (str) en ii) een dictionary (dict) die opgemaakt is zoals diegene die worden teruggegeven bij het aanroepen van de functie ngrammen met als eerste argument $$n$$. De functie moet de waarschijnlijkheid $$P(\text{x})$$ (float) teruggeven, waarbij $$O(\text{x})$$ wordt teruggevonden in de gegeven dictionary (per definitie is $$O(\text{x}) = 0$$ als $$\text{x}$$ geen sleutel is van de gegeven dictionary).
Schrijf een functie entropie waaraan dezelfde twee argumenten moeten doorgegeven worden als bij de functie waarschijnlijkheid. De functie moet de entropie $$E(\text{x})$$ (float) teruggeven, waarbij $$P(\text{x})$$ berekend wordt als bij de functie waarschijnlijkheid.
Schrijf een functie grappigheid waaraan twee argumenten moeten doorgegeven worden: i) een string $$s$$ (str) die enkel bestaat uit letters en ii) een dictionary (dict) die opgemaakt is zoals diegene die worden teruggegeven door de functie ngrammen. De functie moet de grappigheid van $$s$$ (float) teruggegeven, waarbij $$n \in \mathbb{N}_0$$ gelijk is aan de lengte van de sleutels van de gegeven dictionary. De entropie van de $$n$$-grammen moet berekend worden zoals bij de functie entropie. De functie heeft ook nog een optionele parameter genormaliseerd waaraan een Booleaanse waarde kan doorgegeven worden (standaardwaarde: False). Als de waarde True wordt doorgegeven aan de parameter genormaliseerd dan moet de grappigheid van $$s$$ nog gedeeld worden door het aantal $$n$$-grammen in $$s$$.
De functies waarschijnlijkheid, entropie en grappigheid mogen geen onderscheid maken tussen hoofdletters en kleine letters.
In onderstaande voorbeeldsessie gaan we ervan uit dat het tekstbestand woorden.txt4 zich in de huidige directory bevindt.
>>> letters = ngrammen(1, 'woorden.txt')
>>> letters['a']
118845
>>> letters['q']
2535
>>> letters['z']
7450
>>> bigrammen = ngrammen(2, 'woorden.txt')
>>> bigrammen['ac']
6799
>>> bigrammen['qu']
2506
>>> bigrammen['zx']
Traceback (most recent call last):
KeyError: 'zx'
>>> trigrammen = ngrammen(3, 'woorden.txt')
>>> trigrammen['abc']
2
>>> trigrammen['que']
722
>>> trigrammen['xcz']
Traceback (most recent call last):
KeyError: 'xcz'
>>> waarschijnlijkheid('a', letters)
0.07567142511492862
>>> waarschijnlijkheid('Q', letters)
0.0016140945152622664
>>> waarschijnlijkheid('z', letters)
0.00474359137621455
>>> waarschijnlijkheid('ac', bigrammen)
0.0048643609543276645
>>> waarschijnlijkheid('QU', bigrammen)
0.001792923746366396
>>> waarschijnlijkheid('Zx', bigrammen)
0.0
>>> waarschijnlijkheid('abc', trigrammen)
1.6327943479190852e-06
>>> waarschijnlijkheid('QUE', trigrammen)
0.0005894387595987898
>>> waarschijnlijkheid('XcZ', trigrammen)
0.0
>>> entropie('a', letters)
0.28180852742167367
>>> entropie('Q', letters)
0.014970822223506234
>>> entropie('z', letters)
0.03661959827269034
>>> entropie('ac', bigrammen)
0.03737548277295434
>>> entropie('QU', bigrammen)
0.016357686287373385
>>> entropie('Zx', bigrammen)
0.0
>>> entropie('abc', trigrammen)
3.1389206700115134e-05
>>> entropie('QUE', trigrammen)
0.006323717369962047
>>> entropie('XcZ', trigrammen)
0.0
>>> grappigheid('howaymb', letters)
1.0677136135941063
>>> grappigheid('howaymb', letters, genormaliseerd=False)
1.0677136135941063
>>> grappigheid('howaymb', letters, genormaliseerd=True)
0.15253051622772948
>>> grappigheid('Subvick', letters)
1.2150001024776427
>>> grappigheid('QUARBAN', letters)
1.3808215071539807
>>> grappigheid('howaymb', bigrammen)
0.09682092801393552
>>> grappigheid('howaymb', bigrammen, genormaliseerd=False)
0.09682092801393552
>>> grappigheid('howaymb', bigrammen, genormaliseerd=True)
0.01613682133565592
>>> grappigheid('Subvick', bigrammen)
0.14689473722037735
>>> grappigheid('QUARBAN', bigrammen)
0.19558791736702053
>>> grappigheid('howaymb', trigrammen)
0.006989174063509218
>>> grappigheid('howaymb', trigrammen, genormaliseerd=False)
0.006989174063509218
>>> grappigheid('howaymb', trigrammen, genormaliseerd=True)
0.0013978348127018435
>>> grappigheid('Subvick', trigrammen)
0.020151762537186854
>>> grappigheid('QUARBAN', trigrammen)
0.020777229925614614