197 votes

range() pour les flottants

Y a-t-il un range() équivalent pour les flottants 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

2 votes

Ce ne sont pas des fractions mais des flottants. Et les flottants sont... susceptibles de donner des résultats différents de ceux auxquels vous vous attendez.

8 votes

Une solution rapide serait de traiter les entiers comme des décimales, par exemple : range(5, 50, 5) et ensuite diviser chaque nombre par 10.

0 votes

@delnan - mis à jour. Je suis prêt à accepter des inexactitudes infimes pour la commodité d'avoir une plage de valeurs flottantes.

144voto

Xaerxess Points 10324

Vous pouvez soit utiliser :

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

ou utiliser lambda / map :

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

1 votes

Et array(range(5,50,15)) / 10.0 car les tableaux numpy ont des opérateurs pour gérer la division, la multiplication, etc.

2 votes

@edvaldig : vous avez raison, je n'étais pas au courant de cela.... Néanmoins je pense arange(0.5, 5, 1.5) est, selon l'OMI, plus lisible.

2 votes

Je préfère cette réponse à celle acceptée, car les deux premières solutions présentées sont basées sur l'itération sur des entiers et la dérivation des flottants finaux à partir des entiers. Cette solution est plus robuste. Si vous le faites directement avec des flottants, vous risquez d'avoir d'étranges erreurs ponctuelles dues à la façon dont les flottants sont représentés en interne. Par exemple, si vous essayez list(frange(0, 1, 0.5)) il fonctionne bien et 1 est exclu, mais si vous essayez list(frange(0, 1, 0.1)) la dernière valeur que vous obtenez est proche de 1.0, ce qui n'est probablement pas ce que vous voulez. Les solutions présentées ici n'ont pas ce problème.

107voto

kichik Points 7249

~~Je ne connais pas de fonction intégrée, mais en écrire une comme [this](https://stackoverflow.com/a/477610/623735) ne devrait pas être trop compliqué.

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

Comme le mentionnent les commentaires, cela pourrait produire des résultats imprévisibles, comme par exemple :

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

Pour obtenir le résultat attendu, vous pouvez utiliser l'une des autres réponses à cette question, ou comme @Tadhg l'a mentionné, vous pouvez utiliser decimal.Decimal comme le jump argument. Veillez à l'initialiser avec une chaîne de caractères plutôt qu'avec un flottant.

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

Ou même :

import decimal

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

Et puis :

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

[Si vous n'utilisez que des mots positifs jump et un démarrage et un arrêt entiers ( x y y ), cela fonctionne bien. Pour une solution plus générale, voir aquí .]

37 votes

La devise de Python est en fait Il devrait y avoir une - et de préférence une seule - façon évidente de le faire. . Mais Python est quand même génial :)

3 votes

>>> print list(frange(0,100,0.1))[-1]==100.0 sera False

1 votes

frange peut fonctionner de manière inattendue. En raison de la la malédiction de l'arithmétique à virgule flottante par exemple frange(0.0, 1.0, 0.1) donne 11 valeurs, où la dernière valeur est 0.9999999999999999 . Une amélioration pratique serait while x + sys.float_info.epsilon < y: bien que même cela peut probablement échouer avec de grands nombres .

101voto

wim Points 35274

J'avais l'habitude d'utiliser numpy.arange mais j'ai eu quelques complications pour contrôler le nombre d'éléments qu'il renvoie, en raison d'erreurs de virgule flottante. J'utilise donc maintenant linspace par exemple :

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

0 votes

Il y a toujours des erreurs de virgule flottante, sans l'utilisation de decimal par exemple : np.linspace(-.1,10,num=5050)[0]

2 votes

@TNT Non, ce n'est pas une erreur. Vous trouverez np.linspace(-.1,10,num=5050)[0] == -.1 est vrai. C'est juste que le repr(np.float64('-0.1')) montre plus de chiffres.

2 votes

Bien que cet exemple particulier ne montre aucune erreur d'arrondi excessive, il existe des cas d'échec. Par exemple, print(numpy.linspace(0, 3, 148)[49]) imprime 0.9999999999999999 alors que le résultat idéal serait 1.0 . linspace fait un bien meilleur travail que arange mais il n'est pas garanti qu'elle produise l'erreur d'arrondi la plus faible possible.

41voto

Pat Points 4681

Pylab a frange (une enveloppe, en fait, pour 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. ])

5 votes

Frange est déprécié depuis la version 2.2 de matplotlib. numpy.arange devrait être utilisé.

14voto

Karl Knechtel Points 24349

Évalué avec impatience (2.x range ):

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

évalué paresseusement (2.x xrange , 3.x range ):

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

Alternativement :

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

5 votes

+1 ; mais pourquoi pas (x * .5 for x in range(10)) comme une expression génératrice pour une évaluation paresseuse ?

2 votes

Parce que ce serait trop facile, je suppose ? :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X