Existe-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
range(5, 50, 5)
Réponses:
Je ne connais pas de fonction intégrée, mais en écrire une comme celle- ci ne devrait pas être trop compliqué.Comme le mentionnent les commentaires, cela pourrait produire des résultats imprévisibles tels que:
Pour obtenir le résultat attendu, vous pouvez utiliser l'une des autres réponses de cette question, ou comme @Tadhg l'a mentionné, vous pouvez utiliser
decimal.Decimal
commejump
argument. Assurez-vous de l'initialiser avec une chaîne plutôt qu'un flottant.Ou même:
Puis:
la source
>>> print list(frange(0,100,0.1))[-1]==100.0
seraFalse
frange
peut fonctionner de manière inattendue. En raison de la malédiction de l'arithmétique en virgule flottante , par exemplefrange(0.0, 1.0, 0.1)
donne 11 valeurs, où se trouve la dernière valeur0.9999999999999999
. Une amélioration pratique seraitwhile x + sys.float_info.epsilon < y:
même si cela peut probablement échouer avec de grands nombres .decimal.Decimal
comme étape au lieu de flotteurs.Vous pouvez soit utiliser:
ou utilisez lambda / map:
la source
arange(0.5, 5, 1.5)
OMI est plus lisible.list(frange(0, 1, 0.5))
, cela fonctionne bien et 1 est exclu, mais si vous essayezlist(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.J'avais l'habitude de l'utiliser
numpy.arange
mais j'ai eu quelques complications pour contrôler le nombre d'éléments qu'il renvoie, en raison d'erreurs en virgule flottante. Alors maintenantlinspace
, j'utilise , par exemple:la source
decimal
, par exemple:np.linspace(-.1,10,num=5050)[0]
np.linspace(-.1,10,num=5050)[0] == -.1
c'est vrai. C'est juste que lerepr(np.float64('-0.1'))
montre plus de chiffres.print(numpy.linspace(0, 3, 148)[49])
imprime0.9999999999999999
lorsque le résultat idéal serait1.0
.linspace
fait un bien meilleur travail quearange
, mais il n'est pas garanti de produire le minimum d'erreur d'arrondi possible.Pylab a
frange
(un wrapper, en fait, pourmatplotlib.mlab.frange
):la source
Évalué avec impatience (2.x
range
):Évalué paresseusement (2.x
xrange
, 3.xrange
):Alternativement:
la source
(x * .5 for x in range(10))
tant qu'expression génératrice pour une évaluation paresseuse?en utilisant
itertools
: plage de virgule flottante évaluée paresseusement:la source
itertools.takewhile
. Cependant,itertools.count(start, step)
souffre d'erreurs en virgule flottante accumulées. (Évaluertakewhile(lambda x: x < 100, count(0, 0.1))
par exemple.) J'écrirais à latakewhile(lambda x: x < stop, (start + i * step for i in count()))
place.J'ai aidé à ajouter la fonction numeric_range au package more-itertools .
more_itertools.numeric_range(start, stop, step)
agit comme la plage de fonctions intégrée mais peut gérer les types floats, Decimal et Fraction.la source
Il n'existe pas de fonction intégrée de ce type, mais vous pouvez utiliser ce qui suit (code Python 3) pour effectuer le travail de la manière la plus sûre que Python vous permet.
Vous pouvez tout vérifier en exécutant quelques assertions:
Code disponible sur GitHub
la source
Pourquoi n'y a-t-il pas d'implémentation de plage de virgule flottante dans la bibliothèque standard?
Comme le montrent tous les articles ici, il n'y a pas de version en virgule flottante de
range()
. Cela dit, l'omission a du sens si l'on considère que larange()
fonction est souvent utilisée comme un générateur d' index (et bien sûr, cela signifie un accesseur ). Ainsi, lorsque nous appelonsrange(0,40)
, nous disons en fait que nous voulons 40 valeurs commençant à 0, jusqu'à 40, mais non compris 40 lui-même.Lorsque nous considérons que la génération d'index concerne autant le nombre d'indices que leurs valeurs, l'utilisation d'une implémentation float de
range()
dans la bibliothèque standard a moins de sens. Par exemple, si nous appelions la fonctionfrange(0, 10, 0.25)
, nous nous attendrions à ce que 0 et 10 soient inclus, mais cela donnerait un vecteur avec 41 valeurs.Ainsi, une
frange()
fonction dépendant de son utilisation présentera toujours un comportement contre-intuitif; il a trop de valeurs perçues du point de vue de l'indexation ou ne comprend pas un nombre qui devrait raisonnablement être renvoyé du point de vue mathématique.Le cas d'utilisation mathématique
Cela dit, comme discuté,
numpy.linspace()
effectue bien la génération avec la perspective mathématique:Le cas d'utilisation de l'indexation
Et pour la perspective de l'indexation, j'ai écrit une approche légèrement différente avec une magie de chaîne astucieuse qui nous permet de spécifier le nombre de décimales.
De même, nous pouvons également utiliser la
round
fonction intégrée et spécifier le nombre de décimales:Une comparaison rapide et des performances
Bien entendu, compte tenu de la discussion ci-dessus, ces fonctions ont un cas d'utilisation assez limité. Néanmoins, voici une comparaison rapide:
Les résultats sont identiques pour chacun:
Et quelques horaires:
On dirait que la méthode de formatage de chaîne gagne par un cheveu sur mon système.
Les limites
Et enfin, une démonstration du point de la discussion ci-dessus et une dernière limitation:
De plus, lorsque le
skip
paramètre n'est pas divisible par lastop
valeur, il peut y avoir un écart béant compte tenu de ce dernier problème:Il existe des moyens de résoudre ce problème, mais en fin de compte, la meilleure approche serait probablement d'utiliser simplement Numpy.
la source
Une solution sans dépendances numpy etc a été fournie par kichik mais en raison de l'arithmétique en virgule flottante , elle se comporte souvent de manière inattendue. Comme je l' ai noté et blubberdiblub , des éléments supplémentaires se faufilent facilement dans le résultat. Par exemple,
naive_frange(0.0, 1.0, 0.1)
il donnerait0.999...
comme dernière valeur et donnerait ainsi 11 valeurs au total.Une version robuste est fournie ici:
Parce que la multiplication, les erreurs d'arrondi ne s'accumulent pas. L'utilisation de
epsilon
prend en compte les éventuelles erreurs d'arrondi de la multiplication, même si des problèmes peuvent bien sûr surgir dans les très petites et très grandes extrémités. Maintenant, comme prévu:Et avec des nombres un peu plus grands:
Le code est également disponible sous forme de GitHub Gist .
la source
Une version plus simple sans bibliothèque
Oh, diable - je vais jeter dans une version simple sans bibliothèque. N'hésitez pas à l'améliorer [*]:
L'idée principale est que
nsteps
c'est le nombre d'étapes pour vous amener du début à la fin etrange(nsteps)
émet toujours des entiers afin qu'il n'y ait pas de perte de précision. La dernière étape consiste à mapper [0..nsteps] linéairement sur [start..stop].Éditer
Si, comme alancalvitti, vous souhaitez que la série ait une représentation rationnelle exacte, vous pouvez toujours utiliser des fractions :
[*] En particulier,
frange()
renvoie une liste, pas un générateur. Mais cela suffisait à mes besoins.la source
frange(0,1.1,0.1)
et encore plus de ceux avec un choix commefrange(0,1.05,0.1)
Remarque 1: D'après la discussion dans la section des commentaires ici, "ne jamais utiliser
numpy.arange()
(la documentation numpy elle-même le recommande contre). Utilisez numpy.linspace comme recommandé par wim, ou l'une des autres suggestions de cette réponse"Note 2: J'ai lu la discussion dans quelques commentaires ici, mais après être revenu sur cette question pour la troisième fois maintenant, je pense que cette information devrait être placée dans une position plus lisible.
la source
Comme l'a écrit Kichik , cela ne devrait pas être trop compliqué. Cependant ce code:
Est inapproprié en raison de l' effet cumulatif des erreurs lors de l'utilisation de flotteurs. C'est pourquoi vous recevez quelque chose comme:
Alors que le comportement attendu serait:
Solution 1
L'erreur cumulative peut simplement être réduite en utilisant une variable d'index. Voici l'exemple:
Cet exemple fonctionne comme prévu.
Solution 2
Aucune fonction imbriquée. Seulement un moment et une variable de compteur:
Cette fonction fonctionnera également bien, sauf dans les cas où vous souhaitez inverser la plage. Par exemple:
La solution 1 dans ce cas fonctionnera comme prévu. Pour que cette fonction fonctionne dans de telles situations, vous devez appliquer un hack, semblable à ce qui suit:
Avec ce hack, vous pouvez utiliser ces fonctions avec des étapes négatives:
Solution 3
Vous pouvez aller encore plus loin avec la bibliothèque standard simple et composer une fonction de plage pour la plupart des types numériques:
Ce générateur est adapté du livre Fluent Python (Chapitre 14. Itérables, itérateurs et générateurs). Cela ne fonctionnera pas avec des plages décroissantes. Vous devez appliquer un hack, comme dans la solution précédente.
Vous pouvez utiliser ce générateur comme suit, par exemple:
Et bien sûr, vous pouvez également l'utiliser avec float et int .
Faites attention
Si vous souhaitez utiliser ces fonctions avec des étapes négatives, vous devez ajouter une vérification pour le signe de l'étape, par exemple:
La meilleure option ici est d'augmenter
StopIteration
, si vous voulez imiter larange
fonction elle-même.Gamme Mimic
Si vous souhaitez imiter l'
range
interface de la fonction, vous pouvez fournir des vérifications d'argument:Je pense que vous avez compris. Vous pouvez utiliser l'une de ces fonctions (sauf la toute première) et tout ce dont vous avez besoin est une bibliothèque standard python.
la source
J'ai écrit une fonction qui renvoie un tuple d'une plage de nombres à virgule flottante double précision sans décimales au-delà des centièmes. il s'agissait simplement d'analyser les valeurs de plage telles que les chaînes et de séparer l'excédent. Je l'utilise pour afficher des plages à sélectionner dans une interface utilisateur. J'espère que quelqu'un d'autre le trouvera utile.
la source
Usage
Pour arrondir chaque pas à N décimales
Code
Pourquoi choisir cette réponse?
np.linspace
sont aléatoires, elles peuvent ou non fonctionner en raison de la difficulté à choisir le nombre correct de divisions.np.linspace
a vraiment du mal avec les incréments décimaux de 0,1, et l'ordre des divisions dans la formule pour convertir l'incrément en un certain nombre de fractionnements peut entraîner un code correct ou cassé.np.arange
sont obsolètes.En cas de doute, essayez les quatre cas de tests ci-dessus.
la source
Veuillez noter que la première lettre de Range est en majuscule. Cette méthode de dénomination n'est pas recommandée pour les fonctions en Python. Vous pouvez changer Range en quelque chose comme drange ou frange si vous le souhaitez. La fonction "Range" se comporte exactement comme vous le souhaitez. Vous pouvez consulter son manuel ici [ http://reference.wolfram.com/language/ref/Range.html ].
la source
Je pense qu'il existe une réponse très simple qui émule vraiment toutes les fonctionnalités de la plage, mais pour les nombres flottants et entiers. Dans cette solution, vous supposez simplement que votre approximation par défaut est 1e-7 (ou celle que vous choisissez) et vous pouvez la modifier lorsque vous appelez la fonction.
la source
Il y aura bien sûr des erreurs d'arrondi, donc ce n'est pas parfait, mais c'est ce que j'utilise généralement pour les applications qui ne nécessitent pas de haute précision. Si vous souhaitez rendre cela plus précis, vous pouvez ajouter un argument supplémentaire pour spécifier comment gérer les erreurs d'arrondi. Peut-être que passer une fonction d'arrondi pourrait rendre cette fonction extensible et permettre au programmeur de spécifier comment gérer les erreurs d'arrondi.
Si j'écris:
Il produira:
la source
Existe-t-il un équivalent range () pour les flottants en Python? NON Utilisez ceci:
la source
f_range(0.01,0.02,0.001)
... Pour des raisons pratiques,arange
de Numpy est une solution simple, sûre et rapide.Il a plusieurs réponses ici qui ne gère pas les cas de pointe simples comme pas négatif, mauvais départ, arrêt , etc. Voici la version qui gère de nombreux de ces cas donnent correctement même comportement que natif
range()
:Notez que cela entraînerait une erreur de step = 0 tout comme native
range
. Une différence est que la plage native renvoie un objet indexable et réversible alors que ce n'est pas le cas ci-dessus.Vous pouvez jouer avec ce code et des cas de test ici.
la source