Como usar um valor de etapa range () decimal?

744

Existe uma maneira de alternar entre 0 e 1 por 0,1?

Eu pensei que poderia fazê-lo da seguinte forma, mas falhou:

for i in range(0, 1, 0.1):
    print i

Em vez disso, diz que o argumento step não pode ser zero, o que eu não esperava.

Evan Fosmark
fonte
17
int (0.1) == 0, portanto, a etapa é zero. Pode ser inesperado, mas é zero. Você pode reafirmar sua pergunta para refletir o fato de que não esperava isso. Dizer "não é" é falso e enganoso.
S.Lott
3
Entre uma linha curta curta pode ser enrolada usando itertools.takewhilee itertools.count. drangePorém, não é melhor do que o desempenho.
Kos
1
É embaraçoso que o alcance do python não permita isso, dado o quão fácil é implementar um gerador que faça isso mesmo sem acumular erros de arredondamento. Heck, mesmo a seqferramenta GNU coreutils permite fazer seq 0 0.1 1sem erros de arredondamento!
Josch
3
@josch: sequsa o long doubletipo C internamente e está sujeito a erros de arredondamento. Por exemplo, na minha máquina, seq 0 0.1 1fornece 1como última saída (conforme o esperado), mas seq 1 0.1 2fornece 1.9como a última saída (ao invés do esperado 2).
Mark Dickinson
Por conveniência, @ sugestão de Kos pode ser implementado como itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))ou itertools.islice(itertools.count(0,0.1), 10)(depois de ter import itertools), embora eu não tenha testado que é mais eficiente
Anônimo

Respostas:

906

Em vez de usar uma etapa decimal diretamente, é muito mais seguro expressar isso em termos de quantos pontos você deseja. Caso contrário, o erro de arredondamento de ponto flutuante provavelmente fornecerá um resultado errado.

Você pode usar a função linspace da biblioteca NumPy (que não faz parte da biblioteca padrão, mas é relativamente fácil de obter). linspaceleva vários pontos para retornar e também permite especificar se deve ou não incluir o terminal correto:

>>> np.linspace(0,1,11)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ])
>>> np.linspace(0,1,10,endpoint=False)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9])

Se você realmente deseja usar um valor de etapa de ponto flutuante, pode, com numpy.arange.

>>> import numpy as np
>>> np.arange(0.0, 1.0, 0.1)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9])

Erro de arredondamento de ponto flutuante vai causar problemas, no entanto. Aqui está um caso simples em que o erro de arredondamento causa arangeuma matriz de comprimento 4, quando deve produzir apenas 3 números:

>>> numpy.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])
Andrew Jaffe
fonte
51
numpy é um componente tão onipresente do python que considero essa resposta a mais 'pitônica' de todas.
airstrike
21
@AndreTerra O problema é que o @ numpy @ é um pacote de terceiros e acrescenta muita sobrecarga em termos de gerenciamento de dependências, armazenamento (para o próprio pacote) etc. Dependendo do que o desenvolvedor está fazendo, pode ser impossível usar isto.
Rbaleksandar
Pardon me, mas eu não entendia o ponto flutuante arredondamento erro na última parte uma vez np.linspace(1.,1.3,4)e np.arange(1.,1.3,0.1)dar exatamente a mesma saída
deadcode
4
@deadcode O motivo é que np.arange está definido para produzir um intervalo [start,stop)(ou seja, excluindo stop), portanto, não se espera que 1.3 seja incluído na lista. Veja esta pergunta para saber por que ela ainda está incluída e o que fazer contra ela.
Dennis
5
Quanto um pacote é usado é indiscutivelmente nenhum indicador de se é "Pythonic".
Alex Hall
213

O range do Python () só pode fazer números inteiros, não pontos flutuantes. No seu caso específico, você pode usar uma compreensão de lista:

[x * 0.1 for x in range(0, 10)]

(Substitua a chamada para variar com essa expressão.)

Para o caso mais geral, você pode escrever uma função ou gerador personalizado.


fonte
36
Melhor ainda, você pode usar uma compreensão de gerador se estiver trabalhando com o Python 2.4+. (x * 0.1 for x in range(0, 10)).
JAB
42
Mesmo melhor, colocar x/10em vez de x * 0.1: D Nada de especial na verdade, mas alguns números em que haverá mais preciso, por exemplo, para 3*0.1que você começa 0.30000000000000004, enquanto que para 3/10 você começa 0.3:)
Bloke
4
3/10 me dá 0, não 0,3. 3 / 10,0 fornece 0,29999999999999999. Python 2.6.
19
@LarsWirzenius: no Python 2.2+, from __future__ import division; 3/10retorna 0,3. Esse comportamento é o padrão no Python 3.x.
Benjamin Hodgson
2
função rodada também podem ser usados lst = [redondo (x * 0.10,2) para x em faixa (0,10)]
MARCA
148

Com base no 'xrange ([start], stop [, step])' , você pode definir um gerador que aceita e produz qualquer tipo que você escolher (atenda aos tipos de suporte +e <):

>>> def drange(start, stop, step):
...     r = start
...     while r < stop:
...         yield r
...         r += step
...         
>>> i0=drange(0.0, 1.0, 0.1)
>>> ["%g" % x for x in i0]
['0', '0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1']
>>> 
gimel
fonte
45
Isso tem problemas de arredondamento. Por favor, olhe aqui: code.activestate.com/recipes/66472
Christian Oudard 12/12/2009
Eu o estenderia um pouco para a outra direção com um (enquanto r> stop) e um correspondente r - = passo para dar a direção oposta.
usar o seguinte comando
1
Eu fiz uma função xfrange sem os problemas de precisão de flutuação mencionados acima. Verifique se;) stackoverflow.com/questions/477486/…
Carlos Vega
1
Você está acumulando erros de arredondamento. Por favor, use isto: `i = 0; r = iniciar enquanto r <parar: i + = 1; r = início + i * passo; rendimento r`
Cees Timmerman
1
Isto é de pythoncentral.io/pythons-range-function-explained (fontes de documentação Python e outras)
Apostolos
31

Aumente a magnitude do iloop e reduza-o quando necessário.

for i * 100 in range(0, 100, 10):
    print i / 100.0

EDIT: Sinceramente, não me lembro por que pensei que funcionaria sintaticamente

for i in range(0, 11, 1):
    print i / 10.0

Isso deve ter a saída desejada.

cmsjr
fonte
Eu acho que você encontrará que range () funciona com números inteiros; nesse caso, essa seria a única solução, usando a mesma função pelo menos.
Matthew Scharley
1
@cmsjr creative: D Apenas uma coisinha: divida por 100.0 para impedir que o Python trunque o resultado se você estiver usando o Python 2.x. Eu acho que no 3.0, funcionará como você o codificou.
Dana
2
for i * 100 in range(0, 100, 10): SyntaxError: Não é possível atribuir ao operador
Anne van Rossum
25

scipypossui uma função integrada arangeque generaliza o range()construtor do Python para satisfazer seus requisitos de manipulação de float.

from scipy import arange

Catherine Ray
fonte
8
Na verdade, é exatamente o mesmo que arangevocê pode encontrar em numpy: >>> import scipy >>> import numpy >>> numpy.arange is scipy.arangeretornará True.
IFreilicht 10/07
from nump import arange as range
shrewmouse 12/03
24

NumPy é um pouco exagerado, eu acho.

[p/10 for p in range(0, 10)]
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]

De um modo geral, fazer um passo a passo 1/xcom yvocê faria

x=100
y=2
[p/x for p in range(0, int(x*y))]
[0.0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99]

( 1/xproduziu menos ruído quando testei).

Kalle
fonte
18

Semelhante à função de R seq , esta retorna uma sequência em qualquer ordem, dado o valor correto da etapa. O último valor é igual ao valor de parada.

def seq(start, stop, step=1):
    n = int(round((stop - start)/float(step)))
    if n > 1:
        return([start + step*i for i in range(n+1)])
    elif n == 1:
        return([start])
    else:
        return([])

Resultados

seq(1, 5, 0.5)

[1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]

seq(10, 0, -1)

[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

seq(10, 0, -2)

[10, 8, 6, 4, 2, 0]

seq(1, 1)

[1]

Zeferino
fonte
2
Esta é uma ótima resposta para quem deseja obtê-lo sem entrar muito em python.
Chani
1
Isso era quase o que eu procurava - note que seq(0.5, 3.0)retorna [0.5, 1.5, 2.5, 3.5]. Para evitar últimas entradas estar fora-de-gama, substitua n = int(round(...com n = int(floor(...com a linha from math import floorna parte superior (acima def seq(...).
FriendFX
2
@FriendFX Não faça isso! Se floorfor usado, seq(0.2, 0.9, 0.1)vai deixar de atingir endpoint direita e vai voltar[0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7, 0.8]
fdermishin
@ user502144: Boa captura, obrigado. Acho que tenho que me contentar com uma das soluções mais complexas para mantê-la geral.
precisa saber é
14

A função interna range () retorna uma sequência de valores inteiros, receio, portanto você não pode usá-la para executar uma etapa decimal.

Eu diria que basta usar um loop while:

i = 0.0
while i <= 1.0:
    print i
    i += 0.1

Se você estiver curioso, o Python está convertendo seu 0,1 para 0, e é por isso que está lhe dizendo que o argumento não pode ser zero.

Dana
fonte
2
Não faça isso! Adicionar .110 vezes não é o mesmo que adicionar 1! docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
cat
12

Aqui está uma solução usando itertools :

import itertools

def seq(start, end, step):
    if step == 0:
        raise ValueError("step must not be 0")
    sample_count = int(abs(end - start) / step)
    return itertools.islice(itertools.count(start, step), sample_count)

Exemplo de uso:

for i in seq(0, 1, 0.1):
    print(i)
Pramod
fonte
Por uma questão de exaustividade, você deve calcular o valor absoluto para a variável sample_count, de que maneira a sua função também irá trabalhar para um começo negativo (ou seja, de -10 a 10)
Deleteman
10
[x * 0.1 for x in range(0, 10)] 

no Python 2.7x fornece o resultado de:

[0,0, 0,1, 0,2, 0,30000000000000004, 0,4, 0,5, 0,6000000000000001, 0,7000000000000001, 0,8, 0,9]

mas se você usar:

[ round(x * 0.1, 1) for x in range(0, 10)]

fornece o desejado:

[0,0, 0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7, 0,8, 0,9]

Nik
fonte
5

E se você fizer isso com frequência, convém salvar a lista gerada r

r=map(lambda x: x/10.0,range(0,10))
for i in r:
    print i
RSabet
fonte
5

more_itertoolsé uma biblioteca de terceiros que implementa uma numeric_rangeferramenta:

import more_itertools as mit


for x in mit.numeric_range(0, 1, 0.1):
    print("{:.1f}".format(x))

Resultado

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Essa ferramenta também funciona para Decimale Fraction.

pylang
fonte
4

Minhas versões usam a função de intervalo original para criar índices multiplicativos para o turno. Isso permite a mesma sintaxe da função de intervalo original. Eu fiz duas versões, uma usando float e outra usando Decimal, porque descobri que em alguns casos eu queria evitar o desvio de arredondamento introduzido pela aritmética de ponto flutuante.

É consistente com os resultados do conjunto vazio, como no intervalo / intervalo.

Passar apenas um único valor numérico para qualquer função retornará a saída da faixa padrão para o valor do teto inteiro do parâmetro de entrada (portanto, se você fornecesse 5.5, retornaria a faixa (6).)

Edit: o código abaixo já está disponível como pacote no pypi: Franges

## frange.py
from math import ceil
# find best range function available to version (2.7.x / 3.x.x)
try:
    _xrange = xrange
except NameError:
    _xrange = range

def frange(start, stop = None, step = 1):
    """frange generates a set of floating point values over the 
    range [start, stop) with step size step

    frange([start,] stop [, step ])"""

    if stop is None:
        for x in _xrange(int(ceil(start))):
            yield x
    else:
        # create a generator expression for the index values
        indices = (i for i in _xrange(0, int((stop-start)/step)))  
        # yield results
        for i in indices:
            yield start + step*i

## drange.py
import decimal
from math import ceil
# find best range function available to version (2.7.x / 3.x.x)
try:
    _xrange = xrange
except NameError:
    _xrange = range

def drange(start, stop = None, step = 1, precision = None):
    """drange generates a set of Decimal values over the
    range [start, stop) with step size step

    drange([start,] stop, [step [,precision]])"""

    if stop is None:
        for x in _xrange(int(ceil(start))):
            yield x
    else:
        # find precision
        if precision is not None:
            decimal.getcontext().prec = precision
        # convert values to decimals
        start = decimal.Decimal(start)
        stop = decimal.Decimal(stop)
        step = decimal.Decimal(step)
        # create a generator expression for the index values
        indices = (
            i for i in _xrange(
                0, 
                ((stop-start)/step).to_integral_value()
            )
        )  
        # yield results
        for i in indices:
            yield float(start + step*i)

## testranges.py
import frange
import drange
list(frange.frange(0, 2, 0.5)) # [0.0, 0.5, 1.0, 1.5]
list(drange.drange(0, 2, 0.5, precision = 6)) # [0.0, 0.5, 1.0, 1.5]
list(frange.frange(3)) # [0, 1, 2]
list(frange.frange(3.5)) # [0, 1, 2, 3]
list(frange.frange(0,10, -1)) # []
Nisan.H
fonte
Como pode frangefuncionar se parar é None? Essa parte do código nem considera mais o tamanho da etapa.
Josch
1
@josch rangetem duas assinaturas :, range(stop)que assume um padrão start=0, step=1e range(start, stop, step), onde nenhuma suposição é feita. frangereflete isso. Ao usar a range(stop)assinatura, ambos frangee drangeiniciam em 0 e aumentam em 1, para que o comportamento deles seja idêntico ao range(stop)comportamento normal , com a parada arredondada para o número inteiro mais próximo.
Nisan.H
4

Esta é a minha solução para obter intervalos com etapas de flutuação.
Usando esta função, não é necessário importar numpy nem instalá-lo.
Tenho certeza de que poderia ser melhorado e otimizado. Sinta-se à vontade para publicá-lo aqui.

from __future__ import division
from math import log

def xfrange(start, stop, step):

    old_start = start #backup this value

    digits = int(round(log(10000, 10)))+1 #get number of digits
    magnitude = 10**digits
    stop = int(magnitude * stop) #convert from 
    step = int(magnitude * step) #0.1 to 10 (e.g.)

    if start == 0:
        start = 10**(digits-1)
    else:
        start = 10**(digits)*start

    data = []   #create array

    #calc number of iterations
    end_loop = int((stop-start)//step)
    if old_start == 0:
        end_loop += 1

    acc = start

    for i in xrange(0, end_loop):
        data.append(acc/magnitude)
        acc += step

    return data

print xfrange(1, 2.1, 0.1)
print xfrange(0, 1.1, 0.1)
print xfrange(-1, 0.1, 0.1)

A saída é:

[1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1]
[-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0]
Carlos Vega
fonte
1
Ocorreu um erro ao perder o último valor se ele estiver dentro de uma etapa do valor de parada. ou seja, xfrange (1,10,2) apenas 1,3,5,7, ausente 9
Lobe
Para referência e outros leitores, compare esta implementação com este stackoverflow.com/a/477610/54964 . Isso não parece ter grandes problemas de flutuação.
Léo Léopold Hertz
@carlosvega Você pode confirmar por que o Lobe obtém o resultado dele?
Léo Léopold Hertz,
3

Para a integridade da boutique, uma solução funcional:

def frange(a,b,s):
  return [] if s > 0 and a > b or s < 0 and a < b or s==0 else [a]+frange(a+s,b,s)
Bijou Trouvaille
fonte
2

Você pode usar esta função:

def frange(start,end,step):
    return map(lambda x: x*step, range(int(start*1./step),int(end*1./step)))
user376536
fonte
Parece não funcionar corretamente, por exemplo:list(frange(99.8, 100.1, 0.1)) => [99.7, 99.80000000000001, 99.9]
Shai Coleman
2

O truque para evitar arredondamento problema é usar um número separado para percorrer o intervalo, que começa e meia o passo à frente de início .

# floating point range
def frange(a, b, stp=1.0):
  i = a+stp/2.0
  while i<b:
    yield a
    a += stp
    i += stp

Como alternativa, numpy.arangepode ser usado.

wolfram77
fonte
2

Isso pode ser feito usando a biblioteca Numpy. A função arange () permite etapas em flutuação. Mas, ele retorna uma matriz numpy que pode ser convertida em lista usando tolist () para nossa conveniência.

for i in np.arange(0, 1, 0.1).tolist():
   print i
user3654478
fonte
2

Minha resposta é semelhante a outras que usam map (), sem a necessidade de NumPy e sem usar lambda (embora você possa). Para obter uma lista de valores flutuantes de 0,0 a t_max nas etapas de dt:

def xdt(n):
    return dt*float(n)
tlist  = map(xdt, range(int(t_max/dt)+1))
Eric Myers
fonte
2

Ninguém surpreso ainda mencionou a solução recomendada nos documentos do Python 3 :

Veja também:

  • A receita do linspace mostra como implementar uma versão lenta do intervalo, adequada para aplicativos de ponto flutuante.

Uma vez definida, a receita é fácil de usar e não requer numpyou quaisquer outras bibliotecas externas, mas funciona como numpy.linspace(). Observe que, em vez de um stepargumento, o terceiro numargumento especifica o número de valores desejados, por exemplo:

print(linspace(0, 10, 5))
# linspace(0, 10, 5)
print(list(linspace(0, 10, 5)))
# [0.0, 2.5, 5.0, 7.5, 10]

Cito uma versão modificada da receita completa do Python 3 de Andrew Barnert abaixo:

import collections.abc
import numbers

class linspace(collections.abc.Sequence):
    """linspace(start, stop, num) -> linspace object

    Return a virtual sequence of num numbers from start to stop (inclusive).

    If you need a half-open range, use linspace(start, stop, num+1)[:-1].
    """
    def __init__(self, start, stop, num):
        if not isinstance(num, numbers.Integral) or num <= 1:
            raise ValueError('num must be an integer > 1')
        self.start, self.stop, self.num = start, stop, num
        self.step = (stop-start)/(num-1)
    def __len__(self):
        return self.num
    def __getitem__(self, i):
        if isinstance(i, slice):
            return [self[x] for x in range(*i.indices(len(self)))]
        if i < 0:
            i = self.num + i
        if i >= self.num:
            raise IndexError('linspace object index out of range')
        if i == self.num-1:
            return self.stop
        return self.start + i*self.step
    def __repr__(self):
        return '{}({}, {}, {})'.format(type(self).__name__,
                                       self.start, self.stop, self.num)
    def __eq__(self, other):
        if not isinstance(other, linspace):
            return False
        return ((self.start, self.stop, self.num) ==
                (other.start, other.stop, other.num))
    def __ne__(self, other):
        return not self==other
    def __hash__(self):
        return hash((type(self), self.start, self.stop, self.num))
Chris_Rands
fonte
2

Para combater os problemas de precisão do flutuador, você pode usar o Decimalmódulo .

Isso exige um esforço extra de conversão Decimalde intou para floatescrever o código, mas você pode passar stre modificar a função se esse tipo de conveniência for realmente necessário.

from decimal import Decimal
from decimal import Decimal as D


def decimal_range(*args):

    zero, one = Decimal('0'), Decimal('1')

    if len(args) == 1:
        start, stop, step = zero, args[0], one
    elif len(args) == 2:
        start, stop, step = args + (one,)
    elif len(args) == 3:
        start, stop, step = args
    else:
        raise ValueError('Expected 1 or 2 arguments, got %s' % len(args))

    if not all([type(arg) == Decimal for arg in (start, stop, step)]):
        raise ValueError('Arguments must be passed as <type: Decimal>')

    # neglect bad cases
    if (start == stop) or (start > stop and step >= zero) or \
                          (start < stop and step <= zero):
        return []

    current = start
    while abs(current) < abs(stop):
        yield current
        current += step

Saídas de amostra -

list(decimal_range(D('2')))
# [Decimal('0'), Decimal('1')]
list(decimal_range(D('2'), D('4.5')))
# [Decimal('2'), Decimal('3'), Decimal('4')]
list(decimal_range(D('2'), D('4.5'), D('0.5')))
# [Decimal('2'), Decimal('2.5'), Decimal('3.0'), Decimal('3.5'), Decimal('4.0')]
list(decimal_range(D('2'), D('4.5'), D('-0.5')))
# []
list(decimal_range(D('2'), D('-4.5'), D('-0.5')))
# [Decimal('2'),
#  Decimal('1.5'),
#  Decimal('1.0'),
#  Decimal('0.5'),
#  Decimal('0.0'),
#  Decimal('-0.5'),
#  Decimal('-1.0'),
#  Decimal('-1.5'),
#  Decimal('-2.0'),
#  Decimal('-2.5'),
#  Decimal('-3.0'),
#  Decimal('-3.5'),
#  Decimal('-4.0')]
shad0w_wa1k3r
fonte
2
Com características semelhantes Decimalentradas, np.arangefunciona da mesma:np.arange(Decimal('-2.0'), Decimal('2.0'), Decimal('0.1'))
hpaulj
2
Sim obrigado. Embora, isso precisaria de uma lib externa (numpy).
precisa saber é o seguinte
1

Adicione a correção automática para a possibilidade de um sinal incorreto na etapa:

def frange(start,step,stop):
    step *= 2*((stop>start)^(step<0))-1
    return [start+i*step for i in range(int((stop-start)/step))]
BobH
fonte
1

Minha solução:

def seq(start, stop, step=1, digit=0):
    x = float(start)
    v = []
    while x <= stop:
        v.append(round(x,digit))
        x += step
    return v
Jjen
fonte
1

Melhor solução: sem erro de arredondamento
_________________________________________________________________________________

>>> step = .1
>>> N = 10     # number of data points
>>> [ x / pow(step, -1) for x in range(0, N + 1) ]

[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

_________________________________________________________________________________

Ou, para um intervalo definido em vez de pontos de dados definidos (por exemplo, função contínua), use:

>>> step = .1
>>> rnge = 1     # NOTE range = 1, i.e. span of data points
>>> N = int(rnge / step
>>> [ x / pow(step,-1) for x in range(0, N + 1) ]

[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

Para implementar uma função: substitua x / pow(step, -1)por f( x / pow(step, -1) )e defina f.
Por exemplo:

>>> import math
>>> def f(x):
        return math.sin(x)

>>> step = .1
>>> rnge = 1     # NOTE range = 1, i.e. span of data points
>>> N = int(rnge / step)
>>> [ f( x / pow(step,-1) ) for x in range(0, N + 1) ]

[0.0, 0.09983341664682815, 0.19866933079506122, 0.29552020666133955, 0.3894183423086505, 
 0.479425538604203, 0.5646424733950354, 0.644217687237691, 0.7173560908995228,
 0.7833269096274834, 0.8414709848078965]
Jason
fonte
1

start e stop são inclusivos em vez de um ou outro (geralmente o stop é excluído) e sem importações e usando geradores

def rangef(start, stop, step, fround=5):
    """
    Yields sequence of numbers from start (inclusive) to stop (inclusive)
    by step (increment) with rounding set to n digits.

    :param start: start of sequence
    :param stop: end of sequence
    :param step: int or float increment (e.g. 1 or 0.001)
    :param fround: float rounding, n decimal places
    :return:
    """
    try:
        i = 0
        while stop >= start and step > 0:
            if i==0:
                yield start
            elif start >= stop:
                yield stop
            elif start < stop:
                if start == 0:
                    yield 0
                if start != 0:
                    yield start
            i += 1
            start += step
            start = round(start, fround)
        else:
            pass
    except TypeError as e:
        yield "type-error({})".format(e)
    else:
        pass


# passing
print(list(rangef(-100.0,10.0,1)))
print(list(rangef(-100,0,0.5)))
print(list(rangef(-1,1,0.2)))
print(list(rangef(-1,1,0.1)))
print(list(rangef(-1,1,0.05)))
print(list(rangef(-1,1,0.02)))
print(list(rangef(-1,1,0.01)))
print(list(rangef(-1,1,0.005)))
# failing: type-error:
print(list(rangef("1","10","1")))
print(list(rangef(1,10,"1")))

Python 3.6.2 (v3.6.2: 5fd33b5, 8 de julho de 2017, 04:57:36) [MSC v.1900 de 64 bits (AMD64)]

Goran B.
fonte
1

Eu sei que estou atrasado para a festa aqui, mas aqui está uma solução de gerador trivial que está funcionando na 3.6:

def floatRange(*args):
    start, step = 0, 1
    if len(args) == 1:
        stop = args[0]
    elif len(args) == 2:
        start, stop = args[0], args[1]
    elif len(args) == 3:
        start, stop, step = args[0], args[1], args[2]
    else:
        raise TypeError("floatRange accepts 1, 2, or 3 arguments. ({0} given)".format(len(args)))
    for num in start, step, stop:
        if not isinstance(num, (int, float)):
            raise TypeError("floatRange only accepts float and integer arguments. ({0} : {1} given)".format(type(num), str(num)))
    for x in range(int((stop-start)/step)):
        yield start + (x * step)
    return

você pode chamá-lo exatamente como o original range()... não há tratamento de erros, mas deixe-me saber se há um erro que possa ser razoavelmente detectado, e eu atualizarei. ou você pode atualizá-lo. isso é StackOverflow.

David Culbreth
fonte
0

Aqui está minha solução que funciona bem com float_range (-1, 0, 0,01) e funciona sem erros de representação de ponto flutuante. Não é muito rápido, mas funciona bem:

from decimal import Decimal

def get_multiplier(_from, _to, step):
    digits = []
    for number in [_from, _to, step]:
        pre = Decimal(str(number)) % 1
        digit = len(str(pre)) - 2
        digits.append(digit)
    max_digits = max(digits)
    return float(10 ** (max_digits))


def float_range(_from, _to, step, include=False):
    """Generates a range list of floating point values over the Range [start, stop]
       with step size step
       include=True - allows to include right value to if possible
       !! Works fine with floating point representation !!
    """
    mult = get_multiplier(_from, _to, step)
    # print mult
    int_from = int(round(_from * mult))
    int_to = int(round(_to * mult))
    int_step = int(round(step * mult))
    # print int_from,int_to,int_step
    if include:
        result = range(int_from, int_to + int_step, int_step)
        result = [r for r in result if r <= int_to]
    else:
        result = range(int_from, int_to, int_step)
    # print result
    float_result = [r / mult for r in result]
    return float_result


print float_range(-1, 0, 0.01,include=False)

assert float_range(1.01, 2.06, 5.05 % 1, True) ==\
[1.01, 1.06, 1.11, 1.16, 1.21, 1.26, 1.31, 1.36, 1.41, 1.46, 1.51, 1.56, 1.61, 1.66, 1.71, 1.76, 1.81, 1.86, 1.91, 1.96, 2.01, 2.06]

assert float_range(1.01, 2.06, 5.05 % 1, False)==\
[1.01, 1.06, 1.11, 1.16, 1.21, 1.26, 1.31, 1.36, 1.41, 1.46, 1.51, 1.56, 1.61, 1.66, 1.71, 1.76, 1.81, 1.86, 1.91, 1.96, 2.01]
pymen
fonte
0

Sou apenas iniciante, mas tive o mesmo problema ao simular alguns cálculos. Aqui está como eu tentei resolver isso, o que parece estar funcionando com etapas decimais.

Eu também sou muito preguiçoso e, por isso, achei difícil escrever minha própria função de intervalo.

Basicamente, o que fiz foi alterado xrange(0.0, 1.0, 0.01)para xrange(0, 100, 1)e usado a divisão 100.0dentro do loop. Eu também estava preocupado, se haverá erros de arredondamento. Então eu decidi testar, se há algum. Agora ouvi dizer que, se, por exemplo, 0.01de um cálculo não for exatamente o float, 0.01comparando-os, retorne False (se estiver errado, por favor me avise).

Então, decidi testar se minha solução funcionaria para meu intervalo executando um pequeno teste:

for d100 in xrange(0, 100, 1):
    d = d100 / 100.0
    fl = float("0.00"[:4 - len(str(d100))] + str(d100))
    print d, "=", fl , d == fl

E imprimiu True para cada um.

Agora, se estiver errado, informe-me.

user2836437
fonte
0

Este liner não irá desorganizar seu código. O sinal do parâmetro step é importante.

def frange(start, stop, step):
    return [x*step+start for x in range(0,round(abs((stop-start)/step)+0.5001),
        int((stop-start)/step<0)*-2+1)]
Tjaart
fonte