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