Existe-t-il un moyen de passer de 0 à 1 par 0,1?
Je pensais pouvoir le faire comme suit, mais cela a échoué:
for i in range(0, 1, 0.1):
print i
Au lieu de cela, il est dit que l'argument step ne peut pas être zéro, ce à quoi je ne m'attendais pas.
Plutôt que d'utiliser directement une étape décimale, il est beaucoup plus prudent de l'exprimer en termes de nombre de points. Sinon, une erreur d'arrondi en virgule flottante risque de vous donner un résultat erroné.
Vous pouvez utiliser la fonction linspace
de la bibliothèque NumPy (qui ne fait pas partie de la bibliothèque standard mais qui est relativement facile à obtenir). linspace
prend plusieurs points à renvoyer et vous permet également de spécifier si vous souhaitez ou non inclure le bon point de terminaison:
>>> 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 vous voulez vraiment utiliser une valeur de pas à virgule flottante, vous pouvez, avec 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])
Erreur d’arrondi en virgule flottante will poser des problèmes. Voici un cas simple où une erreur d’arrondi fait que arange
produit un tableau de longueur 4 alors qu’il ne devrait produire que 3 nombres:
>>> numpy.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])
La gamme de Python () peut seulement faire des entiers, pas de virgule flottante. Dans votre cas spécifique, vous pouvez utiliser une liste de compréhension:
[x * 0.1 for x in range(0, 10)]
(Remplacez l'appel à interpréter par cette expression.)
Pour le cas plus général, vous pouvez écrire une fonction personnalisée ou un générateur.
En vous appuyant sur 'xrange ([start], stop [ step])' , vous pouvez définir un générateur qui accepte et produit le type de votre choix (respectez les types supportant +
et <
):
>>> 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']
>>>
Augmentez la magnitude de i
pour la boucle, puis réduisez-la lorsque vous en avez besoin.
for i * 100 in range(0, 100, 10):
print i / 100.0
EDIT: Honnêtement, je ne me souviens plus pourquoi je pensais que cela fonctionnerait syntaxiquement
for i in range(0, 11, 1):
print i / 10.0
Cela devrait avoir la sortie souhaitée.
scipy
a une fonction intégrée arange
qui généralise le constructeur range()
de Python pour répondre à vos besoins en matière de traitement des flottants.
from scipy import arange
NumPy est un peu exagéré, je pense.
[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 règle générale, vous feriez une étape-à -1/x
jusqu'à y
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
a produit moins de bruit d’arrondi lors de l’essai).
Semblable à la fonction Rseq
, celle-ci retourne une séquence dans n'importe quel ordre en fonction de la valeur de pas correcte. La dernière valeur est égale à la valeur d'arrêt.
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([])
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 ]
La fonction intégrée range () renvoie une séquence de valeurs entières, je le crains donc vous ne pouvez pas l'utiliser pour effectuer un nombre décimal.
Je dirais simplement utiliser une boucle while:
i = 0.0
while i <= 1.0:
print i
i += 0.1
Si vous êtes curieux, Python convertit votre 0.1 en 0, raison pour laquelle il vous dit que l’argument ne peut être nul.
Voici une solution utilisant itertools :
import itertools
def seq(start, end, step):
if step == 0:
raise ValueError("step must not be 0")
sample_count = int(abs(end - start) / step)
return itertools.islice(itertools.count(start, step), sample_count)
Exemple d'utilisation:
for i in seq(0, 1, 0.1):
print(i)
import numpy as np
for i in np.arange(0, 1, 0.1):
print i
[x * 0.1 for x in range(0, 10)]
en Python 2.7x vous donne le résultat de:
[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]
mais si vous utilisez:
[ round(x * 0.1, 1) for x in range(0, 10)]
vous donne le désir:
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
Et si vous faites cela souvent, vous voudrez peut-être sauvegarder la liste générée r
r=map(lambda x: x/10.0,range(0,10))
for i in r:
print i
Ceci est ma solution pour obtenir des gammes avec des étapes flottantes .
En utilisant cette fonction, il n'est pas nécessaire d'importer numpy, ni de l'installer .
Je suis à peu près sûr que cela pourrait être amélioré et optimisé. N'hésitez pas à le faire et à le poster ici.
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 sortie est:
[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]
Mes versions utilisent la fonction de plage d'origine pour créer des index multiplicatifs pour le décalage. Cela permet la même syntaxe à la fonction de plage d'origine . J'ai créé deux versions, l'une utilisant float et l'autre utilisant Decimal, car j'ai constaté que dans certains cas, je voulais éviter la dérive d'arrondi introduite par l'arithmétique à virgule flottante.
Il est cohérent avec les résultats d'ensemble vides comme dans range/xrange.
Si vous ne transmettez qu'une seule valeur numérique à l'une ou l'autre fonction, la sortie de plage standard sera renvoyée à la valeur de plafond entière du paramètre d'entrée (ainsi, si vous lui attribuez la valeur 5.5, elle renvoie la plage (6).)
Edit: le code ci-dessous est maintenant disponible en paquet sur 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)) # []
more_itertools
est une bibliothèque tierce qui implémente un outil numeric_range
:
import more_itertools as mit
for x in mit.numeric_range(0, 1, 0.1):
print("{:.1f}".format(x))
Sortie
0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
Ma réponse est semblable à celle des autres utilisateurs de map (), sans NumPy et sans lambda (bien que vous puissiez). Pour obtenir une liste des valeurs flottantes comprises entre 0.0 et t_max par étapes de dt:
def xdt(n):
return dt*float(n)
tlist = map(xdt, range(int(t_max/dt)+1))
Vous pouvez utiliser cette fonction:
def frange(start,end,step):
return map(lambda x: x*step, range(int(start*1./step),int(end*1./step)))
Pour compléter la boutique, une solution fonctionnelle:
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)
Le truc pour éviter le problème de l'arrondi consiste à utiliser un numéro distinct pour se déplacer dans la plage, qui commence et la moitié du step devant start.
# floating point range
def frange(a, b, stp=1.0):
i = a+stp/2.0
while i<b:
yield a
a += stp
i += stp
Vous pouvez également utiliser numpy.arange
.
Cela peut être fait en utilisant la bibliothèque Numpy. La fonction arange () autorise les étapes dans float. Mais, il renvoie un tableau numpy qui peut être converti en liste en utilisant tolist () pour notre commodité.
for i in np.arange(0, 1, 0.1).tolist():
print i
Pour contrer les problèmes de précision float, vous pouvez utiliser le module Decimal
.
Cela nécessite un effort supplémentaire pour convertir Decimal
de int
ou float
lors de l'écriture du code, mais vous pouvez plutôt passer str
et modifier la fonction si ce type de commodité est vraiment nécessaire.
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
Exemples de sortie -
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')]
Ajoutez une correction automatique pour la possibilité d'une étape de connexion incorrecte:
def frange(start,step,stop):
step *= 2*((stop>start)^(step<0))-1
return [start+i*step for i in range(int((stop-start)/step))]
Surpris, personne n’a encore mentionné la solution recommandée dans les documents Python 3 :
Voir également:
- La recette linspace montre comment implémenter une version paresseuse de la plage qui convient aux applications en virgule flottante.
Une fois définie, la recette est facile à utiliser et ne nécessite pas numpy
ni aucune autre bibliothèque externe, mais des fonctions telles que numpy.linspace()
. Notez qu'au lieu d'un argument step
, le troisième argument num
spécifie le nombre de valeurs souhaitées, par exemple:
print(linspace(0, 10, 5))
# linspace(0, 10, 5)
print(list(linspace(0, 10, 5)))
# [0.0, 2.5, 5.0, 7.5, 10]
Je cite une version modifiée de la recette complète de Python 3 d'Andrew Barnert ci-dessous:
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))
Meilleure solution: pas d'erreur d'arrondi
_______________________________________________________________________________________
>>> 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]
_______________________________________________________________________________
Ou, pour une plage définie au lieu de points de données définis (par exemple une fonction continue), utilisez:
>>> 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]
Pour implémenter une fonction: remplacez x / pow(step, -1)
par f( x / pow(step, -1) )
et définissez f
.
Par exemple:
>>> 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]
Ma solution:
def seq(start, stop, step=1, digit=0):
x = float(start)
v = []
while x <= stop:
v.append(round(x,digit))
x += step
return v
start et stop sont inclusifs plutôt que l'un ou l'autre (généralement stop est exclu) et sans importation, et en utilisant des générateurs
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 juillet 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]
Je sais que je suis en retard à la fête ici, mais voici une solution de générateur triviale qui fonctionne dans la version 3.6:
def floatRange(*args):
start, step = 0, 1
if len(args) == 1:
stop = args[0]
Elif len(args) == 2:
start, stop = args[0], args[1]
Elif len(args) == 3:
start, stop, step = args[0], args[1], args[2]
else:
raise TypeError("floatRange accepts 1, 2, or 3 arguments. ({0} given)".format(len(args)))
for num in start, step, stop:
if not isinstance(num, (int, float)):
raise TypeError("floatRange only accepts float and integer arguments. ({0} : {1} given)".format(type(num), str(num)))
for x in range(int((stop-start)/step)):
yield start + (x * step)
return
alors vous pouvez l'appeler comme le range()
... d'origine, il n'y a pas de traitement d'erreur, mais laissez-moi savoir s'il y a une erreur qui peut être raisonnablement interceptée, et je mettrai à jour. ou vous pouvez le mettre à jour. c'est StackOverflow.
Je ne suis qu'un débutant, mais j'ai eu le même problème lors de la simulation de certains calculs. Voici comment j'ai essayé de résoudre ce problème, qui semble fonctionner avec des pas décimaux.
Je suis aussi assez paresseux et j'ai donc eu du mal à écrire ma propre fonction de plage.
Fondamentalement, ce que j'ai fait est de changer ma xrange(0.0, 1.0, 0.01)
en xrange(0, 100, 1)
et d’utiliser la division par 100.0
dans la boucle . Je craignais également les erreurs d’arrondi. J'ai donc décidé de vérifier s'il y en avait. Maintenant, j'ai entendu dire que si, par exemple, 0.01
issu d'un calcul n'était pas exactement le float 0.01
qui les comparait devrait renvoyer False (si je me trompe, merci de me le faire savoir).
J'ai donc décidé de tester si ma solution fonctionnerait pour ma gamme en effectuant un court test:
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
Et cela imprimé vrai pour chacun.
Maintenant, si je me trompe totalement, faites-le-moi savoir.
frange (démarrer, arrêter, précision)
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]
Beaucoup de solutions ici présentaient encore des erreurs en virgule flottante dans Python 3.6 et ne faisaient pas exactement ce dont j'avais personnellement besoin.
La fonction ci-dessous prend des entiers ou des flottants, ne nécessite pas d'importations et ne renvoie pas d'erreurs en virgule flottante.
def frange(x, y, step):
if int(x + y + step) == (x + y + step):
r = list(range(int(x), int(y), int(step)))
else:
f = 10 ** (len(str(step)) - str(step).find('.') - 1)
rf = list(range(int(x * f), int(y * f), int(step * f)))
r = [i / f for i in rf]
return r
sign = lambda x: (1, -1)[x < 0]
def frange(start, stop, step):
i = 0
r=len(str(step).split('.')[-1])
args=(start,stop,step)
if not step :return []
if all(int(i)==float(i) for i in args):
start,stop,step=map(int,args)
if sign(step)==1:
while start + i * step < stop:
yield round(start + i * step,r)
i += 1
else:
while start + i * step > stop:
yield round(start + i * step,r)
i += 1
Voici ma solution qui fonctionne bien avec float_range (-1, 0, 0.01) et fonctionne sans erreur de représentation en virgule flottante Ce n'est pas très rapide, mais ça fonctionne 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]
Cette doublure n'encombrera pas votre code. Le signe du paramètre step est important.
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)]