2010-01-27 8 views
5

Ich denke, die Liste Comprehensions kann mir dies geben, aber ich bin mir nicht sicher: irgendwelche eleganten Lösungen in Python (2.6) im Allgemeinen für die Auswahl von einzigartigen Objekten in einer Liste und bietet eine Anzahl?Können Pythons Listenkomprehensionen (im Idealfall) das Äquivalent von 'count (*) ... group by ...' in SQL ausführen?

(Ich habe eine __eq__ definiert, um Eindeutigkeit für meine Objektdefinition zu definieren).

So in RDBMS-Land, etwa so:

CREATE TABLE x(n NUMBER(1)); 
INSERT INTO x VALUES(1); 
INSERT INTO x VALUES(1); 
INSERT INTO x VALUES(1); 
INSERT INTO x VALUES(2); 

SELECT COUNT(*), n FROM x 
GROUP BY n; 

Welche gibt:

COUNT(*) n 
========== 
3  1 
1  2 

, hier also meine gleichwertige Liste in Python:

[1,1,1,2] 

Und ich will die gleiche Ausgabe wie die SQL-SELECT gibt oben.

EDIT: Das Beispiel, das ich hier gab wurde vereinfacht, ich bin der Verarbeitung tatsächlich Listen von benutzerdefinierten Objekt-Instanzen: nur der Vollständigkeit halber füge ich den zusätzlichen Code, den ich brauchte die ganze Sache an die Arbeit:

import hashlib 

def __hash__(self): 
    md5=hashlib.md5() 
    [md5.update(i) for i in self.my_list_of_stuff] 
    return int(md5.hexdigest(),16) 

Die __hash__ Methode wurde benötigt, um die set Konvertierung zu funktionieren (Ich entschied mich für die Liste Verständnis Idee, die in 2.6 arbeitet [trotz der Tatsache, dass ich gelernt, dass eine Ineffizienz beinhaltet (siehe Kommentare) - mein Datensatz ist klein genug dafür ist kein Problem]). my_list_of_stuff oben ist eine Liste von (Strings) auf meiner Objektdefinition.

Antwort

11

Lennart Regebro provided a nice one-liner das tut, was Sie wollen:

>>> values = [1,1,1,2] 
>>> print [(x,values.count(x)) for x in set(values)] 
[(1, 3), (2, 1)] 

As S.Lott mentions, ein defaultdict kann das gleiche tun.

+0

nett ... Prost - funktioniert gut auf meiner 2.6 Installation. – monojohnny

+3

Die defaultdict-Lösung wird bei größeren Wertelisten viel besser funktionieren, da sie die Werte nur einmal wiederholt, anstatt einmal plus einmal für jeden eindeutigen Wert. –

+0

@Thomas: danke für das hinzufügen. – bernie

6

Nicht so leicht als Listenverständnis machbar.

from collections import defaultdict 
def group_by(someList): 
    counts = defaultdict(int) 
    for value in someList: 
     counts[value.aKey] += 1 
    return counts 

Dies ist eine sehr pythonische Lösung. Aber kein Listenverständnis.

+0

Dieser Weg funktioniert, auch wenn es überhaupt kein Listenverständnis ist. – Broam

+0

Sehr "awk" wie Lösung, wie es auch geht! Kleines Problem, wenn es auf meinem System läuft: ... zählt = defaultdict (int) Nameerror: global name 'defaultdict' ist nicht definiert ... (Python 2.6.2 (R262: 71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 Bit (Intel)] auf win32) – monojohnny

+2

@monojohnny: '>>> aus Sammlungen importieren defaultdict' – SilentGhost

11
>>> from collections import Counter 
>>> Counter([1,1,1,2]) 
Counter({1: 3, 2: 1}) 

Counter nur in py3.1, erbt von der dict.

+0

Warum der Downvote? – SilentGhost

4

Sie groupby vom itertools Modul verwenden können:

Make an iterator that returns consecutive keys and groups from the iterable. The key is a function computing a key value for each element. If not specified or is None, key defaults to an identity function and returns the element unchanged. Generally, the iterable needs to already be sorted on the same key function.

>>> a = [1,1,1,2] 
>>> [(len(list(v)), key) for (key, v) in itertools.groupby(sorted(a))] 
[(3, 1), (1, 2)] 

ich seine Laufzeit übernehmen würde, ist schlimmer als die dict -basierten Lösungen von SilentGhost oder S.Lott, da sie die Eingangssequenz zu sortieren hat , aber du solltest das selbst tun. Es ist jedoch ein Listenverständnis. Es sollte schneller sein als Adam Berniers Lösung, da es keine wiederholten linearen Scans der Eingabesequenz durchführen muss. Bei Bedarf kann der Aufruf sorted vermieden werden, indem die Eingangssequenz in-line sortiert wird.

+0

+1 für die Mühe, und ich stimme zu, dass entweder S.Lott oder SilentGhost Lösungen überlegen sind. – bernie

+0

Stimmen Sie nicht zu, bevor Sie sie profiliert haben;) –

+0

guten Punkt :-) Um zu klären, stimme ich zu, basierend auf der Tatsache, dass diese integrierte Lösungen sind; bereits getestet und debuggte für uns. – bernie

1

Die folgenden Funktionen in Python 2.4 und sollten daher in Python 2 arbeiten.6:

lst = [1,1,2,2,3,4,5,6,5] 
lst_tmp = [] 
lst_dups = [] 

for item in lst: 
    if item in lst_tmp: 
     lst_dups.append(item) 
    else: 
     lst_tmp.append(item) 

if len(lst_dups): 
    lst_dups = sorted(set(lst_dups)) 
    for item in lst_dups: 
     print str(lst.count(item)), "instances of", item 
else: 
    print "list is unique"