In diesem Blog geht es um natürliche Sprache und um Literatur. Außerdem geht es um Mengen. Neulinge in Python halten Mengen oft für ein Spielzeug für Mathematiker aber ohne große Relevanz für die tägliche Programmierung. Das Gegenteil ist aber der Fall, denn es gibt zahlreiche Anwendungsfälle für Mengen. Mit ihrer Hilfe kann man beispielsweise doppelte Vorkommen von Objekten aus einer Liste entfernen.

Python und der beste englische Roman

Bernd Klein, Datentyp 'set' und eine Menge Literaten

Bernd Klein, Datentyp ’set‘ und eine Menge Literaten

 

In dem folgenden Beispiel werden wir Mengen nutzen, um die verschiedenen Wörter, die in einem Roman vorkommen, zu bestimmen. Unser Beispiel haben wir aufgebaut um einen Roman, der als bester aber auch am schwersten zu lesender der englischen Sprache gilt. Dieser Roman wurde von James Joyce geschrieben und heißt „Ulysses“.

Wir werden uns nicht um das literarische – also beispielsweise die Schönheit der Sprache oder des Sprachstils – kümmern. Vielmehr geht es uns um die verwendeten Wörter. Unser Vorgehen wird rein statistisch sein. Wir wollen zeigen, dass Joyce in seinem Roman weitaus mehr Wörter als jeder andere Autor verwendet hat. Sein Wortschatz liegt sogar über dem von Shakespeare. Zum Verstehen dieses Blogs müssen Sie aber keine Ahnung vom Englischen haben!

Neben dem Roman „Ulysses“ werden wir auch die Romane „Sons and Lovers“ von D.H. Lawrence, „The Way of All Flesh“ von Samuel Butler, „Robinson Crusoe“ von Daniel Defoe, „To the Lighthouse“ von Virginia Woolf, „Moby Dick“ von Herman Melville betrachten.

Zum Lesen dieses Blogs könnte es sehr hilfreich sein, wenn Sie das Kapitel über Mengen und die beiden ausführlichen Kapitel über reguläre Ausdrücke in meinem Buch durchgearbeitet haben. Ansonsten können Sie auch online die Kapitel Mengen und die zwei Kapitel Reguläre Ausdrücke und fortgeschritten reguläre Ausdrücke, die allerdings nicht den Buchkapiteln entsprechen!

 

Verschiedene Wörter eines Textes

Um alle Wörter aus dem Roman „Ulysses“ herauszuschneiden, können wir die Funktion „findall“ von dem Modul „re“ benutzen. Groß- und Kleinschreibung ist uns egal, deshalb wandeln wir den Text in Kleinschreibung um:
import re

ulysses_txt = open("books/james_joyce_ulysses.txt").read().lower()

words = re.findall(r"\b[\w-]+\b", ulysses_txt)
print("Der Roman Ulysses enthält " + str(len(words)) + " Wörter!")
Der Roman Ulysses enthält 272452 Wörter!

Diese Zahl ist sicherlich eine stattliche Anzahl von Wörtern, sagt aber wenig über die Qualität des Romans aus, denn viele Wörter werden mehrfach vorkommen. Mit Hilfe der Stringmethode count können wir dies verdeutlichen:

for word in ["the", "while", "good", "bad", "ireland", "irish"]:
    print("Das Wort '" + word + "' kommt " + \
          str(words.count(word)) + " mal im Roman vor!" )
Das Wort 'the' kommt 15112 mal im Roman vor!
Das Wort 'while' kommt 123 mal im Roman vor!
Das Wort 'good' kommt 321 mal im Roman vor!
Das Wort 'bad' kommt 90 mal im Roman vor!
Das Wort 'ireland' kommt 90 mal im Roman vor!
Das Wort 'irish' kommt 117 mal im Roman vor!
Interessanter als die bloße Anzahl der Wörter ist die Anzahl der verschiedenen Wörter. Um mehrfache Vorkommen von Wörtern zu eliminieren, benötigen wir die set-Klasse. Wir müssen lediglich die Liste der Wörter „words“ in eine Menge wandeln und in dieser Menge gibt es, wie in jeder Menge, keine doppelten Vorkommen:
diff_words = set(words)
print("'Ulysses' enthält " + str(len(diff_words)) + " verschiedene Wörter!")

Ulysses‘ enthält 29422 verschiedene Wörter!

Dies ist eine beeindruckende Anzahl von verschiedenen Wörtern. Dies wird deutlich, wenn wir uns die Anzah der verschiedenen Wörter in anderen Texten anschauen:

novels = ['sons_and_lovers_lawrence.txt',  
          'the_way_of_all_flash_butler.txt', 
          'robinson_crusoe_defoe.txt', 
          'to_the_lighthouse_woolf.txt', 
          'james_joyce_ulysses.txt', 
          'moby_dick_melville.txt']

for novel in novels:
    txt = open("books/" + novel).read().lower()
    words = re.findall(r"\b[\w-]+\b", txt)
    diff_words = set(words)
    n = len(diff_words)
    print("{name:38s}: {n:5d}".format(name=novel[:-4], n=n))
sons_and_lovers_lawrence              : 10822          
the_way_of_all_flash_butler           : 11434
robinson_crusoe_defoe                 :  6595
to_the_lighthouse_woolf               : 11415
james_joyce_ulysses                   : 29422
moby_dick_melville                    : 18922

Besondere Wörter in Ulysses

Wir wollen nun herausfinden, welche Wörter besonders für den Roman von James Joyce sind, d.h. wir wollen die Wörter finden, die nur in diesem Roman und nicht in den anderen Romanen vorkommen. Die set-Klasse bietet zu diesem eine ideale Operation „-„. Dies kann man in folgendem einfachen Beispiel sehen:

m1 = {"a", "b", "c", "d"}
m2 = {"b", "c", "e"}

m3 = m1 - m2
print(m3)
{'d', 'a'}

Nun wenden wir dies entsprechend auf die Romane an. Wir „subtrahieren“ alle Wörter aus den anderen Romanen von „Ulysses“:

words_in_novel = {}
for novel in novels:
    txt = open("books/" + novel).read().lower()
    words = re.findall(r"\b[\w-]+\b", txt)
    words_in_novel[novel] = words
    
words_only_in_ulysses =  set(words_in_novel['james_joyce_ulysses.txt'])
novels.remove('james_joyce_ulysses.txt')
for novel in novels:
    words_only_in_ulysses -= set(words_in_novel[novel])
    
with open("books/words_only_in_ulysses.txt", "w") as fh:
    txt = " ".join(words_only_in_ulysses)
    fh.write(txt)
    
print(len(words_only_in_ulysses))
15314

15314 ist eine erstaunlich große Zahl.

Die Datei mit den Wörtern, die nur in Ulysses vorkommen enthält merkwürdige und extrem selten vorkommende Wörter:

huntingcrop tramtrack pappin kithogue pennyweight undergarments scission nagyaságos wheedling begad dogwhip hawthornden turnbull calumet covey repudiated pendennis waistcoatpocket nostrum

Gebräuchliche Wörter

Eine andere interessante Aufgabe besteht darin, die Wörter zu finden, die alle Wörter in ihren Romanen verwendet haben. Zu diesem Zweck brauchen wir die set-Methode „intersection“, mit der man die Schnittmenge zweier Mengen bestimmen kann:

# wir beginnen mit den Wörtern aus ulysses
common_words = set(words_in_novel['james_joyce_ulysses.txt'])
for novel in novels:
    common_words &= set(words_in_novel[novel])
    
print(len(common_words))
2200

Die Wörter aus der Menge „common_words“ sind Wörter, die zu den am häufigsten benutzten der englischen Sprache gehören, da sie in allen von uns eingelesenen Texten vorkommen. Dazu schauen wir uns 50 Wörter aus dieser Menge an:

 

counter = 0
for word in common_words:
    print(word, end=", ")
    counter += 1
    if counter == 50:
        break
stock, habit, knowing, belong, united, grateful, promised, absent, contained, envy, race, security, damp, obey, ourselves, fruits, instantly, distress, duty, war, chapter, recovered, offer, copy, shelf, acquaintance, wives, placing, suspicion, pressure, smoke, cast, held, touched, moon, letter, ruin, famous, does, wake, letting, like, prey, positively, walked, dressed, continuing, surface, savage, telling, OOK ULYSSES ***

 

Anmerkung: Wenn Sie mehr über Textfilterung und Python erfahren wollen, empfehle ich Ihnen mein Buch Einführung in Python3 oder in meinem Tutorial die Kapitel über Reguläre Ausdrücke und Reguläre Ausdrücke für Fortgeschrittene. Natürlich können Sie auch alles direkt in einem der Python-Kurs bei Bodenseo lernen.