import spacy
nlp = spacy.load('de')

"""
Explanation: Natural Language Processing
Die computergestützte Verarbeitung natürlicher Sprache ist alles andere als trivial. Die Grundlagen dafür wurden über Jahrzehnte vor allem von der Computerlinguistik geschaffen. Die Sprachverarbeitung findet an vielen Stellen ihren Einsatz. Es können etwas grammatische Strukturen identifiziert und analysiert werden. Ein ganz wichtiger Anwendungsbereich liegt in der semantischen Analyse: der Computer soll Text nicht nur als Aneinanderreihung von Zeichen sehen, sondern den Inhalt eines Textes "verstehen". Ein typischer Anwendungsfall davon ist etwa die Named Entity Recognition, wo es darum geht, z.B. Personen- oder Ortsnennungen in einem Text zu finden.
Ein weiterer Bereich ist die Textklassifizierung, wo wir schon in den Bereich des künstlichen Intelligenz kommen. Eine klassische Anwendung dafür ist etwa die Identifizierung von Spam, die Klassifizierung nach Testsorten oder die Sentiment Analysis, bei der versucht wird, die "Stimmung" eines Textes (z.B. eines Online-Kommentars) zu verstehen.
NLP ist ein relativ komplexer Bereich, in dem statistische und wahrscheinlichkeitstheoritsche Aspekte aber auch Wissensrepräsentationen eine große Rolle spielen. Es gibt eine Reihe von Bibliotheken, die über Jahre für diesen Bereich geschrieben wurden. Für Python gibt es beispielsweise 

das primär für Lernzwecke entstandende NLTK (Natural Language Toolkit) (http://www.nltk.org/)
TextBlob, das auf NLTK aufsetzt (https://textblob.readthedocs.io/en/dev/)
SpaCy Eine sehr schnelle und einfach verwendbare Library, die wir uns noch etwas genauer ansehen werden (https://spacy.io/)
gensim: Spezialisiert auf Topic Modelling und Textklassifikation (https://radimrehurek.com/gensim/)
Stanford CoreNLP ist eigentlich eine (sehr gutes) Java-Programm, für das es aber Python-Wrapper gibt (https://stanfordnlp.github.io/CoreNLP/index.html, https://stanfordnlp.github.io/CoreNLP/other-languages.html)

SpaCy
Spacy steht als externe Bibliothek zur Verfügung. Sie kann einfach via
~~~
pip install spacy 
~~~
installiert werden.
Für jede benötigte Sprache müssen dann noch sprachspezifische Dateien nach installiert werden. Für Deutsch geht das so:
~~~
python -m spacy download de
~~~
Eine Liste der unterstützten Sprachen finden Sie hier: https://spacy.io/models/
Sobald alles installiert ist, können wir erste Experimente starten.
Zuerst laden wir spacy und das (statistische) Modell für Deutsch:
End of explanation
"""


with open('../data/wassermann/der_mann_von_vierzig_jahren.txt') as fh:
    doc = nlp(fh.read())

"""
Explanation: Dann laden wir den schon bekannten Roman von Jakob Wassermann:
End of explanation
"""


for token in doc:
    print(token)

"""
Explanation: Beim Erzeugen des Spacy-Dokuments wurden bereits allerlei Aktionen durchgeführt. Beispielsweise wurde der Text tokeniziert, also in einzelne Tokens (Wörter, wenn man so will) zerlegt. Die passiert sprachspezifisch und ist daher deutlich besser als unsere bisherigen Versuche mit split().
End of explanation
"""


sum(1 for token in doc)

"""
Explanation: Wir können damit zum Beispiel die Zahl der Tokens zählen:
End of explanation
"""


# Gibt Token für Token mit POS-Daten aus. 
# Zum nächsten Token gelangen Sie durch Drücken der Enter-Taste
# Abbruch der Schleife durch Eingabe von 'exit' + Enter
for token in doc:
    print(('{}\n\tLemma: {}\n\tPOS: {}\n\tTAG: {}\n\tDEP: {}'
           '\n\tShape: {}\n\tis_alpha: {}\n\tis_stop: {}'.format(
        token.text, token.lemma_, token.pos_, token.tag_, token.dep_, 
               token.shape_, token.is_alpha, token.is_stop)))
    if input() == 'exit':
        break

"""
Explanation: Part-of-Speech-Tagging
spaCy speichert für jedes Token eine Reihe weitere Daten, die als Property angesprochen werden können:

den Text des Tokens
die lemmatisierte Form des Tokens
pos_: Part-of-speech-Tag (einfach)
ADJ - Adjektiv
ADP - Präposition
ADV - Adverb
AUX - Hilfsverb 
CONJ - Bindewort
DET - Pronomen
INTJ - Ausruf
NOUN - Nomen
NUM - Zahl
PART - Partikel
PRON - Pronomen
PROPN - Eigenname
PUNCT - Satzzeichen
SCONJ - Untergeordnete Konjunktion
SPACE - Leerzeichen
VERB - Verb
X - Nicht-Wort
tag_: Part-of-speech-Tag detailiert (Details hier: https://spacy.io/api/annotation#pos-tagging)
dep_: Syntaktische Abhängigkeit (Beziehung zwischen Tokens) Details hier: https://spacy.io/api/annotation#section-dependency-parsing
shape_: Wortform: jedes Zeichen des Tokens wird so repräsentiert:
X: Großbuchstabe
x: Kleinbuchstabe
d: Ziffer
...
is_alpha_: token is alpha character (d.h. ein normales Wort)
is_stop_: token ist Stopword
End of explanation
"""


# Zeige Orte (LOC) es gibt noch: ORG, PERS, MISC
for ent in doc.ents:
    if ent.text.strip(): #  for some reason we get a lot of empty String Tokens
        if ent.label_ == 'LOC':
            print(ent.text)

"""
Explanation: Named Entities
Named Entity Recognition versucht Entitätsnamen (Personen, Orte, Firmen, Datumsangaben, Telefonnummern, Buchtitel, usw.) im Text zu identifizieren. Der Erfolg solcher Bemühungen hängt stark davon ab, wie gut das jeweilige Modell zur Textsorte (aber z.B. auch zur jeweiligen Zeit passt). Da spaCy vor allem auf zeitgenössische Texte mit einem Fokus auf Online-Text spezialisiert ist, ist das Ergebnis nicht berauschend, zeigt aber die grundlegende Idee. Die Ergebnisse sind rein statistisch und lassen sich trainieren. Auf die Schnelle habe ich leider kein gut trainiertes Modell für etwas antiquiertes literarisches Deutsch gefunden. 
Mehr zu Named Entities in spaCy finden Sie hier: https://spacy.io/usage/linguistic-features#named-entities
End of explanation
"""


text = ('Anton Maier, geboren 1990, wohnt in Wien und mag Bücher. '
        'Seine Frau heißt Anna Huber und interessiert sich für Computer.'
        'Aufgewachsen ist er in Graz und sie in St. Pölten.')
doc = nlp(text)

# Use custom tags for named entities
TAG_MAP = {
    'LOC': ('<place>', '</place>'),
    'PER': ('<person>', '</person>')
}

# stores the single parts which a joinded to a string in the end
buf = []
last_end = 0 # end position of the last entity found
for ent in doc.ents:
    # we are only interested in the tags defined in TAG_MAP, so if ent.label_
    # has no key in TAG_MAP, the tag will be an empty string
    start_tag, end_tag = TAG_MAP.get(ent.label_, ('', ''))
    # extract substring from end pos of last entity to start off this entity
    buf.append(text[last_end:ent.start_char])
    # add tags and entity string
    buf.append(start_tag + ent.text + end_tag)
    last_end = ent.end_char
# Add the remaining text after the last substring    
buf.append(text[last_end:])
print(''.join(buf))

"""
Explanation: Da Entity-Objekte auch die Positionsangaben innerhalb des Textes bereitstellen, kann man Entities bei Bedarf ziemlich leicht im Text taggen. Ich verwende dazu einen übersichtlicheren String:
End of explanation
"""


