Qual é uma maneira eficiente de encontrar o elemento mais comum em uma lista Python?
Os itens da minha lista podem não ser laváveis, portanto, não é possível usar um dicionário. Também no caso de empates, o item com o índice mais baixo deve ser retornado. Exemplo:
>>> most_common(['duck', 'duck', 'goose'])
'duck'
>>> most_common(['goose', 'duck', 'duck', 'goose'])
'goose'
Respostas:
Com tantas soluções propostas, fico surpreso que ninguém tenha proposto o que eu consideraria óbvio (para elementos não-laváveis, mas comparáveis) - [
itertools.groupby
] [1].itertools
oferece funcionalidade rápida e reutilizável e permite delegar alguma lógica complicada a componentes de biblioteca padrão bem testados. Considere, por exemplo:Isso pode ser escrito de forma mais concisa, é claro, mas estou buscando a máxima clareza. As duas
print
declarações podem ser descomentadas para ver melhor o mecanismo em ação; por exemplo, com impressões não comentadas:emite:
Como você vê,
SL
é uma lista de pares, cada par é um item seguido pelo índice do item na lista original (para implementar a condição principal de que, se os itens "mais comuns" com a mesma contagem mais alta forem> 1, o resultado deverá ser seja o mais antigo).groupby
agrupa apenas pelo item (viaoperator.itemgetter
). A função auxiliar, chamada uma vez por agrupamento durante omax
cálculo, recebe e descompacta internamente um grupo - uma tupla com dois itens em(item, iterable)
que os itens do iterável também são tuplas de dois itens(item, original index)
[[os itens deSL
]].Em seguida, a função auxiliar usa um loop para determinar a contagem de entradas no iterável do grupo e o índice original mínimo; retorna esses itens como "chave de qualidade" combinada, com o sinal de índice mínimo alterado para que o
max
itens operação considere "melhor" os itens que ocorreram anteriormente na lista original.Este código poderia ser muito mais simples se se preocupasse um pouco menos com problemas de grande O no tempo e no espaço, por exemplo ...
mesma idéia básica, apenas expressa de maneira mais simples e compacta ... mas, infelizmente, um espaço auxiliar O (N) extra (para incorporar as iteráveis dos grupos às listas) e o tempo O (N ao quadrado) (para obter o
L.index
item de cada item) . Embora a otimização prematura seja a raiz de todos os males da programação, escolher deliberadamente uma abordagem O (N ao quadrado) quando uma O (N log N) disponível está apenas indo muito contra o grão da escalabilidade! -)Finalmente, para aqueles que preferem "oneliners" à clareza e desempenho, uma versão bônus de 1 liner com nomes adequadamente mutilados :-).
fonte
groupby
requer classificação primeiro (O (NlogN)); usar umCounter()
commost_common()
pode superar isso porque ele usa um heapq para encontrar o item de maior frequência (por apenas 1 item, é o tempo de O (N)). ComoCounter()
agora é fortemente otimizado (a contagem ocorre em um loop C), ela pode facilmente superar essa solução, mesmo para pequenas listas. Ele sopra fora da água para grandes listas.Um one-liner mais simples:
fonte
set(lst)
, toda a lista deve ser verificado de novo) ... Provavelmente rápido o suficiente para a maioria dos usos, embora ...set(lst)
porlst
e também funcionará com elementos não-laváveis; embora mais lento.list.count()
precisa percorrer a lista na íntegra e você faz isso para cada item único da lista. Isso faz desta uma solução O (NK) (O (N ^ 2) no pior caso). O uso deCounter()
apenas leva tempo O (N)!Tomando emprestado daqui , isso pode ser usado com o Python 2.7:
Funciona cerca de 4-6 vezes mais rápido que as soluções de Alex e é 50 vezes mais rápido que o one-liner proposto por newacct.
Para recuperar o elemento que ocorre primeiro na lista em caso de empate:
fonte
most_common
é classificado por contagem, não desordenado. Dito isto, não escolherá o primeiro elemento em caso de empate; Adicionei outra maneira de usar o contador que escolhe o primeiro elemento.O que você deseja é conhecido nas estatísticas como mode, e o Python, é claro, possui uma função interna para fazer exatamente isso por você:
Observe que, se não houver um "elemento mais comum", como os casos em que os dois primeiros estão empatados , isso aumentará
StatisticsError
, porque estatisticamente falando, não há modo nesse caso.fonte
set
e é plausívelO(n^3)
.Se eles não forem laváveis, você pode classificá-los e fazer um loop único sobre o resultado contando os itens (itens idênticos estarão próximos um do outro). Mas pode ser mais rápido torná-los laváveis e usar um ditado.
fonte
Counter()
soluçãoEsta é uma solução O (n).
(invertido é usado para garantir que ele retorne o item de índice mais baixo)
fonte
Classifique uma cópia da lista e encontre a execução mais longa. Você pode decorar a lista antes de classificá-la com o índice de cada elemento e, em seguida, escolher a execução que começa com o índice mais baixo em caso de empate.
fonte
Sem o requisito sobre o índice mais baixo, você pode usar
collections.Counter
para isso:fonte
Uma linha:
fonte
fonte
Solução simples de uma linha
Ele retornará o elemento mais frequente com sua frequência.
fonte
Você provavelmente não precisa mais disso, mas foi o que fiz para um problema semelhante. (Parece mais longo do que é por causa dos comentários.)
fonte
Com base na resposta de Luiz , mas satisfazendo a condição " em caso de empates, o item com o menor índice deve ser retornado ":
Exemplo:
fonte
Aqui:
Tenho a vaga sensação de que existe um método em algum lugar da biblioteca padrão que fornecerá a contagem de cada elemento, mas não consigo encontrá-lo.
fonte
Esta é a solução lenta óbvia (O (n ^ 2)) se nem a classificação nem o hash forem possíveis, mas a comparação de igualdade (
==
) estiver disponível:Porém, tornar seus itens passíveis de hash ou classificáveis (conforme recomendado por outras respostas) quase sempre tornaria mais rápido a localização do elemento mais comum se o tamanho da sua lista (n) fosse grande. O (n) em média com hash e O (n * log (n)) na pior das hipóteses para classificação.
fonte
fonte
Eu precisava fazer isso em um programa recente. Eu admito, não consegui entender a resposta de Alex, então foi assim que acabei.
Eu cronometrei com a solução de Alex e é cerca de 10 a 15% mais rápida para listas curtas, mas uma vez que você ultrapassa 100 elementos ou mais (testado até 200000), é cerca de 20% mais lento.
fonte
Olá, esta é uma solução muito simples com grandes O (n)
Onde numere o elemento da lista que se repete na maioria das vezes
fonte
fonte
fonte
fonte