Seite 1 von 2

häufigkeiten von strings

Verfasst: Samstag 17. März 2007, 20:52
von picknicker187
hi,

ich habe listen mit strings und würde gerne zählen wie oft jeder string in den listen vorkommt und am ende dann die string sortiert nach häufigkeit mit der entsprechenden anzahl ausgeben. also zb: liste: ('bla', 'bla', 'blub, 'hallo')
2:bla
1: blub
1:hallo
ich dachte ich könnte ein string beim ersten fund in ein dict schreiben und als value die 1 dazu, bei jedem weiteren vorkommen dann den value um 1 erhöhen und am schluss dann das dict nach values sortiert ausgeben. aber ich weiss leider nicht wie ich einen value um 1 erhöhen kann und irgendwie macht mir das ganze den eindruck als ob es dafür eine wesentlich einfachere lösung geben müsste. hat jenand eine idee?

gruß und danke,
michi

Verfasst: Samstag 17. März 2007, 21:18
von Leonidas
Das geht schon so wie du beschrieben hast:

Code: Alles auswählen

In [8]: words = ('bla', 'bla', 'blub', 'hallo')
In [9]: frequencies = dict()
In [10]: for word in words:
   ....:     frequencies[word] = frequencies.get(word, 0) + 1
   ....:
In [11]: frequencies
Out[11]: {'hallo': 1, 'bla': 2, 'blub': 1}
Wie du jetzt die Worte nach Häufigkeit sortierst, darfst du selbst rausfinden ;)

Verfasst: Samstag 17. März 2007, 21:22
von sape
Auf die schnelle zusammengehackt:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


def sort_elements_to_frequency(iterable, reverse=True):
    freq_dict = dict()
    order = list()
    for elem in iterable:
        try:
            freq_dict[elem] += 1
        except KeyError, error:
            freq_dict[elem] = 1
    
    for k, v in freq_dict.iteritems():
        order.append('%s\0%s' % (v, k))
    order.sort(reverse=reverse)
    return [x.replace('\0', ': ') for x in order]


if __name__ == '__main__':
    li = ['blubb', '43', 'foobar', 'barfoo', 'blubb', '43', 'foobar', 'barfoo',
         'blubb', '43', '43']

    print sort_elements_to_frequency(li)
Ist nicht die performanteste Lösung.

Verfasst: Samstag 17. März 2007, 22:07
von sape
Verbesserte und schnellerer Version.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


def sort_elements_to_frequency(iterable, reverse=True):
    freq_dict = dict()
    order = list()
    for elem in iterable:
        if elem in freq_dict:
            freq_dict[elem] += 1
        else:
            freq_dict[elem] = 1
    
    for k, v in freq_dict.iteritems():
        order.append('%s: %s' % (v, k))
    order.sort(reverse=reverse)
    return order
    
if __name__ == '__main__':
    li = ['blubb', '43', 'foobar', 'barfoo', 'blubb', '43', 'foobar', 'barfoo',
         'blubb', '43', '43']

    print sort_elements_to_frequency(li)
BTW: Der ``in`` test sollte schneller sein als ein try...except.

EDIT:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


def sort_elements_to_frequency(iterable, reverse=True):
    freq_dict = dict()
    order = list()
    for elem in iterable:
        if elem in freq_dict:
            freq_dict[elem] += 1
        else:
            freq_dict[elem] = 1
    
    order = [{v: k} for k, v, in freq_dict.iteritems()]
    order.sort(reverse=reverse)
    return order
    
if __name__ == '__main__':
    li = ['blubb', '43', 'foobar', 'barfoo', 'blubb', '43', 'foobar', 'barfoo',
         'blubb', '43', '43']

    print sort_elements_to_frequency(li)
[{4: '43'}, {3: 'blubb'}, {2: 'foobar'}, {2: 'barfoo'}]

Verfasst: Samstag 17. März 2007, 22:54
von gerold
Ich will auch ;-)

Code: Alles auswählen

>>> baselist = ["aal", "berta", "aal", "zoo", "zoo", "aal"]
>>> words = frozenset(baselist)
>>> words
frozenset(['aal', 'berta', 'zoo'])
>>> counted = {}
>>> for word in words:
...     counted[word] = baselist.count(word)
...     
>>> counted
{'aal': 3, 'berta': 1, 'zoo': 2}
>>> 

# Nachtrag:
>>> counted_list = counted.items()
>>> counted_list
[('aal', 3), ('berta', 1), ('zoo', 2)]
>>> import operator
>>> counted_list.sort(key = operator.itemgetter(1))
>>> counted_list
[('berta', 1), ('zoo', 2), ('aal', 3)]
>>>
mfg
Gerold
:-)

Verfasst: Samstag 17. März 2007, 23:05
von gerold
OK, ich fasse das mal zu einem Programm zusammen. Es wird aber sicher nicht so schnell sein wie das Beispiel von Leonidas, da er die Liste nur einmal durchläuft.

Code: Alles auswählen

import operator

baselist = ["aal", "berta", "aal", "zoo", "zoo", "aal"]
words = frozenset(baselist)

counted_words = []
for word in words:
    counted_words.append(
        (word, baselist.count(word))
    )
counted_words.sort(key = operator.itemgetter(1))

for item in counted_words:
    print "%-10s %i" % item
mfg
Gerold
:-)

Verfasst: Samstag 17. März 2007, 23:16
von gerold
Hier noch einer... damit der Gedanke nicht verloren geht und ich endlich mal ein Beispiel für itertools.groupby habe. :-)

Code: Alles auswählen

import operator
import itertools

baselist = ["aal", "berta", "aal", "zoo", "zoo", "aal"]
sorted_baselist = sorted(baselist)

counted_words = []
for word, items in itertools.groupby(sorted_baselist):
    counted_words.append((word, len(list(items))))

counted_words.sort(key = operator.itemgetter(1))

for item in counted_words:
    print "%-10s %i" % item
mfg
Gerold
:-)

PS: Falls jemand fragt... Ja, man kann alles komplizierter machen, als es sein müsste. :twisted:

Verfasst: Sonntag 18. März 2007, 00:15
von nkoehring
Na dann geb ich auch noch meine Senf dazu ^^

Code: Alles auswählen

>>> tmp
l = ["bla", "bla", "blub", "hallo"]

for i in l:
tmp = list()
for i in l:
    if (l.count(i), i) not in tmp: tmp.append((l.count(i), i))

for c, i in tmp:
    print str(c)+':', i

:roll: :roll: :roll:

Verfasst: Sonntag 18. März 2007, 12:01
von EyDu
Ja, bei so etwas kann man sich einfach nicht zuückhalten :D:

Code: Alles auswählen

data = ["a", "b", "c", "a", "a", "b", "c", "d", "b"]
r = [(data.count(x), x) for x in set(data)]
r.sort()
r = [(x,y) for y,x in r]
Da hätte ich einen ja fast noch intuitiven Einzeiler:D:

Code: Alles auswählen

data = ["a", "b", "c", "a", "a", "b", "c", "d", "b"]
r = [(x,y) for y,x in sorted([(data.count(x), x) for x in set(data)])]

Verfasst: Sonntag 18. März 2007, 13:51
von birkenfeld
@sape: dein erstes Beispiel im zweiten Post sortiert falsch.
@nkoehring, EyDu: euer Code hat AFAICS quadratische Laufzeit.

Leonidas' Lösung ist am geschicktesten IMO.

Verfasst: Sonntag 18. März 2007, 14:01
von EyDu
@birkenfeld:
Aber Leonidas Lösung sortiert nicht, das überlässt er dem Benutzer :-)

Mit der quadratischen Laufzeit sehe ich das bei mir etwas anders:
Bilden der Menge in: m = O(n*log n)
Sortieren: s = O(n * log n)
Und das Tauschen, was eigentlich nicht nötig ist: t = O(n)

Macht dann insgesamt (da sequentiell): m + s + t = O(n*log n)

Wir wollen ja nicht schätzen, sondern rechnen :wink:

EDIT: Da habe ich wohl das Zählen vergessen, dann ist es natürlich quadratische Laufzeit. :roll:

Verfasst: Sonntag 18. März 2007, 16:23
von sape
birkenfeld hat geschrieben:@sape: dein erstes Beispiel im zweiten Post sortiert falsch.
Warum :? Weil '2: foobar' vor '2: barfoo' kommt? Ich weiß auch nicht warum ``sort`` das nicht richtig macht:

Code: Alles auswählen

li = ['2: barfoo', '3: blubb', '2: foobar', '4: 43']
li.sort(reverse=True)
print li
Ansonsten sehe ich nicht wo falsch sortiert wird:
Zweiter Post erstes Beispiel:
['4: 43', '3: blubb', '2: foobar', '2: barfoo']

Zweiter Post zweites Beispiel:
[{4: '43'}, {3: 'blubb'}, {2: 'foobar'}, {2: 'barfoo'}]

Leonidas' Lösung ist am geschicktesten IMO.
Die Lösung sortiert aber nicht.

Verfasst: Sonntag 18. März 2007, 22:30
von Leonidas
sape hat geschrieben:
Leonidas' Lösung ist am geschicktesten IMO.
Die Lösung sortiert aber nicht.
Aber sie berechnet die Häufigkeit am klarsten - ohne Exceptions und stattdessen mit Defaultwerten. Den Weg habe ich mir mal von BlackJack abgeschaut.

Meine Lösung ist insofern genial, da sie dem OP auch etwas zu tun überlässt, so dass er auch etwas lernen kann. Ergo: Edukativ :P

Nun gut, wir könnten die Lösungen (für beide Probleme: Berechnen und Sortieren) auch später in einer Wikiseite zusammentragen, so wie es in [wiki]Anzahl der Es in einem String[/wiki] der Fall ist.

Verfasst: Sonntag 18. März 2007, 22:40
von EyDu
Leonidas hat geschrieben:Aber sie berechnet die Häufigkeit am klarsten - ohne Exceptions und stattdessen mit Defaultwerten
Seh' ich auch so, zum Zählen der Vorkommnisse scheint mir dies auch die eleganteste Variante, aber Ziel muss es ja sein, das gesamte Problem mit möglichst undurchsichtigen und einzeiligen List-Comprehensions zu lösen :D

Verfasst: Sonntag 18. März 2007, 22:47
von Leonidas
EyDu hat geschrieben:aber Ziel muss es ja sein, das gesamte Problem mit möglichst undurchsichtigen und einzeiligen List-Comprehensions zu lösen :D
Man könnte auch noch Threads rauspacken, so wie blackbird bei den Es.

Ich glaube wir haben den OP nun sehr effektiv vergrault indem wir aus seinem Thread einen Wettbewerb für Obfuscated Python gemacht haben 8) picknicker187, bist du noch dran?

Verfasst: Sonntag 18. März 2007, 22:56
von EyDu
Leonidas hat geschrieben:Man könnte auch noch Threads rauspacken, so wie blackbird bei den Es.
Nee, nee, so war meine Lösung nicht gemeint, sondern eher ein Vorschlag wie man es noch machen könnte. Und ich muss sagen, dieser Thread hat doch ganz brauchbare Variationen hervorgebracht, wie man an so ein Problem herangehen kann, auch wenn man sie eventuell als Einsteiger nicht sofort versteht.

Verfasst: Montag 19. März 2007, 00:07
von birkenfeld
sape hat geschrieben:
birkenfeld hat geschrieben:@sape: dein erstes Beispiel im zweiten Post sortiert falsch.
Warum :? Weil '2: foobar' vor '2: barfoo' kommt?
Nein, weil "10: barfoo" vor "1: barfoo" kommt.
Ansonsten sehe ich nicht wo falsch sortiert wird:
Zweiter Post erstes Beispiel:
['4: 43', '3: blubb', '2: foobar', '2: barfoo']

Zweiter Post zweites Beispiel:
[{4: '43'}, {3: 'blubb'}, {2: 'foobar'}, {2: 'barfoo'}]
Ja, mit einstelligen Zahlen funktioniert es schon. Mit mehrstelligen nicht.
Leonidas' Lösung ist am geschicktesten IMO.
Die Lösung sortiert aber nicht.
Ja, das ist trivial anzuhängen.

Verfasst: Montag 19. März 2007, 12:16
von Michael Schneider
birkenfeld hat geschrieben:
sape hat geschrieben:
birkenfeld hat geschrieben:@sape: dein erstes Beispiel im zweiten Post sortiert falsch.
Warum :? Weil '2: foobar' vor '2: barfoo' kommt?
Nein, weil "10: barfoo" vor "1: barfoo" kommt.
Hi,

das Problem kenne ich nur zu gut. Der Doppelpunkt hat den Characterwert 58, Zahlen aber kleinere. Dadurch werden kürzere Zahlen nach unten sortiert. Wenn es nur um die Ausgabe geht, kann man die Zahlen vorn mit Leerzeichen auffüllen, die stehen dann oben:

Code: Alles auswählen

iMax = max(frequencies)
l = ["%*s: %s" % (iMax, iAnzahl, sString) for sString, iAnzahl in frequencies.items()]
l.sort()
Dass foobar vor barfoo kommt ist logisch, wenn man rückwärts sortiert. :-)

Grüße,
Michel

Verfasst: Montag 19. März 2007, 14:07
von birkenfeld
Ja, man kann auch über China nach Amerika fliegen. Man kann aber auch einfach die Zahlen als Integers belassen und sortieren, anstatt sie vorher in Strings umzuwandeln.

Verfasst: Montag 19. März 2007, 19:58
von sape
Birkenfeld und Michael, danke für euren Hinweiß, das hatte ich nicht beachtet.

EDIT: Hab nochmal meine zweite variante (im zweiten post) getestet und die scheint zu gehen :D

Zweite vom zweiten Post:
[{10: 'barfoo'}, {4: '43'}, {3: 'blubb'}, {1: 'foobar'}]