¿Hay 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
range(5, 50, 5)
y luego dividir cada número entre 10.Respuestas:
No conozco una función incorporada, pero escribir una como esta no debería ser demasiado complicado.Como mencionan los comentarios, esto podría producir resultados impredecibles como:
Para obtener el resultado esperado, puede usar una de las otras respuestas en esta pregunta, o como mencionó @Tadhg, puede usarlo
decimal.Decimal
comojump
argumento. Asegúrese de inicializarlo con una cadena en lugar de un flotador.O incluso:
Y entonces:
fuente
>>> print list(frange(0,100,0.1))[-1]==100.0
seráFalse
frange
puede funcionar inesperadamente Debido a la maldición de la aritmética de coma flotante , por ejemplo,frange(0.0, 1.0, 0.1)
arroja 11 valores, donde está el último valor0.9999999999999999
. Una mejora práctica seríawhile x + sys.float_info.epsilon < y:
aunque incluso esto probablemente pueda fallar con grandes números .decimal.Decimal
como paso en lugar de flotadores.Puedes usar:
o use lambda / map:
fuente
arange(0.5, 5, 1.5)
es IMO más legible.list(frange(0, 1, 0.5))
, funciona bien y se excluye 1, pero si lo intentaslist(frange(0, 1, 0.1))
, el último valor que obtienes está cerca de 1.0, que probablemente no sea lo que deseas. Las soluciones presentadas aquí no tienen este problema.Solía usar,
numpy.arange
pero tuve algunas complicaciones para controlar la cantidad de elementos que devuelve, debido a errores de coma flotante. Entonces ahora usolinspace
, por ejemplo:fuente
decimal
, por ejemplo:np.linspace(-.1,10,num=5050)[0]
np.linspace(-.1,10,num=5050)[0] == -.1
es cierto. Es solo querepr(np.float64('-0.1'))
muestra más dígitos.print(numpy.linspace(0, 3, 148)[49])
imprime0.9999999999999999
cuándo sería el resultado ideal1.0
.linspace
hace un trabajo mucho mejor quearange
, pero no se garantiza que produzca el mínimo error de redondeo posible.Pylab tiene
frange
(un contenedor, en realidad, paramatplotlib.mlab.frange
):fuente
Evaluado ansiosamente (2.x
range
):Evaluado perezosamente (2.x
xrange
, 3.xrange
):Alternativamente:
fuente
(x * .5 for x in range(10))
como una expresión generadora para una evaluación perezosa?utilizando
itertools
: rango de punto flotante evaluado perezosamente:fuente
itertools.takewhile
. Sin embargo,itertools.count(start, step)
sufre de errores de coma flotante acumulados. (Evalúetakewhile(lambda x: x < 100, count(0, 0.1))
por ejemplo). Escribiría en sutakewhile(lambda x: x < stop, (start + i * step for i in count()))
lugar.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 funciones incorporado, pero puede manejar tipos flotantes, decimales y fracciones.fuente
No existe tal función incorporada, pero puede usar lo siguiente (código Python 3) para hacer el trabajo tan seguro como Python le permite.
Puede verificarlo todo ejecutando algunas afirmaciones:
Código disponible en GitHub
fuente
¿Por qué no hay implementación de rango de punto flotante en la biblioteca estándar?
Como lo aclaran todas las publicaciones aquí, no hay una versión de coma flotante
range()
. Dicho esto, la omisión tiene sentido si consideramos que larange()
función se usa a menudo como un generador de índice (y, por supuesto, eso significa un accesor ). Entonces, cuando llamamosrange(0,40)
, en efecto estamos diciendo que queremos 40 valores que comiencen en 0, hasta 40, pero que no incluyan 40 en sí.Cuando consideramos que la generación de índices tiene tanto que ver con el número de índices como con sus valores, el uso de una implementación flotante
range()
en la biblioteca estándar tiene menos sentido. Por ejemplo, si llamamos a la funciónfrange(0, 10, 0.25)
, esperaríamos que se incluyan tanto 0 como 10, pero eso produciría un vector con 41 valores.Por lo tanto, una
frange()
función que depende de su uso siempre exhibirá un comportamiento contrario a la intuición; o tiene demasiados valores como se perciben desde la perspectiva de indexación o no incluye un número que razonablemente debería ser devuelto 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:El caso de uso de indexación
Y para la perspectiva de indexación, he escrito un enfoque ligeramente diferente con un poco de magia con cuerdas engañosa que nos permite especificar el número de lugares decimales.
Del mismo modo, también podemos usar la
round
función incorporada y especificar el número de decimales:Una comparación rápida y rendimiento
Por supuesto, dada la discusión anterior, estas funciones tienen un caso de uso bastante limitado. Sin embargo, aquí hay una comparación rápida:
Los resultados son idénticos para cada uno:
Y algunos horarios:
Parece que el método de formato de cadena 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:
Además, cuando el
skip
parámetro no es divisible por elstop
valor, puede haber una brecha enorme debido al último problema:Hay formas de abordar este problema, pero al final del día, el mejor enfoque probablemente sería simplemente usar Numpy.
fuente
Kichik proporcionó una solución sin dependencias numpy, etc., pero debido a la aritmética de coma flotante , a menudo se comporta inesperadamente. Como noté yo y blubberdiblub , elementos adicionales se infiltran fácilmente en el resultado. Por ejemplo,
naive_frange(0.0, 1.0, 0.1)
rendiría0.999...
como su último valor y, por lo tanto, arrojaría 11 valores en total.Aquí se proporciona una versión robusta:
Debido a la multiplicación, los errores de redondeo no se acumulan. El uso de
epsilon
se encarga del posible error de redondeo de la multiplicación, aunque, por supuesto, los problemas pueden surgir en los extremos muy pequeños y muy grandes. Ahora, como se esperaba:Y con números algo mayores:
El código también está disponible como GitHub Gist .
fuente
Una versión más simple sin biblioteca
Aw, diablos: agregaré una versión simple sin biblioteca. Siéntase libre de mejorarlo [*]:
La idea central es que
nsteps
es el número de pasos que lo llevan de principio a fin yrange(nsteps)
siempre emite enteros para que no haya pérdida de precisión. El paso final es mapear [0..nsteps] linealmente en [start..stop].editar
Si, como alancalvitti, desea que la serie tenga una representación racional exacta, siempre puede usar Fracciones :
[*] En particular,
frange()
devuelve una lista, no un generador. Pero fue suficiente para mis necesidades.fuente
frange(0,1.1,0.1)
y aún más de aquellos con una opción comofrange(0,1.05,0.1)
Nota 1: De la discusión en la sección de comentarios aquí, "nunca use
numpy.arange()
(la documentación numpy en sí misma recomienda no hacerlo). Use numpy.linspace como lo recomienda wim, o una de las otras sugerencias en esta respuesta"Nota 2: He leído la discusión en algunos comentarios aquí, pero después de volver a esta pregunta por tercera vez, siento que esta información debería colocarse en una posición más legible.
fuente
Como escribió Kichik , esto no debería ser demasiado complicado. Sin embargo este código:
Es inapropiado debido al efecto acumulativo de los errores al trabajar con flotadores. Por eso recibes algo como:
Si bien el comportamiento esperado sería:
Solución 1
El error acumulativo simplemente se puede reducir mediante el uso de una variable de índice. Aquí está el ejemplo:
Este ejemplo funciona como se esperaba.
Solución 2
No hay funciones anidadas. Solo un tiempo y una variable de contador:
Esta función también funcionará bien, excepto en los casos en que desee el rango invertido. P.ej:
La solución 1 en este caso funcionará como se esperaba. Para que esta función funcione en tales situaciones, debe aplicar un truco, similar al siguiente:
Con este truco puedes usar estas funciones con pasos negativos:
Solución 3
Puede ir aún más lejos con la biblioteca estándar simple y componer una función de rango para la mayoría de los tipos numéricos:
Este generador está adaptado del libro Fluent Python (Capítulo 14. Iterables, iteradores y generadores). No funcionará con rangos decrecientes. Debe aplicar un truco, como en la solución anterior.
Puede usar este generador de la siguiente manera, por ejemplo:
Y, por supuesto, también puede usarlo con float e int .
Ten cuidado
Si desea utilizar estas funciones con pasos negativos, debe agregar una marca para el signo de paso, por ejemplo:
La mejor opción aquí es subir
StopIteration
, si quieres imitar larange
función en sí.Rango mimético
Si desea imitar la
range
interfaz de la función, puede proporcionar algunas comprobaciones de argumentos:Creo que tienes el punto. Puede utilizar cualquiera de estas funciones (excepto la primera) y todo lo que necesita para ellas es la biblioteca estándar de Python.
fuente
Escribí una función que devuelve una tupla de un rango de números de coma flotante de doble precisión sin decimales 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 una interfaz de usuario. Espero que alguien más lo encuentre útil.
fuente
Uso
Para redondear cada paso a N decimales
Código
¿Por qué elegir esta respuesta?
np.linspace
aciertos y errores pueden funcionar o no debido a la dificultad de elegir el número correcto de divisiones.np.linspace
realmente lucha con incrementos decimales de 0.1, y el orden de las divisiones en la fórmula para convertir el incremento en una serie de divisiones puede resultar en un código correcto o roto.np.arange
están en desuso.En caso de duda, pruebe los cuatro casos de prueba anteriores.
fuente
Tenga en cuenta que la primera letra de Range es mayúscula. Este método de denominación no se recomienda para funciones en Python. Puede cambiar el rango a algo como drange o frange si lo desea. La función "Rango" se comporta tal como lo desea. Puede consultar su manual aquí [ http://reference.wolfram.com/language/ref/Range.html ].
fuente
Creo que hay una respuesta muy simple que realmente emula todas las características de rango, pero tanto para flotante como para entero. En esta solución, solo supone que su aproximación por defecto es 1e-7 (o la que elija) y puede cambiarla cuando llame a la función.
fuente
Por supuesto, habrá algunos errores de redondeo, por lo que esto no es perfecto, pero esto es lo que uso generalmente para aplicaciones, que no requieren una alta precisión. Si desea que esto sea más preciso, puede agregar un argumento adicional para especificar cómo manejar los errores de redondeo. Quizás pasar una función de redondeo podría hacerlo extensible y permitirle al programador especificar cómo manejar los errores de redondeo.
Si escribo
Producirá:
fuente
¿Hay un rango () equivalente para flotadores en Python? NO use esto:
fuente
f_range(0.01,0.02,0.001)
... Para los propósitos más prácticos,arange
de Numpy es una solución simple, segura y rápida.Aquí hay varias respuestas que no manejan casos extremos simples como paso negativo, inicio incorrecto, detención, etc. Aquí está la versión que maneja muchos de estos casos correctamente dando el mismo comportamiento que el nativo
range()
:Tenga en cuenta que esto daría un error en el paso = 0 al igual que nativo
range
. Una diferencia es que el rango nativo devuelve un objeto que es indexable y reversible, mientras que el anterior no lo hace.Puedes jugar con este código y probar casos aquí.
fuente