Niet Abraham Lincoln1 heeft de zogenoemde Bixbybrief2 uit 1864 geschreven, maar wel zijn secretaris. Tot die conclusie kwam een groep experts in de forensische linguïstiek op basis van een computationele tekstanalyse. De brief geldt als één van de mooiste stukken proza van een Amerikaanse president.
Boston, 25 november 1864. Bij Lydia Parker Bixby valt een handgeschreven brief in de bus afkomstig van niemand minder dan Abraham Lincoln, de president van de Verenigde Staten van Amerika die op dat moment al dik drie jaar in een burgeroorlog verwikkeld zijn met de Geconfedereerde Staten van Amerika. Liefst vijf zonen van mevrouw Bixby zijn in dat conflict gesneuveld. In een poging om haar lijden te verzachten, schrijft hij haar enkele troostende woorden. Diezelfde dag verschijnt de brief ook in enkele lokale kranten waar hij lezers massaal naar de keel grijpt. Een legende is geboren.
Meteen rijzen echter vragen over de auteur van de brief. Die is dan wel met "A. Lincoln" ondertekend, toch vermoeden velen dat zijn secretaris John Hay3 de tekst heeft geschreven. Deze stelling bewijzen, is schier onmogelijk, ook al omdat de originele brief vrij snel verloren gaat. Andere analysetechnieken op basis van onder meer stijl en woordkeuze schieten eveneens te kort, hoofdzakelijk omdat de Bixbybrief vrij beknopt is en zo te weinig aanknopingspunten biedt.
Met een nieuwe techniek hebben experts in de forensische linguïstiek verbonden aan de universiteit van Manchester de brief nu toch aan een grondige analyse kunnen onderwerpen. Ze concluderen dat Hay de ware auteur is. De techniek heet $$n$$-gram tracing en legt linguïstische sequenties in teksten bloot, ook als die erg kort zijn. Eerst hebben ze 500 teksten van Lincoln en 500 teksten van Hay geanalyseerd. Met die resultaten in het achterhoofd, legden ze vervolgens de Bixbybrief onder de loep. Concreet gingen ze op zoek naar delen van woorden die overeenkwamen met de schrijfstijl van Lincoln en Hay (of juist niet).
Hun onderzoek werpt nieuw licht op de Bixbybrief, die sinds lang bekend staat als een van de mooiste stukken proza van Lincoln en van een Amerikaanse president tout court. Overal in de VS zijn passages uit de brief op standbeelden en monumenten aangebracht. Oud-president George W. Bush4 las de brief op 11 september 2011 voor naar aanleiding van de 10e verjaardag van de aanslagen in New York en de brief komt ook voor in de film Saving private Ryan5 van Steven Spielberg uit 1998.
Overigens is de Bixbybrief mogelijk gedeeltelijk op leugens gebaseerd. Hoewel mevrouw Bixby verklaarde dat vijf van haar zonen in de burgeroorlog waren gesneuveld, zouden in werkelijkheid liefst twee onder hen het conflict overleefd hebben. Volgens sommigen heeft ze dit verzwegen omdat ze voor één van hen een overlevingspensioen trok. Anderen menen dat ze in werkelijkheid sympathieën koesterde voor de Geconfedereerde Staten.
Om de auteur van een tekst te ontmaskeren, worden bij $$n$$-gram tracing eerste profielen opgesteld van de tekst en van teksten die met heel grote zekerheid toegeschreven worden aan enkele mogelijke auteurs. We gebruiken het volgende tekstbestand als voorbeeld om uit te leggen hoe zo'n profiel wordt opgebouwd.
Knock-knock. Who's there? To. To who? No, to whom.
Eerst wordt de tekst opgeschoond door
alle hoofdletters om te zetten naar kleine letters
alle karakters die geen letter zijn te vervangen door een onderstrepingsteken (_)
alle opeenvolgende onderstrepingstekens te vervangen door één enkel onderstrepingsteken
onderstrepingstekens vooraan en achteraan te verwijderen
Bij het opschonen worden enkel de 26 letters van het alfabet (a–z) als letters beschouwd, dus geen witruimtekarakters6, leestekens of letters met diakritische tekens7. De voorbeeldtekst wordt op die manier opgeschoond tot
knock_knock_who_s_there_to_to_who_no_to_whom
Daarna wordt de opgeschoonde tekst opgesplitst in $$n$$-grammen, waarbij een $$n$$-gram niets anders is dan een reeks van $$n \in \mathbb{N}_0$$ opeenvolgende karakters. De voorbeeldtekst bestaat bijvoorbeeld uit de volgende 3-grammen:
kno kno who the to_ who _to noc noc ho_ her o_t ho_ to_ ock ock o_s ere _to o_n o_w ck_ ck_ _s_ re_ to_ _no _wh k_k k_w s_t e_t o_w no_ who _kn _wh _th _to _wh o_t hom
Deze voorstelling geeft ook expliciet aan dat de $$n$$-grammen elkaar gedeeltelijk overlappen. Het aantal opeenvolgende $$n$$-grammen in de opgeschoonde versie van tekst $$t$$ wordt genoteerd als $$c_n^t$$. Als $$t$$ het voorbeeldbestand is, dan geldt dat $$c_3^t = 42$$. De verzameling van alle $$n$$-grammen in de opgeschoonde versie van tekst $$t$$ wordt genoteerd als $$\Omega_n^t$$.
Het profiel van een tekst $$t$$ is niets anders dan een frequentietabel van de $$n$$-grammen in de opgeschoonde versie van $$t$$. Het aantal keer dat $$n$$-gram $$\omega$$ voorkomt in de opgeschoonde versie van tekst $$t$$ wordt genoteerd als $$p_n^t(\omega)$$. Als $$t$$ het voorbeeldbestand is dan zien we dat $$p_3^t(\texttt{the}) = 1$$ (groen), $$p_3^t(\texttt{_wh}) = 3$$ (blauw) en $$p_3^t(\texttt{o_t}) = 2$$ (oranje).
De laatste stap van $$n$$-gram tracing bestaat erin om het profiel van een tekst $$t$$ waarvan de auteur moet bepaald worden en het profiel van een tekst $$a$$ die worden toegeschreven aan een bekende auteur (dit mag ook een bundeling van teksten van die auteur zijn) te gebruiken om de mogelijke attributie van de auteur aan de tekst te bepalen aan de hand van de formule \[ -\sum_{\omega \in \Omega_n^t}p_n^t(\omega) \ln\left(\frac{1 + p_n^a(\omega)}{c_n^a}\right) \] waarbij $$\ln(x)$$ staat voor de natuurlijke logaritme van $$x$$. Hoe groter de attributie, hoe groter de kans dat tekst $$t$$ geschreven werd door de auteur van tekst $$a$$. Gevraagd wordt:
Schrijf een functie opschonen waaraan een tekst (str)
moet doorgegeven worden. De functie moet de opgeschoonde versie (str)
van de gegeven tekst teruggeven.
Schrijf een functie ngrammen waaraan een tekst (str) moet doorgegeven worden. De functie heeft ook nog een optionele parameter n waaraan een getal $$n \in \mathbb{N}_0$$ (int) kan doorgegeven worden (standaardwaarde: 1). De functie moet een lijst (list) teruggeven met alle opeenvolgende $$n$$-grammen in de opgeschoonde versie van de gegeven tekst, opgelijst in de volgorde waarin ze in de opgeschoonde tekst voorkomen.
Schrijf een functie profiel waaraan de locatie (str) van een tekstbestand moet doorgegeven worden. De functie heeft ook nog een optionele parameter n waaraan een getal $$n \in \mathbb{N}_0$$ (int) kan doorgegeven worden (standaardwaarde: 1). De functie moet een dictionary (dict) teruggeven die elk $$n$$-gram $$\omega \in \Omega_n^t$$ afbeeldt op $$p_n^t(\omega)$$, waarbij $$t$$ de tekst is uit het gegeven tekstbestand.
Schrijf een functie aantal_ngrammen waaraan het profiel (dict) van een tekst $$t$$ moet doorgegeven worden. De functie moet de waarde $$c_n^t$$ teruggeven.
Schrijf een functie attributie waaraan twee argumenten moeten doorgegeven worden: i) het profiel van een tekst $$t$$ waarvan de auteur moet bepaald worden en ii) het profiel van een tekst $$a$$ die wordt toegeschreven aan een bekende auteur. De functie moet de mogelijke attributie aan tekst $$t$$ teruggeven van de auteur die tekst $$a$$ geschreven heeft.
De profielen die aan de functies aantal_ngrammen en attributie doorgegeven worden zijn dictionaries (dict) zoals die teruggegeven worden door de functie profiel en mogen nooit gewijzigd worden. De tekstbestanden die aan de functie profiel doorgegeven worden, gebruiken de UTF-88 tekencodering. Bij het openen van een tekstbestand bestand.txt kan je de gebruikte tekencodering meegeven aan de parameter encoding van de ingebouwde functie open:
>>> open('bestand.txt', 'r', encoding='utf-8')
In onderstaande voorbeeldsessie gaan we ervan uit dat de tekstbestanden knock.txt9, bixby.txt10, lincoln.txt11, hay.txt12 en obama.txt13 zich in de huidige directory bevinden.
>>> opschonen("What's wrong? NOTHING's wrong!")
'what_s_wrong_nothing_s_wrong'
>>> opschonen("Knock-knock. Who's there? To. To who? No, to whom.")
'knock_knock_who_s_there_to_to_who_no_to_whom'
>>> opschonen('The past, the present, and the future walked into a bar. It was tense.')
'the_past_the_present_and_the_future_walked_into_a_bar_it_was_tense'
>>> ngrammen("What's wrong? NOTHING's wrong!")
['w', 'h', 'a', 't', '_', 's', '_', 'w', 'r', 'o', 'n', 'g', '_', 'n', 'o', 't', 'h', 'i', 'n', 'g', '_', 's', '_', 'w', 'r', 'o', 'n', 'g']
>>> ngrammen("Knock-knock. Who's there? To. To who? No, to whom.", 3)
['kno', 'noc', 'ock', 'ck_', 'k_k', '_kn', 'kno', 'noc', 'ock', 'ck_', 'k_w', '_wh', 'who', 'ho_', 'o_s', '_s_', 's_t', '_th', 'the', 'her', 'ere', 're_', 'e_t', '_to', 'to_', 'o_t', '_to', 'to_', 'o_w', '_wh', 'who', 'ho_', 'o_n', '_no', 'no_', 'o_t', '_to', 'to_', 'o_w', '_wh', 'who', 'hom']
>>> ngrammen('The past, the present, and the future walked into a bar. It was tense.', n=2)
['th', 'he', 'e_', '_p', 'pa', 'as', 'st', 't_', '_t', 'th', 'he', 'e_', '_p', 'pr', 're', 'es', 'se', 'en', 'nt', 't_', '_a', 'an', 'nd', 'd_', '_t', 'th', 'he', 'e_', '_f', 'fu', 'ut', 'tu', 'ur', 're', 'e_', '_w', 'wa', 'al', 'lk', 'ke', 'ed', 'd_', '_i', 'in', 'nt', 'to', 'o_', '_a', 'a_', '_b', 'ba', 'ar', 'r_', '_i', 'it', 't_', '_w', 'wa', 'as', 's_', '_t', 'te', 'en', 'ns', 'se']
>>> knock = profiel('knock.txt14', 3)
>>> len(knock)
27
>>> knock['the']
1
>>> knock['_wh']
3
>>> knock['o_t']
2
>>> aantal_ngrammen(knock)
42
>>> bixby = profiel('bixby.txt15', n=3)
>>> len(bixby)
456
>>> bixby['the']
17
>>> bixby['in_']
3
>>> bixby['f_t']
4
>>> aantal_ngrammen(bixby)
743
>>> lincoln = profiel('lincoln.txt16', n=3)
>>> attributie(bixby, lincoln)
5209.447183892647
>>> hay = profiel('hay.txt17', n=3)
>>> attributie(bixby, hay)
5216.17091674669
>>> obama = profiel('obama.txt18', n=3)
>>> attributie(bixby, obama)
5079.372864440405
In 2013 werkten twee journalisten van The Sunday Times19 samen met professor Patrick Juola20 van de universiteit van Duquesne (VSA) om J.K. Rowling21 te ontmaskeren als de ware auteur van Koekoeksjong22, het boek dat ze in 2013 onder het pseudoniem Robert Galbraith publiceerde. Ook daarvoor gebruikten ze een variant van de n-gram tracing techniek.
Het verhaal werd breed uitgesmeerd in de pers, met onder andere een uitgebreid artikel in de New York Times23. In het tijdschrift Time24 wordt uitgelegd hoe deze ontdekking tot stand kwam:
As one part of his work, Juola uses a program to pull out the hundred most frequent words across an author's vocabulary. This step eliminates rare words, character names and plot points, leaving him with words like of and but, ranked by usage. Those words might seem inconsequential, but they leave an authorial fingerprint on any work. "Propositions and articles and similar little function words are actually very individual," Juola says. "It's actually very, very hard to change them because they're so subconscious."
Brooks R, Flyn C (2013). JK Rowling, the cuckoo in crime novel nest. The Sunday Times. 25
Grieve J, Clarke I, Chiang E, Gideon H, Heini A, Nini A, Waibel E (2018). Attributing the Bixby Letter using n-gram tracing. Digital Scholarship in the Humanities. 26
Juola P (2008). Authorship attribution. Foundations and Trends in Information Retrieval 1(3), 233–334. 27