python range fractions decimal

subplot python title



rango() para carrozas (14)

¿Existe un rango () equivalente para flotadores en Python? NO Use esto:

def f_range(start, end, step): a = range(int(start/0.01), int(end/0.01), int(step/0.01)) var = [] for item in a: var.append(item*0.01) return var

¿Existe un range() equivalente para flotadores en 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


Ayudé a agregar la función numeric_range al paquete more-itertools .

more_itertools.numeric_range(start, stop, step) actúa como el rango de función incorporado, pero puede manejar flotantes, decimales y tipos de fracciones.

>>> from more_itertools import numeric_range >>> tuple(numeric_range(.1, 5, 1)) (0.1, 1.1, 2.1, 3.1, 4.1)


Creo que hay una respuesta muy simple que realmente emula todas las características del rango, pero para ambos, flotante y entero. En esta solución, solo suponga que su aproximación por defecto es 1e-7 (o la que elija) y puede cambiarla cuando llame a la función.

def drange(start,stop=None,jump=1,approx=7): # Approx to 1e-7 by default '''''' This function is equivalent to range but for both float and integer '''''' if not stop: # If there is no y value: range(x) stop= start start= 0 valor= round(start,approx) while valor < stop: if valor==int(valor): yield int(round(valor,approx)) else: yield float(round(valor,approx)) valor += jump for i in drange(12): print(i)


Escribí una función que devuelve una tupla de un rango de números de punto flotante de precisión doble sin ningún lugar decimal más allá de las centésimas. era simplemente una cuestión de analizar los valores de rango como cadenas y dividir el exceso. Lo uso para mostrar rangos para seleccionar desde dentro de una UI. Espero que alguien más lo encuentre útil.

def drange(start,stop,step): double_value_range = [] while start<stop: a = str(start) a.split(''.'')[1].split(''0'')[0] start = float(str(a)) double_value_range.append(start) start = start+step double_value_range_tuple = tuple(double_value_range) #print double_value_range_tuple return double_value_range_tuple


Evaluados con entusiasmo ( range 2.x):

[x * .5 for x in range(10)]

xrange (2.x xrange , 3.x range ):

itertools.imap(lambda x: x * .5, xrange(10)) # or range(10) as appropriate

Alternativamente:

itertools.islice(itertools.imap(lambda x: x * .5, itertools.count()), 10) # without applying the `islice`, we get an infinite stream of half-integers.


No conozco una función incorporada, pero escribir una como this no debería ser demasiado complicada.

def frange(x, y, jump): while x < y: yield x x += jump

Como mencionan los comentarios, esto podría producir resultados impredecibles como:

>>> list(frange(0, 100, 0.1))[-1] 99.9999999999986

Para obtener el resultado esperado, puede usar una de las otras respuestas en esta pregunta, o como se menciona en @Tadhg, puede usar decimal.Decimal como argumento de jump . Asegúrese de inicializarlo con una cadena en lugar de un flotador.

>>> import decimal >>> list(frange(0, 100, decimal.Decimal(''0.1'')))[-1] Decimal(''99.9'')

O incluso:

import decimal def drange(x, y, jump): while x < y: yield float(x) x += decimal.Decimal(jump)

Y entonces:

>>> list(drange(0, 100, ''0.1''))[-1] 99.9


No existe tal función incorporada, pero puede usar lo siguiente (código de Python 3) para hacer el trabajo tan seguro como Python lo permita.

from fractions import Fraction def frange(start, stop, jump, end=False, via_str=False): """ Equivalent of Python 3 range for decimal numbers. Notice that, because of arithmetic errors, it is safest to pass the arguments as strings, so they can be interpreted to exact fractions. >>> assert Fraction(''1.1'') - Fraction(11, 10) == 0.0 >>> assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840) Parameter `via_str` can be set to True to transform inputs in strings and then to fractions. When inputs are all non-periodic (in base 10), even if decimal, this method is safe as long as approximation happens beyond the decimal digits that Python uses for printing. For example, in the case of 0.1, this is the case: >>> assert str(0.1) == ''0.1'' >>> assert ''%.50f'' % 0.1 == ''0.10000000000000000555111512312578270211815834045410'' If you are not sure whether your decimal inputs all have this property, you are better off passing them as strings. String representations can be in integer, decimal, exponential or even fraction notation. >>> assert list(frange(1, 100.0, ''0.1'', end=True))[-1] == 100.0 >>> assert list(frange(1.0, ''100'', ''1/10'', end=True))[-1] == 100.0 >>> assert list(frange(''1'', ''100.0'', ''.1'', end=True))[-1] == 100.0 >>> assert list(frange(''1.0'', 100, ''1e-1'', end=True))[-1] == 100.0 >>> assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0 >>> assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0 """ if via_str: start = str(start) stop = str(stop) jump = str(jump) start = Fraction(start) stop = Fraction(stop) jump = Fraction(jump) while start < stop: yield float(start) start += jump if end and start == stop: yield(float(start))

Puede verificarlo todo ejecutando algunas afirmaciones:

assert Fraction(''1.1'') - Fraction(11, 10) == 0.0 assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840) assert str(0.1) == ''0.1'' assert ''%.50f'' % 0.1 == ''0.10000000000000000555111512312578270211815834045410'' assert list(frange(1, 100.0, ''0.1'', end=True))[-1] == 100.0 assert list(frange(1.0, ''100'', ''1/10'', end=True))[-1] == 100.0 assert list(frange(''1'', ''100.0'', ''.1'', end=True))[-1] == 100.0 assert list(frange(''1.0'', 100, ''1e-1'', end=True))[-1] == 100.0 assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0 assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0 assert list(frange(2, 3, ''1/6'', end=True))[-1] == 3.0 assert list(frange(0, 100, ''1/3'', end=True))[-1] == 100.0

Código disponible en GitHub


Puedes usar:

[x / 10.0 for x in range(5, 50, 15)]

o use lambda / map:

map(lambda x: x/10.0, range(5, 50, 15))


Pylab tiene frange (un contenedor, en realidad, para matplotlib.mlab.frange ):

>>> import pylab as pl >>> pl.frange(0.5,5,0.5) array([ 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. ])


Solía ​​usar numpy.arange pero tuve algunas complicaciones al controlar la cantidad de elementos que devuelve, debido a los errores de coma flotante. Así que ahora uso linspace , por ejemplo:

>>> import numpy >>> numpy.linspace(0, 10, num=4) array([ 0. , 3.33333333, 6.66666667, 10. ])


utilizando itertools : rango de coma flotante evaluado perezosamente:

>>> from itertools import count, takewhile >>> def frange(start, stop, step): return takewhile(lambda x: x< stop, count(start, step)) >>> list(frange(0.5, 5, 1.5)) # [0.5, 2.0, 3.5]


¿Por qué no hay implementación de rango flotante en la biblioteca estándar?

Como queda claro en todas las publicaciones aquí, no hay una versión de coma flotante range() . Dicho esto, la omisión tiene sentido si consideramos que la función range() se usa a menudo como un generador de índice (y, por supuesto, eso significa un descriptor de acceso ). Entonces, cuando llamamos al range(0,40) , de hecho estamos diciendo que queremos 40 valores comenzando en 0, hasta 40, pero no incluido en 40.

Cuando consideramos que la generación de índices depende tanto del número de índices como de sus valores, el uso de una implementación de range() flotante range() en la biblioteca estándar tiene menos sentido. Por ejemplo, si llamamos a la función frange(0, 10, 0.25) , esperaríamos que se incluyeran 0 y 10, pero eso daría un vector con 41 valores.

Por lo tanto, una función de frange() dependiendo de su uso siempre exhibirá un comportamiento contrario a la intuición; o tiene demasiados valores como se percibe desde la perspectiva de indexación o no incluye un número que razonablemente debería devolverse desde la perspectiva matemática.

El caso de uso matemático

Dicho esto, como se discutió, numpy.linspace() realiza la generación con la perspectiva matemática muy bien:

numpy.linspace(0, 10, 41) array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. , 2.25, 2.5 , 2.75, 3. , 3.25, 3.5 , 3.75, 4. , 4.25, 4.5 , 4.75, 5. , 5.25, 5.5 , 5.75, 6. , 6.25, 6.5 , 6.75, 7. , 7.25, 7.5 , 7.75, 8. , 8.25, 8.5 , 8.75, 9. , 9.25, 9.5 , 9.75, 10. ])

El caso de uso de indexación

Y para la perspectiva de indexación, he escrito un enfoque ligeramente diferente con alguna magia de cuerdas engañosa que nos permite especificar el número de decimales.

# Float range function - string formatting method def frange_S (start, stop, skip = 1.0, decimals = 2): for i in range(int(start / skip), int(stop / skip)): yield float(("%0." + str(decimals) + "f") % (i * skip))

Del mismo modo, también podemos usar la función de round incorporada y especificar el número de decimales:

# Float range function - rounding method def frange_R (start, stop, skip = 1.0, decimals = 2): for i in range(int(start / skip), int(stop / skip)): yield round(i * skip, ndigits = decimals)

Una rápida comparación y rendimiento

Por supuesto, dada la discusión anterior, estas funciones tienen un caso de uso bastante limitado. No obstante, aquí hay una comparación rápida:

def compare_methods (start, stop, skip): string_test = frange_S(start, stop, skip) round_test = frange_R(start, stop, skip) for s, r in zip(string_test, round_test): print(s, r) compare_methods(-2, 10, 1/3)

Los resultados son idénticos para cada uno:

-2.0 -2.0 -1.67 -1.67 -1.33 -1.33 -1.0 -1.0 -0.67 -0.67 -0.33 -0.33 0.0 0.0 ... 8.0 8.0 8.33 8.33 8.67 8.67 9.0 9.0 9.33 9.33 9.67 9.67

Y algunos tiempos:

>>> import timeit >>> setup = """ ... def frange_s (start, stop, skip = 1.0, decimals = 2): ... for i in range(int(start / skip), int(stop / skip)): ... yield float(("%0." + str(decimals) + "f") % (i * skip)) ... def frange_r (start, stop, skip = 1.0, decimals = 2): ... for i in range(int(start / skip), int(stop / skip)): ... yield round(i * skip, ndigits = decimals) ... start, stop, skip = -1, 8, 1/3 ... """ >>> min(timeit.Timer(''string_test = frange_s(start, stop, skip); [x for x in string_test]'', setup=setup).repeat(30, 1000)) 0.024284090992296115 >>> min(timeit.Timer(''round_test = frange_r(start, stop, skip); [x for x in round_test]'', setup=setup).repeat(30, 1000)) 0.025324633985292166

Parece que el método de formateo de cadenas gana por un pelo en mi sistema.

Las limitaciones

Y finalmente, una demostración del punto de la discusión anterior y una última limitación:

# "Missing" the last value (10.0) for x in frange_R(0, 10, 0.25): print(x) 0.25 0.5 0.75 1.0 ... 9.0 9.25 9.5 9.75

Además, cuando el parámetro de skip no es divisible por el valor de stop , puede haber un boquete enorme dado el último problema:

# Clearly we know that 10 - 9.43 is equal to 0.57 for x in frange_R(0, 10, 3/7): print(x) 0.0 0.43 0.86 1.29 ... 8.14 8.57 9.0 9.43

Hay formas de abordar este problema, pero al final del día, lo mejor sería usar Numpy.


Kichik proporcionó una solución sin nudosas dependencias, pero debido a la aritmética de punto flotante , a menudo se comporta inesperadamente. Como noté me y blubberdiblub , elementos adicionales se cuelan fácilmente en el resultado. Por ejemplo, naive_frange(0.0, 1.0, 0.1) produciría 0.999... como su último valor y por lo tanto arrojaría 11 valores en total.

Aquí se proporciona una versión robusta:

def frange(x, y, jump=1.0): ''''''Range for floats.'''''' i = 0.0 x = float(x) # Prevent yielding integers. x0 = x epsilon = jump / 2.0 yield x # yield always first value while x + epsilon < y: i += 1.0 x = x0 + i * jump yield x

Debido a la multiplicación, los errores de redondeo no se acumulan. El uso de epsilon se ocupa de un posible error de redondeo de la multiplicación, aunque los problemas, por supuesto, pueden aumentar en los extremos muy pequeños y muy grandes. Ahora, como se esperaba:

> a = list(frange(0.0, 1.0, 0.1)) > a[-1] 0.9 > len(a) 10

Y con números algo mayores:

> b = list(frange(0.0, 1000000.0, 0.1)) > b[-1] 999999.9 > len(b) 10000000

El código también está disponible como GitHub Gist .


def Range(*argSequence): if len(argSequence) == 3: imin = argSequence[0]; imax = argSequence[1]; di = argSequence[2] i = imin; iList = [] while i <= imax: iList.append(i) i += di return iList if len(argSequence) == 2: return Range(argSequence[0], argSequence[1], 1) if len(argSequence) == 1: return Range(1, argSequence[0], 1)

Tenga en cuenta que la primera letra de Range es mayúscula. Este método de nomenclatura no se recomienda para las funciones en Python. Puedes cambiar Range a algo como drange o frange si quieres. La función "Rango" se comporta exactamente como lo desea. Puede consultar su manual aquí [ http://reference.wolfram.com/language/ref/Range.html ].