As compreensões da lista de Python (idealmente) podem fazer o equivalente a 'Count (*)… grupo por…' no SQL?
-
23-09-2019 - |
Pergunta
Acho que as compreensões da lista podem me dar isso, mas não tenho certeza: quaisquer soluções elegantes no Python (2.6) em geral para selecionar objetos exclusivos em uma lista e fornecer uma contagem?
(Eu defini um __eq__
Para definir a singularidade na minha definição de objeto).
Então, em RDBMS-Land, algo assim:
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;
Que dá:
COUNT(*) n
==========
3 1
1 2
Então, aqui está minha lista equivalente em Python:
[1,1,1,2]
E eu quero a mesma saída que a seleção SQL fornece acima.
EDIT: O exemplo que dei aqui foi simplificado, na verdade estou processando listas de instâncias de objetos definidas pelo usuário: apenas para completar o código extra que eu precisava para fazer com que tudo funcionasse:
import hashlib
def __hash__(self):
md5=hashlib.md5()
[md5.update(i) for i in self.my_list_of_stuff]
return int(md5.hexdigest(),16)
o __hash__
Método era necessário para obter o set
Conversão ao trabalho (optei pela ideia de compreensão de lista que funciona em 2.6 [apesar do fato de ter aprendido que envolva uma ineficiência (ver comentários) - meu conjunto de dados é pequeno o suficiente para isso não é um problema]). my_list_of_stuff
Acima está uma lista de (strings) na minha definição de objeto.
Solução
Lennart Regebro forneceu uma boa linha Isso faz o que você quer:
>>> values = [1,1,1,2]
>>> print [(x,values.count(x)) for x in set(values)]
[(1, 3), (2, 1)]
Como S.Lott menciona, um DefaultDict pode fazer a mesma coisa.
Outras dicas
>>> from collections import Counter
>>> Counter([1,1,1,2])
Counter({1: 3, 2: 1})
Contador Somente disponível em py3.1, herda do dict
.
Não facilmente facilmente como uma compreensão de lista.
from collections import defaultdict
def group_by( someList ):
counts = defaultdict(int)
for value in someList:
counts[value.aKey] += 1
return counts
Esta é uma solução muito pitônica. Mas não é uma compreensão da lista.
Você pode usar groupby
de itertools
módulo:
Faça um iterador que retorne chaves e grupos consecutivos do iterável. A chave é uma função calculando um valor de chave para cada elemento. Se não for especificado ou não for, os principais padrões para uma função de identidade e retornam o elemento inalterado. Geralmente, o iterável precisa já ser classificado na mesma função -chave.
>>> a = [1,1,1,2]
>>> [(len(list(v)), key) for (key, v) in itertools.groupby(sorted(a))]
[(3, 1), (1, 2)]
Eu assumiria que seu tempo de execução é pior que o dict
-Soluções baseadas em Silentghost ou S.Lott, já que ele precisa classificar a sequência de entrada, mas você deve chegar a si mesmo. É uma compreensão da lista, no entanto. Deve ser mais rápido que a solução de Adam Bernier, pois não precisa fazer varreduras lineares repetidas da sequência de entrada. Se necessário, o sorted
A chamada pode ser evitada classificando a sequência de entrada em linha.
Os seguintes trabalhos em Python 2.4 e deve Portanto, trabalhe no Python 2.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"