Vorverarbeitung von Texten mit Python und NLTK

Für viele Aufgaben müssen Texte immer auf der gleichen Art analysiert werden. Für eine Aufgabe wie die Sprachidentifikation können wir einen Text als eine lange Zeichenkette betrachten. Meistens brauchen wir aber eine Liste von Wörtern, mit denen wir weiter arbeiten können.

Wenn wir erstmal den Text haben (was oft nicht einfach ist, wenn der Text z.B. aus einer Webseite extrahiert werden muss), teilen wir den Text zuerst in Sätze und die Sätze anschließend in Wörtern auf. In vielen Fällen führen wir die Wörter dann noch auf ihren Grundform zurück, und bestimmen die Wortart für jedes Wort, da diese oft wichtige Informationen für die wietere Verarbeitung gibt. Eventuell folgen dann Schritte, wie das Nachschlagen der Wörter in einem Thesaurus oder das Erkennen von sogenannten Named Entities: Namen von Personen, Institutionen, Produkte, usw.

Typische Verarbeitungsschritte bei der Textanalyse

Für die Analyse von Englischen Texten sind weitaus mehr Werkzeuge verfügbar als für das Deutsche. Die Verarbeitung von englischen Texten ist daher etwas einfacher. Das wir außerdem auch of englische Texte analysieren müssen, schauen wir uns die Standardverarbeitung zunächst für das Englische an.

Englische Texte

Wenn wir mit Python Texte analysieren wollen, ist das Paket NLTK der Stanford University unverzichtbar. Dieses Paket umfasst State-of-the-Art Implementierungen für so gut wie alle wichtige Algorithmen aus der Sprachverarbeitung und ist in den meisten Python-Distributionen enthalten.

NLTK enthält auch eine große Menge Ressourcen, die Sie nutzen können, oder die manche der bereitgestellten Algorithmen brauchen. Diese sind meistens noch nicht installiert. Das nachinstallieren dieser Ressourcen ist ganz einfach.

In [ ]:
import nltk

nltl.download()

Es sollte jetz ein Fernster geöffnet werden. Wählen Sie alles aus dem NLTK-Buch zum installieren aus.

Satzerkennung und Tokenization

Mit der Python-Funktion Split() können wir einen Text leicht aufteilen. In vielen Fällen machen wir dann aber Fehler. Es fängt schon damit an, dass Satzzeichen am vorangehenden Wort geschrieben werden, aber nicht dazu gehören, es sei denn, das Wort ist eine Abkürzung oder eine Ordinalzahl, aber letzteres nur im Deutschen. Statt uns über alle Ausnahmen Gedanken zu machen, nutzen wir hierfür einfach eine Funktion aus NLTK. Diese Funktion ist übrigens nicht auf Regeln basiert, sondern wurde aus vielen Beispielen gelernt.

Im nächsten Beispiel sehen wir, wie man mit Python und NLTK eine Zeichenkette in eine Liste von Wörtern aufteilen kann. Wir finden nicht nur Wörter, sondern auch Satzzeichen, Zahlen und Symbole. Der Sammelbegriff für diese Einheiten ist Token. Das Zerlegen einer Zeichenkette in Tokens wird daher Tokenisierung oder auf Englisch Tokenization genannt.

In [1]:
import nltk

sentence= "At eight o'clock on Thursday morning... Arthur didn't feel very good."
tokens = nltk.word_tokenize(sentence, language='english')
print(tokens)
['At', 'eight', "o'clock", 'on', 'Thursday', 'morning', '...', 'Arthur', 'did', "n't", 'feel', 'very', 'good', '.']

In vielen Fällen ist es wichtig zu wissen, wo die Satzgrenzen sind: Die Wörter in einem Satz haben eine viel engere Beziehung zueinander, als Wörter in verscheidenen Sätzen. Zum Beispiel stehen Begriffe, die aus mehreren Wörtern bestehen, wie "Bundesministerium für Gesundheit" immer innerhalb eines Satzes.

Die Frage ist nun, ob wir den Text erst in Wörter aufteilen und dann in Sätzen oder umgekehrt. Um zu entscheiden, ob ein Punkt ein Satzende markiert, muss man unter anderem Wissen, ob das Wort vor dem Punkt eine Abkürzung ist. Man muss das Wort also schon mal in der langen Zeichenkette erkannt haben. Der Sentence Splitter von NLTK arbeitet aber trotzdem auf ganzen Texten und braucht keine vorangehende Zerlegung in Tokens.

Zum Ausprobieren, lesen wir einen kurzen englischen Text ein. Den hier genutzten Beispieltext finden Sie hier: http://textmining.wp.hs-hannover.de/texte/hanover.txt . Der Text ist der Wikipediaseite http://en.wikipedia.org/wiki/Hanover.html entnommen.

In [2]:
import nltk
import codecs

textfile = codecs.open("texte/hanover.txt", "r", "utf-8-sig")
text = textfile.read()
textfile.close()

sentences = nltk.sent_tokenize(text,language='english')

tokenized_text = [nltk.word_tokenize(sent, language='english') for sent in sentences]

print(tokenized_text[0])
print(tokenized_text[5])
['Hanover', 'or', 'Hannover', '(', '/ˈhænoʊvər/', ';', 'German', ':', 'Hannover', ',', 'pronounced', '[', 'haˈnoːfɐ', ']', '(', 'About', 'this', 'sound', 'listen', ')', ')', ',', 'on', 'the', 'River', 'Leine', ',', 'is', 'the', 'capital', 'and', 'largest', 'city', 'of', 'the', 'German', 'state', 'of', 'Lower', 'Saxony', '(', 'Niedersachsen', ')', ',', 'and', 'was', 'once', 'by', 'personal', 'union', 'the', 'family', 'seat', 'of', 'the', 'Hanoverian', 'Kings', 'of', 'the', 'United', 'Kingdom', 'of', 'Great', 'Britain', 'and', 'Ireland', ',', 'under', 'their', 'title', 'as', 'the', 'dukes', 'of', 'Brunswick-Lüneburg', '(', 'later', 'described', 'as', 'the', 'Elector', 'of', 'Hanover', ')', '.']
['With', 'a', 'population', 'of', '518,000', ',', 'Hanover', 'is', 'a', 'major', 'centre', 'of', 'Northern', 'Germany', 'and', 'the', 'country', "'s", 'thirteenth', 'largest', 'city', '.']

Wortarterkennung (Part-of-Speech Tagging)

Die Wortart eines Wortes gibt oft wichtige Informationen. Den Hauptinhalt eines Textes erkennen wir beispielsweise schon an den benutzten Substantiven und Verben, während Adjektive und Adverbien wenig beitragen, und Artikel, Präpositionen und Hilfsverben hierzu überhaupt keine nützliche Information liefern.

Wortarten werden im Englischen Part of Speech genannt, ein Programm, dass die Wortarten zuweist, daher Part of speaach tagger oder einfach POS tagger. Der NLTK enthält einen guten (statistischen) POS Tagger.

Die Wortklassen, die dieser Tagger zuweist, sind nicht genau die, die Sie in der Schule gelernt haben. Manche Klassen sind unterteilt, es gibt Tags, die neben der Wortklasse weitere Informationen, wie z.B. 3. Person Singular enthalten und es gibt Klassen für Wörter, die oft schwierig einzuteilen sind. Die Tags, die im folgenden Beispiel genutzt werden, sind die aus dem sogenannten Pennsylvenia Treebank Tagset. Eine Beschreibung der Tags sowie Beispiel dafür bekommen Sie mit der help Funktion:

In [3]:
nltk.help.upenn_tagset()
$: dollar
    $ -$ --$ A$ C$ HK$ M$ NZ$ S$ U.S.$ US$
'': closing quotation mark
    ' ''
(: opening parenthesis
    ( [ {
): closing parenthesis
    ) ] }
,: comma
    ,
--: dash
    --
.: sentence terminator
    . ! ?
:: colon or ellipsis
    : ; ...
CC: conjunction, coordinating
    & 'n and both but either et for less minus neither nor or plus so
    therefore times v. versus vs. whether yet
CD: numeral, cardinal
    mid-1890 nine-thirty forty-two one-tenth ten million 0.5 one forty-
    seven 1987 twenty '79 zero two 78-degrees eighty-four IX '60s .025
    fifteen 271,124 dozen quintillion DM2,000 ...
DT: determiner
    all an another any both del each either every half la many much nary
    neither no some such that the them these this those
EX: existential there
    there
FW: foreign word
    gemeinschaft hund ich jeux habeas Haementeria Herr K'ang-si vous
    lutihaw alai je jour objets salutaris fille quibusdam pas trop Monte
    terram fiche oui corporis ...
IN: preposition or conjunction, subordinating
    astride among uppon whether out inside pro despite on by throughout
    below within for towards near behind atop around if like until below
    next into if beside ...
JJ: adjective or numeral, ordinal
    third ill-mannered pre-war regrettable oiled calamitous first separable
    ectoplasmic battery-powered participatory fourth still-to-be-named
    multilingual multi-disciplinary ...
JJR: adjective, comparative
    bleaker braver breezier briefer brighter brisker broader bumper busier
    calmer cheaper choosier cleaner clearer closer colder commoner costlier
    cozier creamier crunchier cuter ...
JJS: adjective, superlative
    calmest cheapest choicest classiest cleanest clearest closest commonest
    corniest costliest crassest creepiest crudest cutest darkest deadliest
    dearest deepest densest dinkiest ...
LS: list item marker
    A A. B B. C C. D E F First G H I J K One SP-44001 SP-44002 SP-44005
    SP-44007 Second Third Three Two * a b c d first five four one six three
    two
MD: modal auxiliary
    can cannot could couldn't dare may might must need ought shall should
    shouldn't will would
NN: noun, common, singular or mass
    common-carrier cabbage knuckle-duster Casino afghan shed thermostat
    investment slide humour falloff slick wind hyena override subhumanity
    machinist ...
NNP: noun, proper, singular
    Motown Venneboerger Czestochwa Ranzer Conchita Trumplane Christos
    Oceanside Escobar Kreisler Sawyer Cougar Yvette Ervin ODI Darryl CTCA
    Shannon A.K.C. Meltex Liverpool ...
NNPS: noun, proper, plural
    Americans Americas Amharas Amityvilles Amusements Anarcho-Syndicalists
    Andalusians Andes Andruses Angels Animals Anthony Antilles Antiques
    Apache Apaches Apocrypha ...
NNS: noun, common, plural
    undergraduates scotches bric-a-brac products bodyguards facets coasts
    divestitures storehouses designs clubs fragrances averages
    subjectivists apprehensions muses factory-jobs ...
PDT: pre-determiner
    all both half many quite such sure this
POS: genitive marker
    ' 's
PRP: pronoun, personal
    hers herself him himself hisself it itself me myself one oneself ours
    ourselves ownself self she thee theirs them themselves they thou thy us
PRP$: pronoun, possessive
    her his mine my our ours their thy your
RB: adverb
    occasionally unabatingly maddeningly adventurously professedly
    stirringly prominently technologically magisterially predominately
    swiftly fiscally pitilessly ...
RBR: adverb, comparative
    further gloomier grander graver greater grimmer harder harsher
    healthier heavier higher however larger later leaner lengthier less-
    perfectly lesser lonelier longer louder lower more ...
RBS: adverb, superlative
    best biggest bluntest earliest farthest first furthest hardest
    heartiest highest largest least less most nearest second tightest worst
RP: particle
    aboard about across along apart around aside at away back before behind
    by crop down ever fast for forth from go high i.e. in into just later
    low more off on open out over per pie raising start teeth that through
    under unto up up-pp upon whole with you
SYM: symbol
    % & ' '' ''. ) ). * + ,. < = > @ A[fj] U.S U.S.S.R * ** ***
TO: "to" as preposition or infinitive marker
    to
UH: interjection
    Goodbye Goody Gosh Wow Jeepers Jee-sus Hubba Hey Kee-reist Oops amen
    huh howdy uh dammit whammo shucks heck anyways whodunnit honey golly
    man baby diddle hush sonuvabitch ...
VB: verb, base form
    ask assemble assess assign assume atone attention avoid bake balkanize
    bank begin behold believe bend benefit bevel beware bless boil bomb
    boost brace break bring broil brush build ...
VBD: verb, past tense
    dipped pleaded swiped regummed soaked tidied convened halted registered
    cushioned exacted snubbed strode aimed adopted belied figgered
    speculated wore appreciated contemplated ...
VBG: verb, present participle or gerund
    telegraphing stirring focusing angering judging stalling lactating
    hankerin' alleging veering capping approaching traveling besieging
    encrypting interrupting erasing wincing ...
VBN: verb, past participle
    multihulled dilapidated aerosolized chaired languished panelized used
    experimented flourished imitated reunifed factored condensed sheared
    unsettled primed dubbed desired ...
VBP: verb, present tense, not 3rd person singular
    predominate wrap resort sue twist spill cure lengthen brush terminate
    appear tend stray glisten obtain comprise detest tease attract
    emphasize mold postpone sever return wag ...
VBZ: verb, present tense, 3rd person singular
    bases reconstructs marks mixes displeases seals carps weaves snatches
    slumps stretches authorizes smolders pictures emerges stockpiles
    seduces fizzes uses bolsters slaps speaks pleads ...
WDT: WH-determiner
    that what whatever which whichever
WP: WH-pronoun
    that what whatever whatsoever which who whom whosoever
WP$: WH-pronoun, possessive
    whose
WRB: Wh-adverb
    how however whence whenever where whereby whereever wherein whereof why
``: opening quotation mark
    ` ``

Jetzt aber ein Beispiel für den Tagger:

In [4]:
tags = nltk.pos_tag(tokenized_text[0])
print(tags)
[('Hanover', 'NNP'), ('or', 'CC'), ('Hannover', 'NNP'), ('(', '('), ('/ˈhænoʊvər/', 'NNP'), (';', ':'), ('German', 'JJ'), (':', ':'), ('Hannover', 'NNP'), (',', ','), ('pronounced', 'VBD'), ('[', 'JJ'), ('haˈnoːfɐ', 'NN'), (']', 'NNP'), ('(', '('), ('About', 'IN'), ('this', 'DT'), ('sound', 'NN'), ('listen', 'RB'), (')', ')'), (')', ')'), (',', ','), ('on', 'IN'), ('the', 'DT'), ('River', 'NNP'), ('Leine', 'NNP'), (',', ','), ('is', 'VBZ'), ('the', 'DT'), ('capital', 'NN'), ('and', 'CC'), ('largest', 'JJS'), ('city', 'NN'), ('of', 'IN'), ('the', 'DT'), ('German', 'JJ'), ('state', 'NN'), ('of', 'IN'), ('Lower', 'NNP'), ('Saxony', 'NNP'), ('(', '('), ('Niedersachsen', 'NNP'), (')', ')'), (',', ','), ('and', 'CC'), ('was', 'VBD'), ('once', 'RB'), ('by', 'IN'), ('personal', 'JJ'), ('union', 'NN'), ('the', 'DT'), ('family', 'NN'), ('seat', 'NN'), ('of', 'IN'), ('the', 'DT'), ('Hanoverian', 'JJ'), ('Kings', 'NNP'), ('of', 'IN'), ('the', 'DT'), ('United', 'NNP'), ('Kingdom', 'NNP'), ('of', 'IN'), ('Great', 'NNP'), ('Britain', 'NNP'), ('and', 'CC'), ('Ireland', 'NNP'), (',', ','), ('under', 'IN'), ('their', 'PRP$'), ('title', 'NN'), ('as', 'IN'), ('the', 'DT'), ('dukes', 'NNS'), ('of', 'IN'), ('Brunswick-Lüneburg', 'NNP'), ('(', '('), ('later', 'RB'), ('described', 'VBN'), ('as', 'IN'), ('the', 'DT'), ('Elector', 'NNP'), ('of', 'IN'), ('Hanover', 'NNP'), (')', ')'), ('.', '.')]

Lemmatisierung

In vielen Sprachen, wie auch im Deutschen und Englischen, können Wörter in verschiedenen Formen auftreten. (Es gibt auch Sprachen, in denen das nicht der Fall ist. Diese Sprachen werden isolierende Sprachen genannt. Beispiele hierfür sind Mandarin (Chinesisch) und Vietnamesisch)). Oft ist es wichtig, den Grundfom eines Wortes, das im Text in flektierter Form vorkommt, zu bestimmen. Es ist wichtig, dass wir hier drei Begriffe klar trennen:

  • Lemma - Die Form des Wortes, wie sie in einem Wörterbuch steht. Z.B.: Haus, laufen, begründen
  • Stamm - Das Wort ohne Flexionsendungen (Prefixe und Suffixe). Z.B.: Haus, lauf, begründ
  • Wurzel - Kern des Wortes, von dem das Wort ggf. durch Derivation abgeleitet wurde. Z.B.: Haus, lauf, Grund

Wir unterscheiden jetzt Stemmer, Programme, die den Stamm eines Wortes suchen, und Lemmatisierer, die das Lemma für jedes Wort suchen. Manche Stemmer trennen auch produktive Derivationssuffixe ab, und geben in vielen Fällen nicht den Stamm, sondern den Wurzel eines Wortes. Es wird oft davon ausgegangen, dass dies für Information Retrieval von Vorteil ist. Wenn man beispielsweise nach analysieren sucht, möchte man wahrscheinlich nicht nur Ergebnisse mit analysiere, analysiert , analysierende, usw. haben, sondern vermutlich auch welche, in denen nur das Wort Analyse vorkommt. Man kann aber genau so Negativbeispiele finden. Ob diese Art von Stemming wirklich nützlich ist für Information Retrieval, ist nicht eindeutig gekärt (vgl. z.B.: BRANTS, Thorsten. Natural Language Processing in Information Retrieval. In: CLIN. 2003.).

Ein guter Lemmatizer, der im NLTK enthalten ist, ist der WordNet-Stemmer, der die Vollformen einfach im online-Wörterbuch WordNet nachschlägt. Da ein Wort im Englischen oft zu mehreren Klassen gehören kann, braucht der Wordnet-Lemmatizer auch die Wortklasse. Wir brauchen jetzt ein paar Zeilen Code, um die Penn Treebank Tags in Wordnet-Klassen zu übersetzen:

In [5]:
from nltk.corpus import wordnet as wn
lemmatizer = nltk.WordNetLemmatizer()

def wntag(pttag):
    if pttag in ['JJ', 'JJR', 'JJS']:
        return wn.ADJ
    elif pttag in ['NN', 'NNS', 'NNP', 'NNPS']:
        return wn.NOUN
    elif pttag in ['RB', 'RBR', 'RBS']:
        return wn.ADV
    elif pttag in ['VB', 'VBD', 'VBG', 'VBN', 'VBP', 'VBZ']:
        return wn.VERB
    return None

def lemmatize(lemmatizer,word,pos):
    if pos == None:
        return word
    else:
        return lemmatizer.lemmatize(word,pos)

lemmata = [lemmatize(lemmatizer,word,wntag(pos)) for (word,pos) in tags]
print(lemmata)
['Hanover', 'or', 'Hannover', '(', '/ˈhænoʊvər/', ';', 'German', ':', 'Hannover', ',', 'pronounce', '[', 'haˈnoːfɐ', ']', '(', 'About', 'this', 'sound', 'listen', ')', ')', ',', 'on', 'the', 'River', 'Leine', ',', 'be', 'the', 'capital', 'and', 'large', 'city', 'of', 'the', 'German', 'state', 'of', 'Lower', 'Saxony', '(', 'Niedersachsen', ')', ',', 'and', 'be', 'once', 'by', 'personal', 'union', 'the', 'family', 'seat', 'of', 'the', 'Hanoverian', 'Kings', 'of', 'the', 'United', 'Kingdom', 'of', 'Great', 'Britain', 'and', 'Ireland', ',', 'under', 'their', 'title', 'as', 'the', 'duke', 'of', 'Brunswick-Lüneburg', '(', 'later', 'describe', 'as', 'the', 'Elector', 'of', 'Hanover', ')', '.']

Etwas einfacher ist der sogenannt Porter Stemmer. Der Porterstemmer benutzt kein Wörterbuch sondern hat nur eine Liste von Suffixen, die abgetrennt oder ersetzt werden. Dies führt in vielen Fällen zu unsinnigen Ergebnisse. Oft ist das aber unproblematisch, so lange verschiedene Formen eines Wortes auf dem gleichen eindeutigen Stamm zurückgeführt werden. Neben dem Porter Stemmer enthält NLTK den Lancaster Stemmer, der nach dem gleichen Prinzip arbeitet. Schauen Sie sich die Ergebnisse genau an und vergleichen die Sie die STämme mit den Lemmata des Wordnet-Stemmers!

In [6]:
porter = nltk.PorterStemmer()
lancaster = nltk.LancasterStemmer()

print("Porter Stemmer:")
stems = [porter.stem(t) for t in tokenized_text[0]]
print(stems)

print("\nLancaster Stemmer:")
stems = [lancaster.stem(t) for t in tokenized_text[0]]
print(stems)
Porter Stemmer:
['hanov', 'or', 'hannov', '(', '/ˈhænoʊvər/', ';', 'german', ':', 'hannov', ',', 'pronounc', '[', 'haˈnoːfɐ', ']', '(', 'about', 'thi', 'sound', 'listen', ')', ')', ',', 'on', 'the', 'river', 'lein', ',', 'is', 'the', 'capit', 'and', 'largest', 'citi', 'of', 'the', 'german', 'state', 'of', 'lower', 'saxoni', '(', 'niedersachsen', ')', ',', 'and', 'wa', 'onc', 'by', 'person', 'union', 'the', 'famili', 'seat', 'of', 'the', 'hanoverian', 'king', 'of', 'the', 'unit', 'kingdom', 'of', 'great', 'britain', 'and', 'ireland', ',', 'under', 'their', 'titl', 'as', 'the', 'duke', 'of', 'brunswick-lüneburg', '(', 'later', 'describ', 'as', 'the', 'elector', 'of', 'hanov', ')', '.']

Lancaster Stemmer:
['hanov', 'or', 'hannov', '(', '/ˈhænoʊvər/', ';', 'germ', ':', 'hannov', ',', 'pronount', '[', 'haˈnoːfɐ', ']', '(', 'about', 'thi', 'sound', 'list', ')', ')', ',', 'on', 'the', 'riv', 'lein', ',', 'is', 'the', 'capit', 'and', 'largest', 'city', 'of', 'the', 'germ', 'stat', 'of', 'low', 'saxony', '(', 'niedersachs', ')', ',', 'and', 'was', 'ont', 'by', 'person', 'un', 'the', 'famy', 'seat', 'of', 'the', 'hanov', 'king', 'of', 'the', 'unit', 'kingdom', 'of', 'gre', 'britain', 'and', 'ireland', ',', 'und', 'their', 'titl', 'as', 'the', 'duk', 'of', 'brunswick-lüneburg', '(', 'lat', 'describ', 'as', 'the', 'elect', 'of', 'hanov', ')', '.']

Deutsch

Deutsch und Englsich sind Sprachen, die sich in vielen Hinsichten zielich ähnlich sind. Die Verarbeitungsschritte für einen Deutschen Text unerscheiden sich daher nicht wesentlich von denen für englische Texte. Bei der Tokenisierung und Satzerkennung müssen wir lediglich Deutsch als Parameter angeben, damit besonderheiten des Deutschen besser berücksichtigt werden.

Zum Ausprobieren, lesen wir wieder einen kurzen Text ein. Den hier genutzten Beispieltext finden Sie hier: http://textmining.wp.hs-hannover.de/texte/syrien.txt . Der Text ist der Wikipediaseite http://de.wikipedia.org/wiki/Syrien.html entnommen.

In [14]:
import nltk
import codecs

textfile = codecs.open("texte/Syrien.txt", "r", "utf-8")
text = textfile.read()
textfile.close()

sentences = nltk.sent_tokenize(text,language='german')

tokenized_sent = nltk.tokenize.word_tokenize(sentences[23],language='german')
print(tokenized_sent)
['Im', 'Westen', 'ist', 'das', 'Mittelmeer', 'die', 'Grenze', ',', 'im', 'Osten', 'die', 'Syrische', 'Wüste', 'und', 'der', 'Euphrat', '.']

Lemmatisierung und Wortarterkennung

Leider enthält das NLTK Paket keine Lemmatisierer und Wortarterkenner (POS Tagger) für das Deutsche.

Wir nutzen hier für beide Funtionen den Hanover Tagger (Siehe: Christian Wartena (2019). A Probabilistic Morphology Model for German Lemmatization. In: Proceedings of the 15th Conference on Natural Language Processing (KONVENS 2019): Long Papers. Pp. 40-49, Erlangen. )

Wir müssen den Hanover Tagger zunächst (einmalig) herunterladen und installieren:

In [ ]:
!pip install HanTa

Wir binden das Modul ein und laden ein vortrainiertes Modell:

In [10]:
from HanTa import HanoverTagger as ht

tagger = ht.HanoverTagger('morphmodel_ger.pgz')

Wir können jetzt Wörter oder Sätze analysieren. Die Funktion analyze() gibt ein Lemma und Wortart für eine Wortform:

In [12]:
print(tagger.analyze('Fachmärkte'))
('Fachmarkt', 'NN')

Mit der Funktion _tagsent() werden alle Wörter in einem Satz lemmatisiert und und getagt. Bei Mehrdeutigkeiten, wir die WOrtart gewählt, die im Kontext am wahrscheinlichsten ist. Wir versuchen das mal mit unserem Beispielsatz.

In [16]:
tags = tagger.tag_sent(tokenized_sent)
print(tags)
[('Im', 'im', 'APPRART'), ('Westen', 'Westen', 'NN'), ('ist', 'sein', 'VAFIN'), ('das', 'das', 'ART'), ('Mittelmeer', 'Mittelmeer', 'NN'), ('die', 'die', 'ART'), ('Grenze', 'Grenze', 'NN'), (',', '--', '$,'), ('im', 'im', 'APPRART'), ('Osten', 'Osten', 'NN'), ('die', 'die', 'ART'), ('Syrische', 'syrisch', 'ADJA'), ('Wüste', 'Wüste', 'NN'), ('und', 'und', 'KON'), ('der', 'der', 'ART'), ('Euphrat', 'Euphrat', 'NN'), ('.', '--', '$.')]

Weitere Möglichkeiten des Hanover Taggers werden hier beschrieben.

Unser Text ist nicht sehr lang, aber zum Schluss schreiben wir doch noch mal etwas mehr Code und machen noch mal eine nette Statistik:

In [19]:
from pprint import pprint

nouns = [] 
sentences_tok = [nltk.tokenize.word_tokenize(sent) for sent in sentences]
for sent in sentences_tok:
    tags = tagger.tag_sent(sent) 
    nouns_from_sent = [lemma for (word,lemma,pos) in tags if pos == "NN" or pos == "NE"]
    nouns.extend(nouns_from_sent)

fdist = nltk.FreqDist(nouns)    

pprint(fdist.most_common(10))
fdist.plot(50,cumulative=False)
[('Syrien', 165),
 ('Damaskus', 39),
 ('Jahr', 38),
 ('Land', 34),
 ('Aleppo', 28),
 ('Israel', 22),
 ('%', 21),
 ('Juni', 17),
 ('Präsident', 16),
 ('Libanon', 16)]

Schon bei diesem kurzen Text sehen wir eine Verteilung, die dem Zipfschen Gesetz entspricht!

Alternative: Treetagger

Der TreeTagger ist ein statistischer/trainierter Tagger und einer der beliebtesten Tagger für das Deutsche. Der TraTagger ist nicht in Python installiert, wodurch die Installation etwas umständlicher ist. Dafür ist die Analyse wesentlich schneller. Die Distribution enthällt trainierte Parameter für verschiedene Sprachen

Installation

  • Herunterladen von http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/
  • Zip-Datei einfach entpacken.
  • Am Besten in Program Files, im Benutzerverzeichnis oder auf der höchsten Ebene eines Laufwerks.
  • Wenn Sie mit Jupyter arbeiten evt. im Wurzelverzechnis Ihrer Notebooks.
  • Außer den TreeTagger müssen Sie ein Sprachmodell herunterladen. Die gibt es unter dem Link Parameter Files . Im TreeTagger-Ordner auf Ihrem Rechner legen Sie einen Unterodner lib an. In diesen Ordner kopieren Sie den Parameter File.
  • Wir nutzen http://treetaggerwrapper.readthedocs.org um den TreeTagger in Python nutzen zu können.
  • Treetaggerwrapper.py von Moodle einfach in das Arbeitsverzeichnis kopieren.

Wenn der TreeTagger nicht funktioniert, können Sie den TreeTagger mit dem Installationspfad aufrufen: tagger = treetaggerwrapper.TreeTagger(TAGLANG='de', TAGDIR='Pfad zum TreeTagger')

In [22]:
import treetaggerwrapper

tree_tagger = treetaggerwrapper.TreeTagger(TAGLANG='de')

tags = tree_tagger.tag_text(tokenized_sent,tagonly=True) #don't use the TreeTagger's tokenization!

pprint(tags)
['Im\tAPPRART\tin',
 'Westen\tNN\tWeste|Westen',
 'ist\tVAFIN\tsein',
 'das\tART\tdie',
 'Mittelmeer\tNN\tMittelmeer',
 'die\tART\tdie',
 'Grenze\tNN\tGrenze',
 ',\t$,\t,',
 'im\tAPPRART\tin',
 'Osten\tNN\tOsten',
 'die\tART\tdie',
 'Syrische\tADJA\tsyrisch',
 'Wüste\tNN\tWüste',
 'und\tKON\tund',
 'der\tART\tdie',
 'Euphrat\tNE\tEuphrat',
 '.\t$.\t.']

Wir haben jetzt die Lemmata und die Wortarten gefunden! Die Tags, die der TreeTagger für die Wortartenvergibt sind in den Parameter-Dateien festgelegt. In der Datei, die auf der Webseiite für das Deutsche zur Verfügung gestellt wird, sind das die Tags aus dem Stuttgart-Tübingen Tagset. Eine Beschreibung dieser Tags finden Sie hier: http://www.ims.uni-stuttgart.de/forschung/ressourcen/lexika/TagSets/stts-table.html

Das Ergebnis ist aber der direkte Ausgabe vom TreeTagger und nicht sehr pythonisch. Das kann aber leicht geändert werden:

In [24]:
tags2 = treetaggerwrapper.make_tags(tags)
pprint(tags2)
[Tag(word='Im', pos='APPRART', lemma='in'),
 Tag(word='Westen', pos='NN', lemma='Weste|Westen'),
 Tag(word='ist', pos='VAFIN', lemma='sein'),
 Tag(word='das', pos='ART', lemma='die'),
 Tag(word='Mittelmeer', pos='NN', lemma='Mittelmeer'),
 Tag(word='die', pos='ART', lemma='die'),
 Tag(word='Grenze', pos='NN', lemma='Grenze'),
 Tag(word=',', pos='$,', lemma=','),
 Tag(word='im', pos='APPRART', lemma='in'),
 Tag(word='Osten', pos='NN', lemma='Osten'),
 Tag(word='die', pos='ART', lemma='die'),
 Tag(word='Syrische', pos='ADJA', lemma='syrisch'),
 Tag(word='Wüste', pos='NN', lemma='Wüste'),
 Tag(word='und', pos='KON', lemma='und'),
 Tag(word='der', pos='ART', lemma='die'),
 Tag(word='Euphrat', pos='NE', lemma='Euphrat'),
 Tag(word='.', pos='$.', lemma='.')]