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