Seite 1 von 1

n-Gramme

Verfasst: Montag 19. März 2012, 22:58
von MarcelF6
Guten Abend miteinander

Ich habe die folgende Funktion, welche alle n-Gramme (0<n<4) eines gegebenen Wortes ausgeben sollte:

Code: Alles auswählen

def trigrams(text):
	unigrams, bigrams, trigrams = {}, {}, {}
	for i, c in enumerate(text):
		unigrams[c] = unigrams.get(c, 0) + 1
		bigrams[text[i:i+2]] = unigrams.get(c, 0)
		trigrams[text[i:i+3]] = unigrams.get(c, 0)
	trigrams.update(bigrams)
	trigrams.update(unigrams)
	return trigrams

print trigrams('Kralle')
Wenn ich wie hier "Kralle" angebe, erwarte ich folgende Ausgabe:
{'a': 1, 'all': 1, 'le': 1, 'e': 1, 'ral': 1, 'al': 1, 'l': 2, 'Kr': 1, 'r': 1, 'ra': 1, 'K': 1, 'Kra': 1, 'll': 1, 'lle': 1}
ABER: Mit meiner Funktion erhalte ich momentan noch 2 bei 'le', obwohl 'le' nur einmal vorhanden ist.
Auch bei anderen Worten ist es immer dieses eine Bigramm, das falsch ist.
Wo steckt hier bloss der Fehler? :S

Re: n-Gramme

Verfasst: Montag 19. März 2012, 23:13
von EyDu
Ohne es mir jetzt im Detail angeschaut zu haben, aber lass dir mal in der Schleife ``c``, ``text[i:i+2]`` und ``text[i:i+3]`` ausgeben. Dann würde ich über deren Länge nachdenken und warum diese Teilweise falsch ist.

Re: n-Gramme

Verfasst: Montag 19. März 2012, 23:28
von MarcelF6
Danke für die schnelle Antwort. Fehler bereits korrigiert - habe +1 geschrieben statt die 1 in die Klammer :P
Danke für den Hinweis :)

Re: n-Gramme

Verfasst: Montag 19. März 2012, 23:32
von MarcelF6
Oh nein, das war doch nicht der Fehler.
Hatte das hier korrigiert:

Code: Alles auswählen

unigrams[c] = unigrams.get(c, 1) 
Aber in der ursprünglichen Version war das korrekt.

Das Problem ist ja, dass wenn zwei gleiche Buchstaben auftauchen, dass dann das zweite eine Vorkommnis-Zahl 2 erhält etc.. Dieser Fehler wirkt sich dann natürlich aus...(hier: das "l").
Aber wie lässt sich das beheben?

Re: n-Gramme

Verfasst: Montag 19. März 2012, 23:40
von BlackJack
@MarcelF6: Lass mich mal raten: Du hast den Code für die Unigramme von irgendwo fertig her und jetzt die Aufgabe den um Bi- und Trigramme zu erweitern und versuchst jetzt durch wildes rumprobieren das Problem zu lösen…

Re: n-Gramme

Verfasst: Montag 19. März 2012, 23:40
von EyDu
Das Problem ist eigentlich, das du den völlig falschen Ansatzt wählst. Warum machst du nicht einfach drei Schleifen oder zwei in einander verschachtelte?

Re: n-Gramme

Verfasst: Dienstag 20. März 2012, 00:20
von MarcelF6
Ok, also mit den Schleifen sähe es so aus:

Code: Alles auswählen

def trigrams(text):
	unigrams, bigrams, trigrams = {}, {}, {}
	for i, c in enumerate(text):
		unigrams[text[i:i+1]] = unigrams.get(c, 0) + 1
        for i, c in enumerate(text):
		bigrams[text[i:i+2]] = bigrams.get(c, 0) + 1
	for i, c in enumerate(text):
		trigrams[text[i:i+3]] = trigrams.get(c, 0) + 1
	trigrams.update(bigrams)
	trigrams.update(unigrams)
	return trigrams
Den Fehler allerdings konnte ich immernoch nicht ausmerzen. Bei Bigrammen, die eigentlich mehrfach vorkommen, wird z.T. immernoch eine falsche Anzahl ausgegeben. Mit den Indizes stimmt es sicher...

PS: Nein, der Code (auch nicht Teile davon) stammt nicht von extern. Sonst würde er wahrscheinlich fehlerls funktionieren ;)

Re: n-Gramme

Verfasst: Dienstag 20. März 2012, 02:29
von bords0

Code: Alles auswählen

>>> "Kralle"[4:4 + 3]
'le'
Fehler jetzt klar?

Re: n-Gramme

Verfasst: Dienstag 20. März 2012, 10:34
von MarcelF6
:shock:
Das habe ich gar nicht bemerkt. Ja, nun ist klar, wo sich der Fehler befindet.
Für die Behebung habe ich nun einfach zusätzliche Abfragen eingefügt.Die Abfragen selbst stimmen so, also sie liefern immer den erwarteten Wert zurück. Was aber stimmt mit dem jeweiligen Inhalt nicht?
Wenn ich z.B. bei den Trigrammen nur noch zwei Buchstaben erwische, dann sollte die Anzahl unter Bigrammen um 1 erhöht werden (sofern dieselbe Folge dort nicht schon gezählt wurde).

So schauts momentan aus:

Code: Alles auswählen

def trigrams(text):
	unigrams, bigrams, trigrams = {}, {}, {}
	for i, c in enumerate(text):
		unigrams[text[i:i+1]] = unigrams.get(c, 0) + 1
        for i, c in enumerate(text):
		if (len(text[i:i+2])) == 2:
		    bigrams[text[i:i+2]] = bigrams.get(c, 0) + 1
		elif (len(text[i:i+2])) == 1:
		    bigrams[text[i:i+2]] = unigrams.get(c, 0) + 1
	for i, c in enumerate(text):
		if (len(text[i:i+3])) == 3:
		    trigrams[text[i:i+3]] = trigrams.get(c, 0) + 1
		elif (len(text[i:i+3])) == 2:
		    trigrams[text[i:i+3]] = bigrams.get(c, 0) + 1
		elif (len(text[i:i+3])) == 1:
		    trigrams[text[i:i+3]] = unigrams.get(c, 0) + 1
	trigrams.update(bigrams)
	trigrams.update(unigrams)
	return trigrams
Danke vielmals für die Hinweise!

Re: n-Gramme

Verfasst: Dienstag 20. März 2012, 12:45
von EyDu
Hättets du meinen ersten Hinweis auch nur ausprobiert, dann hättest du den Fehler auch selber gefunden. Nun sollte dir auch klar sein, warum es falsch ist jedes Mal bis zur Länge des des Worts zu iterieren.

Code: Alles auswählen

>>> import collections
>>> def ngram(word, n):
...     result = collections.defaultdict(int)
...     length = len(word)
...     for i in range(n):
...         for j in range(length-i):
...             c = word[j:j+i+1]
...             result[c] += 1
...     return result

Re: n-Gramme

Verfasst: Dienstag 20. März 2012, 14:44
von MarcelF6
Ah dein Code kann beliebige n-Gramme bearbeiten - Super! :)
Ich habe mich doch nochmal hingesetzt und den Code bearbeitet. Mittlerweilen funktioniert es genau so, wie es sollte :) Jupii :)
..aber auch dein n-Gram-Code hab ich gleich getestet (und gespeichert ;-) )

Re: n-Gramme

Verfasst: Dienstag 20. März 2012, 15:07
von pillmuncher
Oder so (ab Python 2.7):

Code: Alles auswählen

from collections import Counter

def ngrams(word, length):
    k = len(word)
    for i in xrange(1, length + 1):
        for j in xrange(k - i + 1):
            yield word[j:j + i]

print list(ngrams(word='Kralle', length=3))
print dict(Counter(ngrams(word='Kralle', length=3)))
Ergebnis:

Code: Alles auswählen

['K', 'r', 'a', 'l', 'l', 'e', 'Kr', 'ra', 'al', 'll', 'le', 'Kra', 'ral', 'all', 'lle']
{'a': 1, 'all': 1, 'le': 1, 'e': 1, 'll': 1, 'K': 1, 'l': 2, 'al': 1, 'Kr': 1, 'r': 1, 'ra': 1, 'Kra': 1, 'ral': 1, 'lle': 1}