# [Python] Dictionary aus Textdatei erstellen



## Ließa (16. Januar 2018)

Hallo ihr Lieben,



ich bin neu in diesem Forum und habe so etwas noch nie gemacht. Ich studiere Linguistik und muss einen Grundkurs namens Programmieren absolvieren. In diesem Kurs ist es meine Aufgabe wöchentliche Assignments zu bewerkstelligen und letzendlich 70% der Aufgaben richtig zu beantworten. Dementsprechend ist mein Druck ziemlich hoch. Ich hoffe hier auf Hilfe von euch. Ihr, die, vermutlich viel besser als ich, sich in Python viel besser auskennt.

Das soll auch keinetfalls so rüberkommen, dass ich meine Aufgabe eintrage und euch machen lasse. Ich denke einfach nur, dass ich das in einfachen Worten besser verstehe, als in hochkomplexen Ausformulierungen.


Die Aufgabe lautetet wie folgt:

Schreibt eine Funktion, die die Textkonkordanz (Fundstellen aller Worter im Dokument) fur einen gegebenen Text berechnet. Als Argument bekommt die Funktion die entsprechende Textdatei und zuruckgeben sollte sie ein Dictionary, das als Schlussel ein Wort und als Wert eine Liste dessen Vorkommen enthalt. Die Vorkommen realisiert ihr am Besten als ein Tupel aus Zeilen- und Spaltenangabe, wobei die Spalten einfach aussagen, um das wievielte Wort in der Zeile es sich handelt.
Zur Veranschaulichung hier mal ein Beispiel, wie ein Teil des Dictionaries fur eine sehr kleine Textdatei aussehen konnte:
{..., 'day' : [(23,2)], 'not' : [(34,1),(67,4)], 'here' : [(32,4)], ...}
Dieses Dictionary wurde beinhalten, dass das Wort 'day' einmal vorkommt und das zweite Wort in Zeile 23 ist, das Wort 'not' zweimal vorkommt, und zwar in Zeile 34 als erstes Wort und in Zeile 67 als viertes Wort usw.
Eine Textdatei ('Alice in Wonderland'), die ihr verwenden konnt, haben wir euch beigefugt. Ihr konnt euch auch selbst eine raussuchen, wenn ihr wollt, aber beachtet, dass es nicht sowas wie unsere Tiger-Korpus-Datei sein sollte, wo immer nur ein Wort pro Zeile steht - sonst waren die Spaltenangaben sinnlos.
Tipp: Um die Konkordanz eines bestimmten Wortes zu erhalten, konnt ihr dann einfach nur das Dictionary an der Stelle dieses Wortes ausgeben. Wenn ihr z.B. nur die Vorkommen von dem Wort 'water' haben wollt, konntet ihr Folgendes schreiben: 
concordance = compute concordance('alice.txt') print concordance['water'] Euer Output sollte dann folgendermaen aussehen (so konnt ihr auch uberprufen, ob euer Programm korrekt funktioniert): [(582, 1), (1969, 4)]. Und noch ein weiterer Beispielaufruf: print concordance['tea'] Output: [(1677, 10), (1784, 4), (1834, 4), (1931, 4), (3110, 5)]


Meine Idee ist nun:
1. alle wörter aus dem text in eine liste überführen
2. Die liste bereinigen, denn doppelte wörter würden hier auch doppelt vorkommen
3. Für jedes wort aus der liste im text schauen, an welchen stellen es vor kommt und die koordinaten eintragen

Ich beginne mit:
text_file = open("filename.dat", "r")
lines = text_file.read().split(' ')
print lines
print len(lines)
text_file.close()

Aber so richtig weiter weiß ich leider nicht. Hat jemand von euch eine Idee?

Lieben Gruß, Ließa


----------



## Technipion (16. Januar 2018)

Hallo Ließa,
und herzlich Willkommen auf tutorials.de!


Ließa hat gesagt.:


> Das soll auch keinetfalls so rüberkommen, dass ich meine Aufgabe eintrage und euch machen lasse.


Da brauchst du dir keine Sorgen machen. Fertige Lösungen werden hier im Forum sowieso nicht präsentiert. Allerdings wirkst du ja auch so, als würdest du dich selbst dahinter klemmen. Von daher ist ein wenig Hilfe kein Problem!

Also direkt zum Problem: Du sollst eine Python-Funktion schreiben, die für eine gegebene Eingabedatei ein Dictionary mit den Vorkommnissen der einzelnen Wörter - in der Form (Zeile, Wortnr.) - zurückgibt. Leider hast du keine Beispieldatei angehängt. Ich habe deshalb zu Vergleichszwecken folgende Datei mit einem Lorem Ipsum Generator erstellt:


Spoiler: Inhalt der Datei example.txt





```
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam
erat, sed diam voluptua. At vero eos et accusam et justo duo dolores
et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est
Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur
sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam
et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea
takimata sanctus est Lorem ipsum dolor sit amet.
```



Da du durchgehend das print-Statement benutzt (und nicht die print()-Funktion), gehe ich davon aus du hast bis jetzt nur in Python2 gearbeitet. Kurze Geschichte dazu: Python2 ist das "alte" Python (daher oft auch einfach nur Python genannt). Schon seit langer Zeit gibt es jedoch Python3, den Nachfolger. Python3 hat viele Vorteile gegenüber Python2, unter anderem ist es imho leichter zu lernen. Deshalb schlage ich dir vor, wir machen von hier an in Python3 weiter.


Ließa hat gesagt.:


> Meine Idee ist nun:
> 1. alle wörter aus dem text in eine liste überführen
> 2. Die liste bereinigen, denn doppelte wörter würden hier auch doppelt vorkommen
> 3. Für jedes wort aus der liste im text schauen, an welchen stellen es vor kommt und die koordinaten eintragen


Guter Ansatz, allerdings machst du dir das Leben damit unnötig schwer. Im ersten Schritt würdest du also alle Wörter durchgehen. Dann extra per Hand die Liste bereinigen. Und dann nochmal für jedes Wort in der Liste extra nachsuchen wo es sich überall befindet.
Hier kommt das schöne in Python: Es geht viel einfacher .

Ich gehe im Weiteren davon aus, dass du mit den Grundsätzen von Python vertraut bist (sowas wie Funktionen, Einrückungen, Listen, Dateien, etc.).

Erster Kniff: Du sollst ein Dictionary benutzen, in dem die "Positionen" von Wörtern als Tupel in einer Liste gespeichert sind. Also eine Liste von "Koordinaten". Also ein dict() aus Listen...
Ein Dictionary enthält in Python grundsätzlich erstmal nichts. Stell dir folgenden Fall vor:

```
some_dict = {'key1': ['something1', 'something2']}

some_dict['key1'].append('something3') # geht gut, key1 existiert bereits und hat eine Liste für uns parat

some_dict['key2'].append('something1') # ERROR: key2 ist noch nicht im Dict!

# Das ist vorerst nicht weiters schlimm, aber wir versuchen ja etwas an die Liste von key2 anzuhängen.
# Und diese Liste existiert schlicht noch nicht -> Fehler!
```
Extra für solche Fälle hält Python einen speziellen Dictionary-Typ zur Verfügung. Im Modul _collections_ befindet sich das _defaultdict_. Das ermöglicht dir folgendes:

```
from collections import defaultdict

some_dict = defaultdict(list) # Lege ein Dictionary an, das grundsätzlich leere Listen bereithält
some_dict['key1'] = ['something1', 'something2'] # trage die Liste für key1 ein

some_dict['key1'].append('something3') # geht gut, key1 existiert ja bereits und hat eine Liste für uns parat

some_dict['key2'].append('something1') # geht jetzt auch gut. Das defaultdict merkt, dass key2 noch nicht vorhanden ist, und erstellt für uns eine leere Liste, an die wir Dinge anhängen können.
```
Wenn du dir ein defaultdict(list) anlegst, kannst du also munter "Koordinaten" an die Listen anhängen.

Der nächste Schritt ist das Einlesen der Zeilen. Du hast das so gemacht:


Ließa hat gesagt.:


> text_file = open("filename.dat", "r")
> lines = text_file.read().split(' ')
> print lines
> print len(lines)
> text_file.close()


Dazu ein paar grundsätzliche Dinge: Zeilen einlesen kannst du auch mit _text_file.readlines()_, dann musst du dich nicht mit _.split()_ um das Zerhackstückeln kümmern. Außerdem: Du rufst _text_file.close()_ erst ganz am Schluss auf. Das geht zwar, allerdings ist das schlechter Stil. So hält dein Programm die Datei bis ganz zum Ende offen, d.h. der Computer muss "sich die Datei die ganze Zeit merken". Schöner wäre es, wenn du das close() nach dem read() aufrufen würdest. Danach brauchst du die Datei ja schließlich auch nicht mehr.

Jetzt kommt der nächste Trick: Du möchtest die Zeilen ablaufen. Allerdings wäre es wirklich schön, wenn du nicht wüsstest was in der aktuellen Zeile steht, sondern auch welche Nummer diese Zeile eigentlich hat. Auch dafür hält Python ein Kommando bereit; _enumerate()_:

```
for line_number, line_content in enumerate(lines):
    print('Line number', line_number, 'contains:', line_content)
    # line_number zählt mit, wo wir uns befinden
    # line_content enthält die eigentlichen Daten
```
Damit hast du im Prinzip schon die erste Koordinate deiner Wörter. Wenn du jetzt in _line_content_ ein bestimmtes Wort findest, dann ist seine Zeilennummer einfach _line_number + 1_ (da bei Computern immer ab 0 gezählt wird, ist die erste Zeile hier Zeile 0, die zweite Zeile dann Zeile 1, usw.).

Als nächstes musst du also innerhalb der Zeilen nach Wörtern suchen. Sagen wir du hast eine Zeile 'Wort1 Wort2 Wort3'. Wie kriegst du die einzelnen Wörter? Ganz einfach über _.split()_ ...
'Wort1 Wort2 Wort3'.split() liefert dir eine Liste ['Wort1', 'Wort2', 'Wort3']. Ein Problem gibt es hier noch: Was ist, wenn ich dir folgende Zeile gebe: 'Wort1, Wort2. Wort3'
Rufst du hier .split() auf, erhältst du ['Wort1,', 'Wort2.', 'Wort3']. Die Punkte und Kommas wollen wir aber nicht. Deshalb müssen wir sie zuerst entfernen. In Python kannst du Zeichen aus einem String entfernen, indem du sie mit "Nichts" ersetzt (also mit ''). Wir ersetzen also zunächst die Kommas, und danach die Punkte mit Nichts:
'Wort1, Wort2. Wort3'.replace(',', '') liefert 'Wort1 Wort2. Wort3'
'Wort1 Wort2. Wort3'.replace('.', '') liefert 'Wort1 Wort2 Wort3'

Oder in Kurzform: 'Wort1, Wort2. Wort3'.replace(',', '').replace('.', '') liefert 'Wort1 Wort2 Wort3'

Nachdem du Punkte und Kommas entfernt hast, kannst du mit .split() die Zeile in einzelne Wörter trennen:

```
line_content = line_content.replace(',', '').replace('.', '')
words = line_content.split()
```
Die Liste _words_ enthält jetzt alle Worte in deiner Zeile.

Es fehlt nicht mehr viel, jetzt musst du wieder mit enumerate() über die einzelnen Wörter in _words_ iterieren. Ich würde empfehlen, das aktuelle Wort via _word = word.lower()_ zu Kleinbuchstaben zu konvertieren. Sonst hast du später in deinem Dict Wörter mit Groß- und Kleinbuchstaben. Angenommen die aktuelle Zeilennummer ist i, und die aktuelle Wortnummer innerhalb der Zeile ist j. Dann kannst du die Koordinaten des aktuellen Wortes über

```
concordances_dict[word].append( (i+1, j+1) )
```
eintragen.

Ich weiß das ging jetzt alles ziemlich schnell. Aber lies es dir nochmal in Ruhe durch. Ich denke du schaffst das schon die einzelnen Stücke zusammen zu setzen. Falls du noch Probleme haben solltest, kannst du gerne nachfragen. Aber wie immer gilt: Wenn du es selbst herausfindest, macht es viel mehr Spaß 

Zur Probe: Hier ist das Dict für die oben angegebene Datei example.txt schön dargestellt, für den Fall dass du deine Ausgabe überprüfen möchtest.


Spoiler: Ausgabe für example.txt





```
elitr [(1, 8), (6, 2)]
tempor [(2, 3), (6, 7)]
dolores [(3, 13), (8, 4)]
ut [(2, 5), (6, 9)]
ea [(4, 2), (8, 6)]
ipsum [(1, 2), (5, 2), (5, 7), (9, 5)]
sadipscing [(1, 7), (6, 1)]
accusam [(3, 9), (7, 12)]
sea [(4, 9), (8, 13)]
at [(3, 5), (7, 8)]
lorem [(1, 1), (5, 1), (5, 6), (9, 4)]
amet [(1, 5), (5, 5), (5, 10), (9, 8)]
magna [(2, 9), (7, 2)]
vero [(3, 6), (7, 9)]
sit [(1, 4), (5, 4), (5, 9), (9, 7)]
erat [(3, 1), (7, 4)]
duo [(3, 12), (8, 3)]
labore [(2, 6), (6, 10)]
dolor [(1, 3), (5, 3), (5, 8), (9, 6)]
gubergren [(4, 7), (8, 11)]
sed [(1, 9), (3, 2), (6, 3), (7, 5)]
justo [(3, 11), (8, 2)]
consetetur [(1, 6), (5, 11)]
diam [(1, 10), (3, 3), (6, 4), (7, 6)]
clita [(4, 5), (8, 9)]
eirmod [(2, 2), (6, 6)]
takimata [(4, 10), (9, 1)]
no [(4, 8), (8, 12)]
stet [(4, 4), (8, 8)]
est [(4, 12), (9, 3)]
eos [(3, 7), (7, 10)]
nonumy [(2, 1), (6, 5)]
rebum [(4, 3), (8, 7)]
aliquyam [(2, 10), (7, 3)]
voluptua [(3, 4), (7, 7)]
et [(2, 7), (3, 8), (3, 10), (4, 1), (6, 11), (7, 11), (8, 1), (8, 5)]
invidunt [(2, 4), (6, 8)]
sanctus [(4, 11), (9, 2)]
dolore [(2, 8), (7, 1)]
kasd [(4, 6), (8, 10)]
```




Ich denke damit bist du jetzt erst mal beschäftigt 

Gruß Technipion


----------

