python floating-point range

python - plt title position



¿Cómo utilizar un valor de paso de rango decimal()? (30)

Agregue autocorrección para la posibilidad de un signo incorrecto en el paso:

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

¿Hay una manera de pasar entre 0 y 1 por 0.1?

Pensé que podría hacerlo de la siguiente manera, pero falló:

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

En cambio, dice que el argumento de paso no puede ser cero, lo cual no esperaba.


Aquí está mi solución que funciona bien con float_range (-1, 0, 0.01) y funciona sin errores de representación de punto flotante. No es muy rápido, pero funciona bien:

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]


Aquí hay una solución usando itertools :

import itertools def seq(start, end, step): assert(step != 0) sample_count = abs(end - start) / step return itertools.islice(itertools.count(start, step), sample_count)

Ejemplo de uso:

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

Salida:

0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9


Aumente la magnitud de i para el bucle y luego redúzcalo cuando lo necesite.

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

EDIT: Honestamente no puedo recordar por qué pensé que funcionaría sintácticamente

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

Eso debería tener la salida deseada.


El rango de Python () solo puede hacer números enteros, no puntos flotantes. En su caso específico, puede utilizar una lista de comprensión en su lugar:

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

(Reemplace la llamada a rango con esa expresión).

Para el caso más general, es posible que desee escribir una función personalizada o un generador.


El truco para evitar el problema de redondeo es usar un número separado para moverse a través del rango, que comienza y la mitad del paso adelante del inicio .

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

Alternativamente, se puede usar numpy.arange .


En lugar de usar un paso decimal directamente, es mucho más seguro expresar esto en términos de cuántos puntos desea. De lo contrario, es probable que el error de redondeo de punto flotante le dé un resultado incorrecto.

Puede usar la función linspace de la biblioteca NumPy (que no es parte de la biblioteca estándar pero es relativamente fácil de obtener). linspace toma una cantidad de puntos para devolver, y también le permite especificar si desea incluir el punto final correcto:

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

Si realmente desea utilizar un valor de paso de punto flotante, puede numpy.arange con 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])

Sin embargo, el error de redondeo de punto flotante causará problemas. Aquí hay un caso simple donde el error de redondeo hace que un arange produzca una matriz de longitud 4 cuando solo debería producir 3 números:

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


Esta es mi solución para obtener rangos con pasos de flotación.
Usando esta función no es necesario importar numpy, ni instalarlo.
Estoy bastante seguro de que podría mejorarse y optimizarse. Siéntete libre de hacerlo y publicarlo aquí.

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)

La salida es:

[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]


Este forro no desordenará su código. El signo del parámetro de paso es 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)]


La función incorporada range () devuelve una secuencia de valores enteros, me temo, por lo que no puedes usarla para hacer un paso decimal.

Yo diría que solo use un bucle while:

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

Si tienes curiosidad, Python está convirtiendo tu 0.1 en 0, por lo que te está diciendo que el argumento no puede ser cero.


La mejor solución: no hay error de redondeo
_________________________________________________________________________________

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

_________________________________________________________________________________

O, para un rango establecido en lugar de puntos de datos establecidos (por ejemplo, función continua), 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 una función: reemplace x / pow(step, -1) con f( x / pow(step, -1) ) , y defina f .
Por ejemplo:

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


Mi respuesta es similar a la de otros usando map (), sin necesidad de NumPy y sin usar lambda (aunque podría hacerlo). Para obtener una lista de valores flotantes de 0.0 a t_max en pasos de dt:

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


Mi solución:

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


Mis versiones usan la función de rango original para crear índices multiplicativos para el cambio. Esto permite la misma sintaxis a la función de rango original. Hice dos versiones, una con flotador y otra con decimal, porque descubrí que en algunos casos quería evitar la deriva de redondeo introducida por la aritmética de punto flotante.

Es consistente con los resultados de conjuntos vacíos como en rango / rango.

Pasar solo un único valor numérico a cualquiera de las funciones devolverá la salida del rango estándar al valor techo entero del parámetro de entrada (por lo tanto, si le asignó 5.5, devolvería el rango (6)).

Edición: el siguiente código ya está disponible como paquete en 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)) # []


NumPy es un poco excesivo, creo.

[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]

En términos generales, para hacer un paso por 1/x hasta y harías

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 produjo menos ruido de redondeo cuando hice la prueba).


Para completar la boutique, una solución 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)


Para contrarrestar los problemas de precisión de flotación, puede utilizar el módulo Decimal .

Esto exige un esfuerzo adicional de convertir a Decimal desde int o float mientras escribe el código, pero en su lugar puede pasar y modificar la función si ese tipo de comodidad es realmente necesaria.

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

Salidas de muestra -

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


Puede utilizar esta función:

def frange(start,end,step): return map(lambda x: x*step, range(int(start*1./step),int(end*1./step)))


Se puede hacer utilizando la librería Numpy. La función arange () permite pasos en float. Pero, devuelve una matriz numpy que se puede convertir a la lista usando tolist () para nuestra conveniencia.

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


Similar a R''s función de seq R''s , esta devuelve una secuencia en cualquier orden dado el valor de paso correcto. El último valor es igual al 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]


Sobre la base de ''xrange ([inicio], detener [, paso])'' , puede definir un generador que acepte y produzca cualquier tipo que elija (se adhiere a los tipos que admiten + y < ):

>>> 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''] >>>


Solo soy un principiante, pero tuve el mismo problema, al simular algunos cálculos. Aquí es cómo intenté resolver esto, que parece estar funcionando con pasos decimales.

También soy bastante perezoso y por eso me resultó difícil escribir mi propia función de rango.

Básicamente, lo que hice fue cambiar mi xrange(0.0, 1.0, 0.01) a xrange(0, 100, 1) y usé la división por 100.0 dentro del bucle. También me preocupaba, si habrá errores de redondeo. Así que decidí probar, si hay alguna. Ahora escuché que si, por ejemplo, 0.01 de un cálculo no es exactamente el valor flotante 0.01 compararlos debería devolver False (si me equivoco, por favor, avíseme).

Así que decidí probar si mi solución funcionaría para mi rango ejecutando una prueba corta:

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

Y se imprimió verdadero para cada uno.

Ahora, si lo entiendo totalmente mal, por favor hágamelo saber.


Sorprendido, nadie ha mencionado todavía la solución recomendada en los documentos de Python 3 :

Ver también:

  • La receta de linspace muestra cómo implementar una versión perezosa de rango que sea adecuada para aplicaciones de punto flotante.

Una vez definida, la receta es fácil de usar y no requiere numpy ni ninguna otra biblioteca externa, pero funciona como numpy.linspace() . Tenga en cuenta que en lugar de un argumento de step , el tercer argumento num especifica el número de valores deseados, por ejemplo:

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 una versión modificada de la receta completa de Python 3 de Andrew Barnert a continuación:

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


Y si lo hace a menudo, puede guardar la lista generada r

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


el inicio y la parada son inclusivos en lugar de uno u otro (normalmente se excluye la parada) y sin importaciones, y se utilizan generadores

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 julio de 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]


frange (start, stop, precision)

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]


more_itertools es una biblioteca de terceros que implementa una herramienta numeric_range :

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

Salida

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9

Esta herramienta también funciona para Decimal y Fraction .


scipy tiene una función incorporada scipy que generaliza el constructor de range() Python para satisfacer sus requisitos de manejo de flotadores.

from scipy import arange


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

en Python 2.7x te da el resultado de:

[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]

pero si usas:

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

te da la deseada

[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