Bigramme

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.
desperation63

Hallo liebe Leute,

ich habe letztens einen Text über Text-Kategorisierung gelesen und habe überlegt das Ganze in Python 3.4 umzusetzen. Dabei wird ein Text in Bigramme aufgeteilt bzw. jedes einzelne Wort des Textes. Die einzelnen Bigramme sollen wie folgt aus sehen. Bei dem Wort "TEXT" sollen folgende Bigramme erscheinen: _T, TE, EX, XT, T_
Das soll dann mit allen Worten des Textes gemacht werden und das Vorkommen der einzelnen Bigramme soll gezählt werden.

Es soll also immer ein Bigramm mehr erstellt werden als das Wort Buchstaben besitzt. Leider fehlt mir der Ansatz wie ich diese Bigramme erstellen kann. Könnte mir da jemand helfen?

Vielen Danke im Voraus.

Viele Grüße,

desperation63
BlackJack

Da würden sich die `itertools` anbieten:

Code: Alles auswählen

from itertools import chain, tee


def iter_bigrams(word):
    it_a, it_b = tee(chain('_', word, '_'))
    next(it_b)
    return (a + b for a, b in zip(it_a, it_b))


def main():
    print(list(iter_bigrams('TEXT')))  # -> ['_T', 'TE', 'EX', 'XT', 'T_']



if __name__ == '__main__':
    main()
desperation63

Das sieht schonmal sehr gut aus, danke. Aber wie kann ich das Prinzip jetzt auf einen ganzen Text anwenden? Wenn ich deinen Code jetzt so übernehme, wie er ist und versuche das ganze statt mit einem Wort mit einem ganzen Text zu machen, dann klappt das leider nicht mehr so gut. Wäre super wenn du mir da noch helfen könntest.
BlackJack

@desperation63: Naja, denn Text halt auf Worte aufteilen und für jedes Wort dann die Funktion aufrufen.
desperation63

Sprich den Text mit der split()-Funktion bearbeiten das habe ich gemacht, aber wie rufe ich die FUnktion für jedes Wort auf? Das will grad nicht funktionieren.
BlackJack

@desperation63: Schon mal was von Schleifen gehört? ;-) Oder mit der `map()`-Funktion.

Code: Alles auswählen

import re
from itertools import chain, tee

WORD_RE = re.compile(r'\w+')


def iter_words(text):
    return (m.group(0) for m in WORD_RE.finditer(text))


def iter_bigrams(word):
    it_a, it_b = tee(chain('_', word.upper(), '_'))
    next(it_b)
    return (a + b for a, b in zip(it_a, it_b))


def iter_bigrams_from_text(text):
    return chain.from_iterable(map(iter_bigrams, iter_words(text)))


def main():
    print(list(iter_bigrams_from_text('Dies ist ein Text-Text.')))


if __name__ == '__main__':
    main()
nezzcarth
User
Beiträge: 1778
Registriert: Samstag 16. April 2011, 12:47

BlackJack hat geschrieben:Da würden sich die `itertools` anbieten:

Code: Alles auswählen

from itertools import chain, tee


def iter_bigrams(word):
    it_a, it_b = tee(chain('_', word, '_'))
    next(it_b)
    return (a + b for a, b in zip(it_a, it_b))
Eine Zwischenfrage:
Das ließe sich aber nicht so gut auf N-Gramme generalisieren, oder? Für die im Ausgangspost erwähnte Textklassifizierung wäre das allerdings ggf. praktisch (bei dem mir bekannten "klassischen" Ansatz, werden z.B. N-Gramm mit n=1..5 verwendet.)
BlackJack

@nezzcarth: Stimmt das lässt sich nicht so leicht generalisieren. Dafür könnte man dann zum Beispiel eine `collections.deque` mit einer maximalen Länge verwenden (ungetestet):

Code: Alles auswählen

def iter_ngrams(n, word):
    ngram = deque('_' * n, n)
    for character in chain(word.upper(), '_' * (n - 1)):
        ngram.append(character)
        yield ''.join(ngram)
nezzcarth
User
Beiträge: 1778
Registriert: Samstag 16. April 2011, 12:47

@BlackJack:
Danke sehr, die Lösung find' ich gut :) Ich hatte mit deinem Ansatz von oben ein wenig rumgespielt:

Code: Alles auswählen

def ngrams2(word, n=2, fill='_'):
    iterators = tee(chain(fill*n, word, fill*(n-1)), n)
    for i in range(len(iterators)):
        for iterator in iterators[i:]:
            next(iterator)
    return [''.join(i) for i in zip(*iterators)]
Aber ich denke, das ist nicht sonderlich hübsch.
desperation63

Das sieht super aus danke. Gibt es auch eine Möglichkeit, dass ich beispielsweise den Bigramme von zwei Texte dann miteinander vergleiche, um eine Differenz zwischen den Texten zu ermitteln, so funktioniert das nämlich bei dieser Text-Kategorisierung.
desperation63

Hat jemand eine Idee, wie ich die Bigramme durchzählen könnte und dann die Bigramme 2er verschiedener Texte vergleichen kann anhand ihrer Vorkommnisse?
BlackJack

@desperation63: Zählen könnte man mit `collections.Counter` machen:

Code: Alles auswählen

import re
from collections import Counter
from itertools import chain, tee

WORD_RE = re.compile(r'\w+')


def iter_words(text):
    return (m.group(0) for m in WORD_RE.finditer(text))


def iter_bigrams(word):
    it_a, it_b = tee(chain('_', word.upper(), '_'))
    next(it_b)
    return (a + b for a, b in zip(it_a, it_b))


def iter_bigrams_from_text(text):
    return chain.from_iterable(map(iter_bigrams, iter_words(text)))


def main():
    text_a = 'Dies ist ein Test-Text.'
    text_b = 'Dies ist ein anderer Test-Text.'
    histogram_a = Counter(iter_bigrams_from_text(text_a))
    histogram_b = Counter(iter_bigrams_from_text(text_b))
    print(histogram_a)
    print(histogram_b)
    print()
    histogram_a.subtract(histogram_b)
    print(histogram_a)


if __name__ == '__main__':
    main()
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Je nach Wortmodell sind beispielsweise Set-Operationen hilfreich bei Uebereinstimmung/Differenz.

Und fuer NLP sollte auch nltk nicht unerwaehnt bleiben (die haben z.B. auch Implementierungen fuer n-Gramme und Wortverteilungen).
desperation63

Danke für die Hilfestellungen jedoch stehe ich vor einem neuen Problem.

Ich habe nur 10 vordefinierte Kategorien und will eine neue Textdatei zu einer Kategorie machen und diese eine Kategorie dann mit den anderen 10 vergleichen. Dabei soll herausgefunden werden welche der 10 Kategorien die geringsten Abweichungen mit der einen neuen Kategorie hat(sprich welche Kategorie hat die meisten gemeinsamen Bigramme mit der neuen Kategorie).

Edit: Die Kategorien sind jeweils als Dictionaries vorhanden.

Hat jemand eine Idee wie ich das umsetzen kann?

Danke im Voraus und danke für die bisherige Hilfe. Ihr seid super


PS: @cofi. nltk möchte ich nicht verwenden, trotzdem danke für den hinweis.
desperation63

Hat keiner ne Idee? Ich komm grad leider nicht weiter.
desperation63

Hat wirklich niemand eine Idee, wie ich das eine Dictionary mit den anderen 10 abgleichen kann und dann ausgeben kann, mit welchem Dictionary das eine die meisten Übereinstimmungen hat?
BlackJack

@desperation63: Du musst Dir halt eine Methode ausdenken/auswählen die auf dieses Problem bezogen am besten den ”Abstand” zwischen zwei Dictionaries auf eine Zahl abbilden kann. Man könnte die beiden Dictionaries zum Beispiel ”voneinander abziehen”, die Werte als Vektor auffassen, und die Länge beziehungsweise den Betrag davon berechnen.
nezzcarth
User
Beiträge: 1778
Registriert: Samstag 16. April 2011, 12:47

@desparation63: Vielleicht wäre es einfacher, wenn du verraten könntest, um welchen Text, den du im Eingagspost erwähntest, es sich handelt. Willst du tatsächlich nur das Dictionary mit der größten Schnittmenge? Weshalb berechnest du dann Häufigkeiten?

Ein anderes Verfahren ist basiert bespielsweise darauf, dass man die N-Gramm-Häufigkeiten für jede Kategorie berechnet und in eine Rangfolge bringt. Vergleiche funktionieren dann darüber, dass man die Rangfolgenunterschiede aufsummiert und diejenige Kategorie mit der geringsten Abweichung wählt.
desperation63

@nezzcarth

Das mit den Häufigkeiten hab ich wieder rausgenommen. Der Text an sich ist eig erstmal egal. Ich hab da einfach einen kurzen Beispieltext genommen den ich hier rumfliegen hatte.

Ich habe jetzt erstmal die Schnittmengen von allen Profilen mit dem einen Vergleichsprofil gebildet. Wie gebe ich nun am besten das mit der größten Schnittmenge aus?


Dein Vorschlag hört sich interessant aber sehr kompliziert an. Wie könnte man sowas denn realisieren?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das Stichwort lautet Information Retrieval, in deinem Fall würde sich zunächst mal das Vector Space Model anbieten. Das lässt sich leicht und schnell implementieren. Gehe einfach mal die Wikipedia-Seite zu IR durch und arbeite dich ein wenig ein.
Das Leben ist wie ein Tennisball.
Antworten