n-Gramme

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.
Antworten
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

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
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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.
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Danke für die schnelle Antwort. Fehler bereits korrigiert - habe +1 geschrieben statt die 1 in die Klammer :P
Danke für den Hinweis :)
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

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?
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…
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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?
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

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 ;)
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Code: Alles auswählen

>>> "Kralle"[4:4 + 3]
'le'
Fehler jetzt klar?
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

: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!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

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 ;-) )
Benutzeravatar
pillmuncher
User
Beiträge: 1532
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

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}
In specifications, Murphy's Law supersedes Ohm's.
Antworten