Pergunta

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

Eu pensei que eu poderia fazê-lo como o seguinte, mas falhou:

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

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

Foi útil?

Solução

Ao invés de usar um passo decimal diretamente, é muito mais seguro para expressar isso em termos de quantos pontos você quer. Caso contrário, de ponto flutuante erro de arredondamento é susceptível de dar-lhe um resultado errado.

Você pode usar a função linspace do NumPy biblioteca (que não faz parte do biblioteca padrão, mas é relativamente fácil de obter). linspace leva um número de pontos de retorno, e também permite que você especifique se deve ou não incluir o ponto final direita:

>>> 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 quiser usar um valor do passo de ponto flutuante, você 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

ponto flutuante arredondamento irá causar problemas, no entanto. Aqui está um caso simples, onde o arredondamento erro faz com que arange para produzir uma matriz de comprimento-4 quando ele só deve produzir 3 números:

>>> numpy.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])

Outras dicas

gama de Python () só pode fazer inteiros, não de ponto flutuante. No seu caso específico, você pode usar uma compreensão de lista em vez disso:

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

(Substituir a chamada para intervalo com aquela expressão.)

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

Com base na 'xrange ([Iniciar], parada [, passo])' , você pode definir um gerador que aceita e produz qualquer tipo que você escolher (stick aos tipos de apoio + 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']
>>> 

Aumentar a magnitude da i para o loop e depois reduzi-lo quando você precisar dele.

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

EDIT: Eu honestamente não me lembro porque eu pensei que iria trabalhar sintaticamente

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

Isso deve ter a saída desejada.

scipy foi construído em um arange função que generaliza construtor range() do Python para satisfazer a sua exigência de manipulação float.

from scipy import arange

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, para fazer um passo-a-1/x até y você 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/x produziu menos arredondamento barulho quando eu testei).

O range () built-in função retorna uma seqüência de valores inteiros, estou com medo, então você não pode usá-lo para fazer um passo decimal.

Eu diria que é só usar um tempo loop:

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

Se você está curioso, Python é converter o seu 0,1-0, razão pela qual ele está dizendo a você o argumento não pode ser zero.

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 Utilização:

for i in seq(0, 1, 0.1):
    print(i)
[x * 0.1 for x in range(0, 10)] 

em Python 2,7x lhe dá o resultado de:

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

mas se você usar:

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

dá-lhe a desejada:

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

import numpy as np
for i in np.arange(0, 1, 0.1): 
    print i 

E se você fizer isso muitas vezes, você pode querer salvar a lista r gerado

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

As minhas versões usam a função gama original para criar índices multiplicativos para a mudança. Isso permite mesma sintaxe para a função de intervalo original. Eu fiz duas versões, uma com flutuador e um 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 conjunto vazio como na gama / xrange.

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

Editar: o código abaixo está agora disponível como um pacote em 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)) # []

Esta é a minha solução para obter intervalos com etapas flutuador.
Usando esta função não é necessário importar numpy, nem instalá-lo. e eu sou bastante certo de que ele poderia ser melhorado e aperfeiçoado. Sinta-se livre para fazê-lo e publicá-la 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]

more_itertools é uma biblioteca de terceiros que implementa um numeric_range ferramenta:

import more_itertools as mit


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

saída

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Esta ferramenta também funciona para Decimal e Fraction

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)))

O truque para evitar round-off problema é a utilização de um número separado para percorrer o intervalo, que começa e metade passo à frente de start .

# 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.arange pode ser usado.

Para a integralidade 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)

Isso pode ser feito usando a biblioteca Numpy. função arange () permite passos no flutuador. Mas, ele retorna uma matriz numpy que pode ser convertido em lista usando ToList () para nossa conveniência.

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

A minha resposta é semelhante a outros usando mapa (), sem necessidade de NumPy, e sem o uso de lambda (embora você poderia). Para obter uma lista de valores float de 0,0 a t_max em passos de dt:

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

Adicionar auto-correção 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))]

A 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

Melhor Solução: nenhum 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 do conjunto de pontos de dados (por exemplo função contínua), o uso:

>>> 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: substituir x / pow(step, -1) com f( x / pow(step, -1) ), e definir 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]

iniciar e parar são, inclusive, em vez de um ou outro (geralmente parada é 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, 08 de julho de 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]

surpreendido ninguém ainda mencionou a solução recomendada nas Python 3 docs :

Veja também:

Uma vez definido, a receita é fácil de usar e não requer numpy ou quaisquer outras bibliotecas externas, mas funções como numpy.linspace(). Note-se que em vez de um argumento step, o terceiro argumento num 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 do total de receita 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))

Para combater os problemas de precisão float, você poderia usar o Decimal módulo .

Isto exige um esforço extra de conversão para Decimal de int ou float ao escrever o código, mas você pode em vez passar str e modificar a função, se esse tipo de conveniência é de fato 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')]

Aqui está a minha solução que funciona bem com float_range (-1, 0, 0,01) e obras 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]

Eu sou apenas um novato, mas eu tive o mesmo problema, quando simulando alguns cálculos. Aqui está como eu tentou resolver isso, que parece estar funcionando com passos decimais.

Eu também sou muito preguiçoso e então eu achei difícil de escrever minha própria função gama.

Basicamente o que eu fiz é mudou minha xrange(0.0, 1.0, 0.01) para xrange(0, 100, 1) e usou a divisão por 100.0 dentro do loop. Eu também estava preocupado, se haverá erros de arredondamento. Então eu decidi teste, se houver algum. Agora eu ouvi, que, se por exemplo 0.01 de um cálculo não é exatamente o 0.01 flutuador compará-los deve retornar False (se eu estiver errado, por favor me avise).

Então eu decidi testar se minha solução vai funcionar para minha faixa, 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 é impresso verdadeiro para cada um.

Agora, se eu estou ficando é totalmente errado, por favor me avise.

Este forro não vai encher o seu código. O sinal da passo parâmetro é 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)]

frange (start, stop, precisão)

def frange(a,b,i):
    p = 10**i
    sr = a*p
    er = (b*p) + 1
    p = float(p)
    return map(lambda x: x/p, xrange(sr,er))

In >frange(-1,1,1)

Out>[-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top