Seite 1 von 2

Verfasst: Mittwoch 24. Dezember 2008, 02:40
von cofi
Hm natürlich ... es ging ja um genestete Dicts. :( Entschuldige hab ich übersehen ;)

Verfasst: Mittwoch 24. Dezember 2008, 05:06
von HerrHagen
Hallo!

Hier mal eine einfache Lösung. Die Verwendung der default_dicts find ich ja doch eher kryptisch...
Du müsstest noch das Lesen des strings duch das Lesen der Datei ersetzen. Aber da weißt du ja wie das geht.

Code: Alles auswählen

dat = """The AT
Fulton NP
County NN
an AT
an AT
an AT
an NN
an IN
investigation NN
investigation XXX
investigation NN
of IN
Atlanta's NP$
recent JJ
primary NN
election NN
produced VBD """

lines = dat.splitlines()  # ersetzen durch Lesen aus Datei
lines = map(str.split, lines)

lexicon = {}
for word, tag in lines:
    if not lexicon.has_key(word):
        lexicon[word] = {tag:1}
    else:
        try:
            lexicon[word][tag] += 1
        except:
            lexicon[word][tag] = 1
lexicon hat am Ende folgenden Inhalt:

Code: Alles auswählen

>>> lexicon
{'produced': {'VBD': 1}, 'of': {'IN': 1}, "Atlanta's": {'NP$': 1}, 'primary': {'NN': 1}, 'an': {'AT': 3, 'NN': 1, 'IN': 1}, 'County': {'NN': 1}, 'investigation': {'XXX': 1, 'NN': 2}, 'election': {'NN': 1}, 'The': {'AT': 1}, 'Fulton': {'NP': 1}, 'recent': {'JJ': 1}}

>>> for a, b in zip(lexicon.keys(), lexicon.values()):
        print a, b

        
produced {'VBD': 1}
of {'IN': 1}
Atlanta's {'NP$': 1}
primary {'NN': 1}
an {'AT': 3, 'NN': 1, 'IN': 1}
County {'NN': 1}
investigation {'XXX': 1, 'NN': 2}
election {'NN': 1}
The {'AT': 1}
Fulton {'NP': 1}
recent {'JJ': 1}
Ich hoffe das ist das was du dir vorgestellt hast.

Frohe Weihnachten
HerrHagen

Verfasst: Mittwoch 24. Dezember 2008, 10:13
von BlackJack
@HerrHagen: Also ich finde `defaultdict`\s weit weniger kryptisch als Deine Lösung, wo man sich erst einmal klar machen muss welcher Zweig wann zum Einsatz kommt, oder so eine lange Zeile mit zwei `get()`-Aufrufen.

Alternative zu ``lambda`` wäre `functools.partial()`:

Code: Alles auswählen

from __future__ import division, with_statement
from collections import defaultdict
from functools import partial
from pprint import pprint


def main():
    lexicon = defaultdict(partial(defaultdict, int))
    with open('test.txt') as lines:
        for line in lines:
            word, tag = line.rstrip().split()
            lexicon[word][tag] += 1
    
    for word, tags in sorted(lexicon.iteritems()):
        print '%s: %s' % (word, sorted(tags.iteritems()))


if __name__ == "__main__":
    main()
Ansonsten würde ich ``in`` statt `has_key()` verwenden und auf keinen Fall ein ``except`` ohne eine konkrete Ausnahme verwenden. Zeile 30 soll ja zum Beispiel nicht ausgeführt werden, wenn in Zeile 28 ein Fipptehler bei einem Namen existiert, oder wenn jemand bei der Programmausführung an einer ungünstigen Stelle Strg+C drückt.

Verfasst: Mittwoch 24. Dezember 2008, 11:54
von HerrHagen
Ansonsten würde ich ``in`` statt `has_key()` verwenden und auf keinen Fall ein ``except`` ohne eine konkrete Ausnahme verwenden. Zeile 30 soll ja zum Beispiel nicht ausgeführt werden, wenn in Zeile 28 ein Fipptehler bei einem Namen existiert, oder wenn jemand bei der Programmausführung an einer ungünstigen Stelle Strg+C drückt.
Da hast du natürlich recht, war einfach schon etwas spät in der Nacht...
@HerrHagen: Also ich finde `defaultdict`\s weit weniger kryptisch als Deine Lösung, wo man sich erst einmal klar machen muss welcher Zweig wann zum Einsatz kommt, oder so eine lange Zeile mit zwei `get()`-Aufrufen.
Da bin ich anderer Meinung. Deine Variante ist wirklich nett. Hat aber meiner Meinung nach ein Problem:

Code: Alles auswählen

from collections import defaultdict
from functools import partial
Man benötigt die Kenntnis der Funktionsweise von gleich zwei Standard-Modulen. Das ist ja nichts schlimmes, ist aber als Einsteigerlösung (nach der hier gefragt wurde) eher weniger zu gebrauchen.
Außerdem find ich die Verzweigung nicht so schlimm, da sich das ganze wie Pseudocode ließt. Wenn lexicon nicht word enthält, dann lege einen neuen Eintrag...
Ich finde, dass man bei der defaultdict Variante mehr nachdenken muss warum das ganze überhaupt funktioniert.

Warum importierst du pprint, division wenn du sie dann nicht benutzt?

MFG HerrHagen

Verfasst: Mittwoch 24. Dezember 2008, 12:19
von Leonidas
HerrHagen hat geschrieben:Da bin ich anderer Meinung. Deine Variante ist wirklich nett. Hat aber meiner Meinung nach ein Problem:

Code: Alles auswählen

from collections import defaultdict
from functools import partial
Man benötigt die Kenntnis der Funktionsweise von gleich zwei Standard-Modulen. Das ist ja nichts schlimmes, ist aber als Einsteigerlösung (nach der hier gefragt wurde) eher weniger zu gebrauchen.
Wieso? Eine Einsteigerlösung bedeutet ja nicht dass man alles auf umständliche Weise machen muss. Der Einsatz von ``partial`` ist da etwas Grenzwertig, da für Anfänger Funktionen höherer Ordnung etwas tricky sein können (außer sie haben Higher Order Perl gelesen, was es jetzt kostenlos online gibt), aber das ``defaultdict`` macht den Code ausreichend simpler dass es sich lohnt einfach mal in die Doku zu schauen was es tut. Einsteigercode ist ja auch für den Einstieg gedacht aber das heißt ja nicht dass man an dem Code nicht wachsen kann.
HerrHagen hat geschrieben:Warum importierst du pprint, division wenn du sie dann nicht benutzt?
Einige Leute haben halt Templates für neue Python-Dateien und da kann es dann vorkommen, dass da ``pprint`` oder ``division`` automatisch importiert werden. Für den Quelltext ist es natürlich unnötig.

Verfasst: Mittwoch 24. Dezember 2008, 14:01
von BlackJack
Der `__future__`-Import ist bei mir Standard. `pprint` dagegen überflüssigerweise noch drin geblieben.

Verfasst: Donnerstag 25. Dezember 2008, 13:46
von Dingels
@ Herr Hagen

vielen Dank, das ist genau das, was ich gesucht habe. Eigentlich total simpel, aber man muss erst mal drauf kommen. Wer wie ich so lange in Perl programmiert hat, hat es mit Python erst mal etwas schwer. :lol:

Ich bedanke mich recht herzlich und wünsche euch noch schöne Feiertage. :)


Dingels

Verfasst: Donnerstag 25. Dezember 2008, 15:10
von Leonidas
Wobei der Code recht ineffizient ist, da dort erst die ganze Datei in den Speicher gelesen wird und dan gesplittet - würde man vermeiden wollen wenn es auch anders geht. Und in diesem Beispiel ist das ganz einfach möglich, indem man über die Zeilen der Datei direkt iteriert.

Verfasst: Donnerstag 25. Dezember 2008, 16:32
von Dingels
@ Leonidas

Ich weiß, hab jetzt statt file() auch open() und close() verwendet. Der Vorteil dessen ist ja doch recht offensichtlich. :wink:

Verfasst: Donnerstag 25. Dezember 2008, 16:38
von Leonidas
Das meinte ich eigentlich nicht, sondern dass da ``splitlines()`` auf einem String ausgeführt wird was dann recht viel Zeit und Speicher beansprucht wenn man auch direkt über eine Datei iterieren kann.

Verfasst: Samstag 27. Dezember 2008, 00:06
von BlackJack
Und vielleicht noch der Hinweis, dass man sich in Python eigene Klassen schreiben könnte, wenn es viele Funktionen gibt, die auf der gleichen Art von verschachtelter Datenstruktur operieren.

Spätestens ab der dritten Verschachtelungsebene in der Datenstruktur ist das IMHO ein muss das aufzubrechen, damit der Quelltext verständlich und wartbar bleibt.

Verfasst: Samstag 27. Dezember 2008, 13:17
von HerrHagen
@Leonidas: Schon klar. Ich hab das ganze ja nur mit Strings gemacht damit es jeder sofort ausführen kann, da ja niemand die Beispieldatei hat.