Python-Anfänger braucht Hilfe bei multiplen Datenstrukturen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Hm natürlich ... es ging ja um genestete Dicts. :( Entschuldige hab ich übersehen ;)
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

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
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.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

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
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Der `__future__`-Import ist bei mir Standard. `pprint` dagegen überflüssigerweise noch drin geblieben.
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

@ 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
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

@ Leonidas

Ich weiß, hab jetzt statt file() auch open() und close() verwendet. Der Vorteil dessen ist ja doch recht offensichtlich. :wink:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
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.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

@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.
Antworten