Eu gostaria de identificar grupos de números contínuos em uma lista, para que:
myfunc([2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 20])
Retorna:
[(2,5), (12,17), 20]
E queria saber qual era a melhor maneira de fazer isso (principalmente se houver algo embutido no Python).
Edit: Nota Eu originalmente esqueci de mencionar que os números individuais devem ser retornados como números individuais, não intervalos.
python
list
range
continuous
mikemaccana
fonte
fonte
Respostas:
more_itertools.consecutive_groups
foi adicionado na versão 4.0.Demo
Código
Aplicando esta ferramenta, criamos uma função geradora que encontra intervalos de números consecutivos.
A implementação de origem emula uma receita clássica (conforme demonstrado por @Nadia Alramli).
Nota:
more_itertools
é um pacote de terceiros instalável viapip install more_itertools
.fonte
EDIT 2: Para responder ao novo requisito do OP
Resultado:
Você pode substituir xrange por intervalo ou qualquer outra classe personalizada.
Os documentos Python têm uma receita muito legal para isso:
Resultado:
Se você deseja obter exatamente a mesma saída, pode fazer o seguinte:
resultado:
EDITAR: O exemplo já está explicado na documentação, mas talvez eu deva explicá-lo mais:
Se os dados eram:
[2, 3, 4, 5, 12, 13, 14, 15, 16, 17]
Entãogroupby(enumerate(data), lambda (i,x):i-x)
é equivalente ao seguinte:A função lambda subtrai o índice do elemento do valor do elemento. Então, quando você aplica o lambda em cada item. Você obterá as seguintes chaves para groupby:
groupby agrupa elementos por um valor-chave igual, portanto, os primeiros 4 elementos serão agrupados e assim por diante.
Espero que isso o torne mais legível.
python 3
versão pode ser útil para iniciantesimporte as bibliotecas necessárias primeiro
fonte
lambda x:x[0]-x[1]
.[2,3,4,5] == xrange(2,6)
, nãoxrange(2,5)
. Pode valer a pena definir um novo tipo de dados de intervalo inclusivo.for key, group in groupby(enumerate(data), lambda i: i[0] - i[1]): group = list(map(itemgetter(1), group))
A solução "ingênua" que acho pelo menos um pouco legível.
fonte
print([i if i[0] != i[1] else i[0] for i in group(x)])
Supondo que sua lista esteja classificada:
fonte
[j - i for i, j in enumerate(lst)]
é inteligente :-)Aqui está algo que deve funcionar, sem nenhuma importação necessária:
fonte
Observe que o código usando
groupby
não funciona como fornecido no Python 3, então use isso.fonte
Isso não usa uma função padrão - apenas repete a entrada, mas deve funcionar:
Observe que isso requer que a entrada contenha apenas números positivos em ordem crescente. Você deve validar a entrada, mas este código foi omitido para maior clareza.
fonte
Aqui está a resposta que encontrei. Estou escrevendo o código para que outras pessoas entendam, por isso sou bastante prolixo com nomes de variáveis e comentários.
Primeiro, uma função auxiliar rápida:
E então o código real:
Exemplo de execução:
retorna:
fonte
>>> getranges([2, 12, 13])
Saídas:[[12, 13]]
. Isso foi intencional?Resultado:
fonte
Usar
groupby
ecount
deitertools
nos dá uma solução curta. A ideia é que, em sequência crescente, a diferença entre o índice e o valor permaneça a mesma.Para acompanhar o índice, podemos usar um itertools.count , o que torna o código mais limpo, pois usa
enumerate
:Alguns exemplos de saída:
fonte
Usando numpy + listas de compreensão:
Com a função numpy diff, entradas de vetor de entrada consequentes cuja diferença não é igual a um podem ser identificadas. O início e o fim do vetor de entrada precisam ser considerados.
Resultado:
Observação: a solicitação de que números individuais sejam tratados de maneira diferente (retornados como individuais, não como intervalos) foi omitida. Isso pode ser obtido por meio do pós-processamento dos resultados. Normalmente, isso tornará as coisas mais complexas sem obter nenhum benefício.
fonte
Uma solução curta que funciona sem importações adicionais. Ele aceita qualquer iterável, classifica entradas não classificadas e remove itens duplicados:
Exemplo:
Esta é a mesma solução de @dansalmo que eu achei incrível, embora um pouco difícil de ler e aplicar (já que não é fornecida como uma função).
Observe que ele pode ser facilmente modificado para cuspir intervalos abertos "tradicionais"
[start, end)
, por exemplo, alterando a instrução de retorno:Copiei esta resposta de outra pergunta que foi marcada como uma duplicata desta com a intenção de torná-la mais fácil de encontrar (depois de agora pesquisar novamente por este tópico, encontrando apenas a pergunta aqui no início e não ficando satisfeito com as respostas dado).
fonte
As versões de Mark Byers , Andrea Ambu , SilentGhost , Nadia Alramli e truppo são simples e rápidas. A versão 'truppo' me encorajou a escrever uma versão que retém o mesmo comportamento ágil enquanto lida com tamanhos de passo diferentes de 1 (e lista como elementos singletons que não estendem mais de 1 passo com um determinado tamanho de passo). É dado aqui .
fonte