Existe um range()
equivalente para carros alegóricos em Python?
>>> range(0.5,5,1.5)
[0, 1, 2, 3, 4]
>>> range(0.5,5,0.5)
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
range(0.5,5,0.5)
ValueError: range() step argument must not be zero
range(5, 50, 5)
e, em seguida, basta dividir cada número por 10.Respostas:
Não conheço uma função interna, mas escrever uma como essa não deve ser muito complicado.Como os comentários mencionam, isso pode produzir resultados imprevisíveis, como:
Para obter o resultado esperado, você pode usar uma das outras respostas nesta pergunta ou, como @Tadhg mencionado, você pode usar
decimal.Decimal
comojump
argumento. Certifique-se de inicializá-lo com uma string em vez de um float.Ou até:
E depois:
fonte
>>> print list(frange(0,100,0.1))[-1]==100.0
seráFalse
frange
pode funcionar inesperadamente. Devido à maldição da aritmética de ponto flutuante , por exemplo,frange(0.0, 1.0, 0.1)
produz 11 valores, sendo o último valor0.9999999999999999
. Uma melhoria prática seria,while x + sys.float_info.epsilon < y:
embora mesmo isso possa provavelmente falhar com grandes números .decimal.Decimal
como etapa em vez de carros alegóricos.Você pode usar:
ou use lambda / map:
fonte
arange(0.5, 5, 1.5)
IMO é mais legível.list(frange(0, 1, 0.5))
, funcionará bem e 1 será excluído, mas se você tentarlist(frange(0, 1, 0.1))
, o último valor obtido será próximo de 1,0, o que provavelmente não é o que você deseja. As soluções apresentadas aqui não têm esse problema.Eu costumava usar,
numpy.arange
mas tive algumas complicações ao controlar o número de elementos que ele retorna, devido a erros de ponto flutuante. Então agora eu usolinspace
, por exemplo:fonte
decimal
, por exemplo:np.linspace(-.1,10,num=5050)[0]
np.linspace(-.1,10,num=5050)[0] == -.1
é verdadeiro. É só que osrepr(np.float64('-0.1'))
shows mostram mais dígitos.print(numpy.linspace(0, 3, 148)[49])
imprime0.9999999999999999
quando o resultado ideal seria1.0
.linspace
faz um trabalho muito melhor quearange
, mas não é garantido que produza o erro de arredondamento mínimo possível.O Pylab possui
frange
(um invólucro, na verdade, paramatplotlib.mlab.frange
):fonte
Ansiosamente avaliado (2.x
range
):Preguiçosamente avaliado (2.x
xrange
, 3.xrange
):Alternativamente:
fonte
(x * .5 for x in range(10))
como expressão geradora de avaliação preguiçosa?usando
itertools
: faixa de ponto flutuante avaliada preguiçosamente:fonte
itertools.takewhile
. No entanto,itertools.count(start, step)
sofre de erros acumulados de ponto flutuante. (Avalie,takewhile(lambda x: x < 100, count(0, 0.1))
por exemplo.) Emtakewhile(lambda x: x < stop, (start + i * step for i in count()))
vez disso, eu escreveria .Ajudei a adicionar a função numeric_range ao pacote more-itertools .
more_itertools.numeric_range(start, stop, step)
age como o intervalo de funções interno, mas pode lidar com tipos de flutuação, decimal e fração.fonte
Não existe essa função interna, mas você pode usar o seguinte (código Python 3) para fazer o trabalho tão seguro quanto o Python permitir.
Você pode verificar tudo executando algumas asserções:
Código disponível no GitHub
fonte
Por que não há implementação de intervalo de ponto flutuante na biblioteca padrão?
Como ficou claro por todas as postagens aqui, não há versão de ponto flutuante
range()
. Dito isso, a omissão faz sentido se considerarmos que arange()
função é frequentemente usada como um gerador de índice (e, claro, isso significa um acessador ). Portanto, quando chamamosrange(0,40)
, estamos dizendo que queremos 40 valores começando em 0, até 40, mas não inclusivos em 40.Quando consideramos que a geração de índice é tanto sobre o número de índices quanto sobre seus valores, o uso de uma implementação de float
range()
na biblioteca padrão faz menos sentido. Por exemplo, se chamarmos a funçãofrange(0, 10, 0.25)
, esperaríamos que 0 e 10 fossem incluídos, mas isso renderia um vetor com 41 valores.Assim, uma
frange()
função que depende de seu uso sempre exibirá um comportamento contra-intuitivo; possui valores demais percebidos da perspectiva da indexação ou não inclui um número que razoavelmente deve ser retornado da perspectiva matemática.O caso de uso matemático
Com isso dito, conforme discutido,
numpy.linspace()
executa a geração com a perspectiva matemática bem:O caso de uso de indexação
E para a perspectiva da indexação, escrevi uma abordagem um pouco diferente com algumas mágicas complicadas de string que nos permitem especificar o número de casas decimais.
Da mesma forma, também podemos usar a
round
função interna e especificar o número de casas decimais:Uma rápida comparação e desempenho
Obviamente, dada a discussão acima, essas funções têm um caso de uso bastante limitado. No entanto, aqui está uma comparação rápida:
Os resultados são idênticos para cada um:
E alguns horários:
Parece que o método de formatação de strings vence por um fio no meu sistema.
As limitações
E, finalmente, uma demonstração do ponto da discussão acima e uma última limitação:
Além disso, quando o
skip
parâmetro não é divisível pelostop
valor, pode haver uma lacuna de bocejo devido ao último problema:Existem maneiras de resolver esse problema, mas no final do dia, a melhor abordagem provavelmente seria usar o Numpy.
fonte
Uma solução sem dependências numpy etc foi fornecida pelo kichik, mas devido à aritmética do ponto flutuante , ela geralmente se comporta inesperadamente. Conforme observado por mim e pelo blubberdiblub , elementos adicionais se infiltram facilmente no resultado. Por exemplo
naive_frange(0.0, 1.0, 0.1)
, renderia0.999...
como seu último valor e, portanto, renderia 11 valores no total.Uma versão robusta é fornecida aqui:
Devido à multiplicação, os erros de arredondamento não se acumulam. O uso de
epsilon
cuida de um possível erro de arredondamento da multiplicação, mesmo que as questões possam, obviamente, aumentar nos extremos muito pequeno e muito grande. Agora, como esperado:E com números um pouco maiores:
O código também está disponível como GistHub Gist .
fonte
Uma versão mais simples sem biblioteca
Aw, heck - vou jogar em uma versão simples sem biblioteca. Sinta-se livre para melhorar [*]:
A idéia principal é que
nsteps
é o número de etapas para você começar do começo ao fim erange(nsteps)
sempre emite números inteiros, para que não haja perda de precisão. O passo final é mapear [0..nsteps] linearmente para [start..stop].editar
Se, como alancalvitti, você gostaria que a série tivesse uma representação racional exata, sempre poderá usar Frações :
[*] Em particular,
frange()
retorna uma lista, não um gerador. Mas bastava para as minhas necessidades.fonte
frange(0,1.1,0.1)
e ainda mais daqueles com uma opção comofrange(0,1.05,0.1)
Nota 1: Na discussão na seção de comentários aqui, "nunca use
numpy.arange()
(a própria documentação numpy recomenda isso). Use numpy.linspace conforme recomendado por wim ou uma das outras sugestões nesta resposta"Nota 2: Li a discussão em alguns comentários aqui, mas depois de voltar a esta pergunta pela terceira vez, sinto que essa informação deve ser colocada em uma posição mais legível.
fonte
Como escreveu Kichik , isso não deve ser muito complicado. No entanto, este código:
É inadequado devido ao efeito cumulativo de erros ao trabalhar com carros alegóricos. É por isso que você recebe algo como:
Enquanto o comportamento esperado seria:
Solução 1
O erro cumulativo pode simplesmente ser reduzido usando uma variável de índice. Aqui está o exemplo:
Este exemplo funciona conforme o esperado.
Solução 2
Nenhuma função aninhada. Apenas um tempo e uma variável de contador:
Essa função também funcionará bem, exceto nos casos em que você deseja a faixa invertida. Por exemplo:
A solução 1 neste caso funcionará conforme o esperado. Para fazer essa função funcionar nessas situações, você deve aplicar um hack, semelhante ao seguinte:
Com este hack, você pode usar estas funções com etapas negativas:
Solução 3
Você pode ir ainda mais longe com a biblioteca padrão simples e compor uma função de intervalo para a maioria dos tipos numéricos:
Este gerador é adaptado do livro Fluent Python (capítulo 14. Iteráveis, iteradores e geradores). Não funcionará com intervalos decrescentes. Você deve aplicar um hack, como na solução anterior.
Você pode usar este gerador da seguinte maneira, por exemplo:
E é claro que você pode usá-lo com float e int também.
Seja cuidadoso
Se você deseja usar essas funções com etapas negativas, adicione uma verificação do sinal de etapa, por exemplo:
A melhor opção aqui é aumentar
StopIteration
, se você deseja imitar arange
própria função.Intervalo de imitação
Se você deseja imitar a
range
interface da função, pode fornecer algumas verificações de argumento:Eu acho que você entendeu. Você pode usar qualquer uma dessas funções (exceto a primeira) e tudo o que precisa é da biblioteca padrão do python.
fonte
eu escrevi uma função que retorna uma tupla de um intervalo de números de ponto flutuante de precisão dupla sem casas decimais além dos centésimos. era simplesmente uma questão de analisar os valores do intervalo, como seqüências de caracteres, e dividir o excesso. Eu o uso para exibir intervalos para selecionar em uma interface do usuário. Espero que alguém ache útil.
fonte
Uso
Para arredondar cada etapa para N casas decimais
Código
Por que escolher esta resposta?
np.linspace
acerto-e-erro, podem ou não funcionar devido à dificuldade em escolher o número correto de divisões.np.linspace
realmente enfrenta incrementos decimais de 0,1, e a ordem das divisões na fórmula para converter o incremento em várias divisões pode resultar em código correto ou quebrado.np.arange
estão obsoletas.Em caso de dúvida, tente os quatro casos de teste acima.
fonte
Observe que a primeira letra do intervalo é maiúscula. Esse método de nomeação não é incentivado para funções no Python. Você pode alterar Range para algo como drange ou frange, se quiser. A função "Range" se comporta exatamente como você deseja. Você pode verificar o manual aqui [ http://reference.wolfram.com/language/ref/Range.html ].
fonte
Eu acho que existe uma resposta muito simples que realmente emula todos os recursos do range, mas para float e integer. Nesta solução, você apenas supõe que sua aproximação por padrão seja 1e-7 (ou a que você escolher) e poderá alterá-la quando chamar a função.
fonte
É claro que haverá alguns erros de arredondamento, portanto isso não é perfeito, mas é o que geralmente uso para aplicativos, que não exigem alta precisão. Se você quiser tornar isso mais preciso, adicione um argumento extra para especificar como lidar com erros de arredondamento. Talvez a passagem de uma função de arredondamento possa torná-lo extensível e permitir que o programador especifique como lidar com erros de arredondamento.
Se eu escrever:
Ele produzirá:
fonte
Existe um intervalo () equivalente para carros alegóricos em Python? NÃO Use isto:
fonte
f_range(0.01,0.02,0.001)
... Para os propósitos mais práticos, aarange
Numpy é uma solução simples, segura e rápida.Existem várias respostas aqui que não lidam com casos simples de borda, como etapa negativa, início errado, parada etc. Aqui está a versão que lida com muitos desses casos, dando corretamente o mesmo comportamento que o nativo
range()
:Observe que isso resultaria em erro na etapa = 0, como na versão nativa
range
. Uma diferença é que o intervalo nativo retorna um objeto indexável e reversível, enquanto o anterior não.Você pode jogar com esse código e testar casos aqui.
fonte