De næste årtier var der forholdsvis stille i stylometriens verden. Det var svært at komme i kølvandet på Mendenhall for forskere, der for de fleste vedkommende nok i almindelig havde en mere magelig tilgang til tingene. Men i 1935 beskrev lingvisten George Zipf nogle karakteristika ved hyppigheden af de forskellige ords optræden i en given tekst, der senere er almindelig kendt som Zipfs Lov. Mens Hendenhall havde en forkærlighed for at tælle længden på ord, så koncentrerede Zipf sig om at tælle, hvor hyppigt de forskellige ord optræder i teksten. Verden er i sandhed fuld af lystige mennesker. Zipf havde endda et par ordlystne studentermedhjælpere til at tælle med.
Zipfs lov postulerer at i en større tekst-korpus er hyppigheden af et ord omvendt proportionel med dets placering i en rangeret frekvensliste. – Det lød flot, men jeg er ikke sikker på, at jeg selv helt forstod det. Jeg prøver igen: Tæller man hvor hyppigt de forskellige ord findes i en tekst og derefter placerer dem på en liste, hvor det hyppigste ord står øverst, det næsthyppigste som nummer 2, det tredjehyppigste nummer tre, und so weiter, så siger Zipf’s lov, at det hyppigste ord optræder dobbelt så hyppigt som det næsthyppigste ord, tre gange så hyppigt som nummer tre på listen – og sådan fortsætter det indtil det sidste ord er sagt – eller rettere talt med. Plotter man derfor en graf med rangplacering på x-aksen og hyppighed på y-aksen, så får man en hyperbel. Den er kedelig at se på, så derfor laver man i stedet et zipf-plot, en log-log graf, hvor istedet tages logaritmen til rangen og logaritmen til hyppighed. Det giver en tilnærmelsesvis lige streg, hvis Zipfs lov dur, og det gør den sædvanligvis. Den halter dog ofte lidt ved en håndfuld af de øverstplacerede ord på listen og en smule i bunden.
Det finurlige er, at den er gældende for tekster på vidt forskellige sprog. Denne graf fra Wikipedia viser Zipfs-plot for tekster på 30 forskellige sprog:

I det følgende vil jeg efterprøve, om Zipfs lov også gælder for det litterære triumvirat: Kierkegaaard, H.C. Andersen og Grundtvig – eller de er underkastet en guddommelig undtagelse.
Med undgangspunkt i de tre valgte værker (Enten-Eller I-II af Kierkegaard, Mit Livs Eventyr af H.C.Andersen og Verdens Krønike af Grundtvig) vil jeg forsøge at generere en rangordnet ord-frekvensliste for hver og indsat som tabel i Excel – og samtidig få lavet et zipf-plot på de tre lister.
Teksterne hentes ind og omdannes til en liste af ord med funktioner beskrevet i en tidligere blog-post. Herefter kan ord-frekvenslisten dannes med f.eks:
[Serializable] public class WordRank { public int Rank { get; set; } = 0; public string Ord { get; set; } = ""; public int Antal { get; set; } = 0; public double Pct { get; set; } = 0; public double Accumulated { get; set; } = 0; } public static List<WordRank> GetWordFrequencyList(List<string> words, int decimals) { var res = new List<WordRank>(); var dictionary = new Dictionary<string, int>(); foreach (var w in words) { if (dictionary.ContainsKey(w)) dictionary[w]++; else dictionary.Add(w, 1); } var totalWords = dictionary.Sum(p => p.Value); int counter = 1; foreach (var item in dictionary.OrderByDescending(o => o.Value)) { WordRank rank = new WordRank(); rank.Rank = counter; rank.Antal = item.Value; rank.Ord = item.Key; rank.Pct = Math.Round(item.Value * 100 / (double)totalWords, decimals); rank.Accumulated = res.Sum(p => p.Pct) + rank.Pct; res.Add(rank); counter++; } return res; }
Herefter er det ganske enkelt at få generet Excel-arket med hjælp fra EPPlus-wrapper, beskrevet i en anden blog-post.
List<TextInfo> source = new List<TextInfo>() { new TextInfo(){Title = "Enten-Eller",Filename = "enteneller.txt" }, new TextInfo(){Title = "Mit Livs Eventyr",Filename = "hca.txt" }, new TextInfo(){Title = "Verdens Krønike",Filename = "gv5.txt" } }; //Opret Excel-fil var excel = new Export.Excel("Zipf_lov.xlsx"); //Tilføj Excel-ark til filen var sheet = excel.AddSheet("Ordfrekvensliste", true); // Tilføj et XYScatterLines-chart var chart = sheet.AddChart("ZIPF's LOV", eChartType.XYScatterLinesNoMarkers,500,500, logaritmic:true); // Styling: chart.XAxis.Title.Text = "Log (Rank)"; chart.YAxis.Title.Text = "Log (Hyppighed)"; chart.Legend.Position = eLegendPosition.Top; chart.XAxis.MaxValue = 20000; foreach (var book in source) { //Indlæs tekst var text = Stylo.PreProcessText(Path.Combine(@"..\..\Tekster\", book.Filename)); //Hug det op i ord var words = Stylo.GetWords(text,false); //Dan ord-frekvensliste var wr = Stylo.GetWordFrequencyList(words, 3); //Tilføj tabel med ord-frekvenslisten til arket sheet.AddTable($"{book.Title}", wr); //Tilføj data-serie til grafen var s = sheet.AddSeries(chart, "Rank", "Pct"); } //Gem fil og åben automatisk i Excel excel.Save(true);
Nedenstående viser resultatet i Excel – her vist de første 25 ord og deres hyppighed i de tre skrifter. Listen kan efter behag udvides med samlet frekvensliste, z-score, Burrows delta – eller hvad man nu eller gerne vil imponere med på den næste date.

Og såmænd om ikke også Zipf’s plot ser ud som det skal. Hvor forskellig i stil og indhold de tre giganten er, så ender de alligevel op med en stor set identisk kurve.

En væsentlig pointe er, at nogle få ord bruges ekstremt meget i sproget og mange ord yderst sjældent. For både Søren Kierkegaard, Grundtvig og H.C. Andersen ses det, at en fjerdedel af deres forfatterskab udgøres af 13 ord – lidt popsmart sagt. Under 25 ord udgør en tredjedel af alle ord i værkerne og 80 ord halvdelen.
Såkaldte funktionsord er fremtrædende i toppen af ordfrekvenslister. Besynderligt navn, for det er ord, der absolut ingen funktion har, bortset fra at binde teksten sammen – før den når til det, som vi alle venter på: indholdsordene. Brugen af de hyppige funktionsord foregår mere ubevidst, og de har vist sig at være en stærk forfatter-signatur, der i betydelig grad har en stabilitet på tværs af genre og over tid.
Stylometri i C# del 1, del 2 og del 4.