Samstag, 22. Januar 2011

 

Ermittle die Top-10 der Worte in einem Text (POS1: 2A, 2C)

Aufgabe 1

Schreiben Sie eine Funktion hauf(string), welche die 10 häufigsten Worte im String string ermittelt und als Liste von Tupeln der Art (wort, anzahl) zurück liefert (Länge der Liste ist <= 10). Die Liste muss so sortiert sein, dass das Häufigste Wort ganz am Anfang ist. Haben Worte die selben Häufigkeiten, so haben diese alphabetisch sortiert zu sein.




Aufgabe 2

Schreiben Sie eine Funktion histogramm(liste, weite = 60), welche aus der übergebenen Liste ein Histogramm erzeugt und als String zurück liefert. Eine „Zeile“ soll so aussehen:
das            123 ************
Wobei für das Wort 14 Zeichen, die Anzahl 3 Zeichen vorgesehen werden müssen. Dahinter ist eine Anzahl Sternchen, die der Anzahl bezogen auf das Maximum (höchste Anzahl in der Liste) und der Weite entspricht (round(anzahl / max * weite)).


Aufgabe 3

Schreiben Sie eine Funktion main(), welche alle auf der Kommandozeile angegebenen Dateien öffnet und die Gesamthäufigkeiten ermittelt (also hauf() mit dem gesamten gelesenen Text aufruft) und mit den ermittelten Daten das Histogramm ausgibt.
Sind keine Dateien angegeben, so ist von stdin zu lesen (bis EOF).
Beispielaufruf mit den Testdaten vom /vorgabe-Verzeichnis:
hp@l111~/workspace/python$ python3 haufdiagramm.py testdaten.txt
der            89 ************************************************************
die            61 *****************************************
und            43 *****************************
Histogramm     40 ***************************
in             28 *******************
für            17 ***********
ist            17 ***********
den            16 ***********
das            15 **********
ein            14 *********

Mögliche Lösung

Ich werde die einzelnen Funktionen erklären:
import sys
Dieser Import wird für die Kommandozeilenargumente benötigt.

Folgende Funktion ermittelt die Wort-Häufigkeiten, sortiert das Ergebnis nach Häufigkeit und liefert die ersten 10 Elemente.
def hauf(string):
    """ermittelt die 10 häufigsten Worte in 'string' und liefert Liste mit 
    Tupel (wort, anzahl)"""
    dict = {}
    for wort in string.split():
split() ohne Parameter zerlegt einen String in eine Liste von "Worten", wobei ein "Wort" eine Folge von Zeichen ohne Leerzeichen, Tabulator und Zeilenumbruch ist. Mit anderen Worten Leerzeichen, Tabulatoren und Zeilenumbrüche sind die Trenner.
        wort = wort.rstrip('.,;?!")') # Satzzeichen entfernen
        wort = wort.lstrip('"(') # Hochkomma und Klammer dürfen vorne stehen
lstrip() und rstrip() entfernen alle Zeichen in Klammer llinks bzw. rechts. In unserem Fall alle Satzzeichen, die unmittelbar vor einem Wort bzw. nach einem Wort stehen dürfen.
Die beiden Zeilen könnte man natürlich auch zusammenfassen zu: wort = wort.rstrip('.,;?!")').lstrip('"(').
Die folgende erste Zeile prüft, ob der String auch tatsächlich ein Wort ist (nur Buchstaben).
        if wort.isalpha():
            if wort in dict:
                dict[wort] += 1
            else:
                dict[wort] = 1
Obige 4 letzte Zeilen prüfen zunächst, ob das Wort bereits im Dictionary enthalten ist. Wenn ja, dann wird die gespeicherte Anzahl erhöht, wenn nein, dann wird unter diesem Wort eine 1 gespeichert.
        l = list(dict.items())
Erzeugt eine Liste mit Tupeln (wort, anzahl) aus dem Dictionary.
        l.sort(key=lambda x: (-x[1], x[0]))
sort() sortiert eine Liste nach der natürlichen Reihenfolge. Mit dem Parameter key kann eine Funktion übergeben werden, anhand deren Returnwert sortiert werden soll. Die Funktion wird jeweils mit einem Listenelement aufgerufen. Man könnte also eine Funktion schreiben, die jeweils das zweite Element des Tupels (die Liste besteht in diesem Fall ja aus Tupeln der Art (wort, anzahl)) zurückliefert. Da absteigend sortiert werden soll, muss entweder der Wert mit -1 multipliziert werden (-[x1]) oder man muss beim Sortieren sort(reverse=True) aufrufen. Den Namen dieser Funktion übergibt man dann per key=sortfunktion.
Diese Funktion könnte so aussehen: def sortierfunktion(x):return (-x[1], x[0])
lambda x: definiert eine anonyme Funktion (Funktion ohne Namen) mit dem Parameter x, die gleich an Ort und Stelle definiert und übergeben werden kann.
    l = l[0:10]
    return l
Die letzten beiden Zeilen schneiden die ersten 10 Elemente der Liste aus und liefern sie zurück.
Das könnte man auch nur mit einer Anweisung machen: return l[:10].

def stars(num, max, width):
    """liefert String mit so vielen Sternen, wie num / max * width"""
    return "*" * round(num / max * width)
Dazu braucht man nicht allzu viel sagen.

Die folgende Funktion erzeugt das in Aufgabe 2 geforderte Histogramm.

Um einen passenden Maßstab für die geforderte Weite weite=60 zu finden, wird das Maximum der Werte ermittelt. Das muss natürlich das zweite Element des ersten Tupels der Liste sein, wenn überhaupt Tupel in der Liste sind.
def histogramm(liste, weite=60):
    """erzeugt Histogramm und liefert es zurück"""
    if len(liste) > 0:
        max = liste[0][1]
    ret = ""
    for wort, anzahl in liste:
Diese Schleife geht jedes Tupel der Liste durch, wobei die Tupel gleich in wort und anzahl "ausgepackt" wird.
        ret += "%-14s%3d %s\n" % (wort, anzahl, stars(anzahl, max, weite))
Hier wird jede Zeile an den String ret angehängt, wobei Stringformatierung angewendet wird.
    return ret

Die folgenden beiden Funktionen dient nur zum Testen mit fixen Strings und liefert einen String mit vorgegebenen Anzahlen von Worten.
def teststr():
    """liefert Testeingabe"""
    s = "Häufigkeit " * 50 + "das " * 123 + "die, " * 100 + "Haus " * 40
    s += "Maus " * 38 + "Koffer " * 34 + "Kilogramm " * 22 + "Punkt " * 18
    s += "Genau " * 16 + "Zehn " * 10
    s += "nichts " * 5 + "23 " * 50 + "ziemlich " * 3
    return s 

def test():
    """Testen..."""
    liste = hauf(teststr())
    print(histogramm(liste, 12.3))

Diese Funktion prüft, ob überhaupt Kommandozeilenargumente vorhanden sind. Gibt es welche, so werden alle Argumente als Dateien zum Lesen geöffnet und mit read() komplett gelesen und an den String eingabe angehängt.
Gibt es keine Dateinamen auf der Kommandozeile, so wird mittels eingabe = sys.stdin.read() von der Standardeingabe gelesen.
Die letzte Zeile ruft die beiden Hauptfunktionen auf und gibt das Ergebnis aus.
Es fehlt allerdings noch die Überprüfung, ob eine Datei überhaupt existiert bzw. geöffnet werden kann. Diesen Teil möge der Leser selber ergänzen.
def main():
    """öffnet Datei oder liest von stdin"""
    if len(sys.argv) > 1:
        eingabe = ""
        for arg in sys.argv[1:]:
            f = open(arg)
            eingabe += " " + f.read()
            f.close()
    else:
        eingabe = sys.stdin.read()
    print(histogramm(hauf(eingabe)))
    

Die folgenden Zeilen dienen zum Aufrufen der Funktion main() (bzw. test() zum Testen), sofern dieses Script als Programm aufgerufen wurde.
if __name__ == '__main__':
#    test()
    main()

Vollständiges Programm (ohne Header)

import sys

def hauf(string):
    """ermittelt die 10 häufigsten Worte in 'string' und liefert Liste mit 
    Tupel (wort, anzahl)"""
    dict = {}
    for wort in string.split():
        wort = wort.rstrip('.,;?!")') # Satzzeichen entfernen
        wort = wort.lstrip('"(') # Hochkomma und Klammer dürfen vorne stehen
        if wort.isalpha():
            if wort in dict:
                dict[wort] += 1
            else:
                dict[wort] = 1
    l = list(dict.items())
    l.sort(key=lambda x: (-x[1], x[0]))
    l = l[0:10]
    return l

def stars(num, max, width):
    """liefert String mit so vielen Sternen, wie num / max * width"""
    return "*" * round(num / max * width)

def histogramm(liste, weite=60):
    """erzeugt Histogramm und liefert es zurück"""
    if len(liste) > 0:
        max = liste[0][1]
    ret = ""
    for wort, anzahl in liste:
        ret += "%-14s%3d %s\n" % (wort, anzahl, stars(anzahl, max, weite))
    return ret

def teststr():
    """liefert Testeingabe"""
    s = "Häufigkeit " * 50 + "das " * 123 + "die, " * 100 + "Haus " * 40
    s += "Maus " * 38 + "Koffer " * 34 + "Kilogramm " * 22 + "Punkt " * 18
    s += "Genau " * 16 + "Zehn " * 10
    s += "nichts " * 5 + "23 " * 50 + "ziemlich " * 3
    return s 

def test():
    """Testen..."""
    liste = hauf(teststr())
    print(histogramm(liste, 12.3))

def main():
    """öffnet Datei oder liest von stdin"""
    if len(sys.argv) > 1:
        eingabe = ""
        for arg in sys.argv[1:]:
            f = open(arg)
            eingabe += " " + f.read()
            f.close()
    else:
        eingabe = sys.stdin.read()
    print(histogramm(hauf(eingabe)))
    
if __name__ == '__main__':
#    test()
    main()

Labels: , , ,


Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]





<< Startseite

This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]