Comment utiliser une valeur de pas de plage décimale ()?

744

Existe-t-il un moyen de passer de 0 à 1 de 0,1?

Je pensais que je pouvais le faire comme suit, mais cela a échoué:

for i in range(0, 1, 0.1):
    print i

Au lieu de cela, il dit que l'argument step ne peut pas être nul, ce à quoi je ne m'attendais pas.

Evan Fosmark
la source
17
int (0,1) == 0, donc le pas est en fait nul. Cela peut être inattendu, mais c'est zéro. Vous voudrez peut-être reformuler votre question pour refléter le fait que vous ne vous y attendiez pas. Dire «ce n'est pas» est faux et trompeur.
S.Lott
3
BTW Une doublure courte peut être enroulée en utilisant itertools.takewhileet itertools.count. Ce n'est pas mieux que pour les drangeperformances.
Kos
1
Il est gênant que la plage de python ne le permette pas, étant donné la facilité d'implémentation d'un générateur qui le fait même sans accumuler d'erreurs d'arrondi. Heck, même l' seqoutil dans GNU coreutils permet de se passer des seq 0 0.1 1erreurs d'arrondi!
josch
3
@josch: sequtilise le long doubletype C en interne et est sujet aux erreurs d'arrondi. Par exemple sur ma machine, seq 0 0.1 1donne 1comme dernière sortie (comme prévu), mais seq 1 0.1 2donne 1.9comme dernière sortie (plutôt que prévu 2).
Mark Dickinson
Pour plus de commodité, la suggestion de @ Kos peut être implémentée en tant que itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))ou itertools.islice(itertools.count(0,0.1), 10)(après vous l'avez import itertools), bien que je n'ai pas testé ce qui est plus efficace
Anonyme

Réponses:

906

Plutôt que d'utiliser directement un pas décimal, il est beaucoup plus sûr d'exprimer cela en termes de nombre de points que vous souhaitez. Sinon, une erreur d'arrondi à virgule flottante est susceptible de vous donner un mauvais résultat.

Vous pouvez utiliser la fonction linspace de la bibliothèque NumPy (qui ne fait pas partie de la bibliothèque standard mais est relativement facile à obtenir). linspaceprend un certain nombre de points à renvoyer et vous permet également de spécifier s'il faut 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 à virgule flottante va causer des problèmes, cependant. Voici un cas simple où une erreur d'arrondi provoque la arangeproduction d'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])
Andrew Jaffe
la source
51
numpy est un composant tellement omniprésent du python que je considère que cette réponse est la plus «pythonique» de toutes.
frappe aérienne le
21
@AndreTerra Le problème est que @ numpy @ est un package tiers et ajoute beaucoup de frais généraux en termes de gestion des dépendances, de stockage (pour le package lui-même), etc. Selon ce que fait le développeur, il peut être impossible à utiliser il.
rbaleksandar
Pardonnez-moi, mais je n'ai pas compris l'erreur d'arrondi à virgule flottante dans la dernière partie depuis np.linspace(1.,1.3,4)et np.arange(1.,1.3,0.1)donne exactement la même sortie
code mort
4
@deadcode La raison en est que np.arange est défini pour produire une plage [start,stop)(c'est-à-dire excluant stop), donc on ne s'attendrait pas à ce que 1.3 soit inclus dans la liste. Voir cette question pour savoir pourquoi elle est toujours incluse et que faire contre elle.
Dennis
5
La quantité de paquet utilisée n'est sans doute pas un indicateur de son caractère "Pythonique".
Alex Hall
213

Le range () de Python ne peut faire que des entiers, pas des virgules flottantes. Dans votre cas spécifique, vous pouvez utiliser une compréhension de liste à la place:

[x * 0.1 for x in range(0, 10)]

(Remplacez l'appel à range par cette expression.)

Pour le cas plus général, vous souhaiterez peut-être écrire une fonction ou un générateur personnalisé.


la source
36
Encore mieux, vous pouvez simplement utiliser une compréhension de générateur si vous travaillez avec Python 2.4+. (x * 0.1 for x in range(0, 10)).
JAB
42
Encore mieux, mettez à la x/10place de x * 0.1: D Rien de spécial en fait, mais certains chiffres seront plus précis, par exemple pour 3*0.1vous 0.30000000000000004, alors que pour 3/10 vous obtenez 0.3:)
Bloke
4
3/10 me donne 0, pas 0,3. 3 / 10,0 donne 0,2999999999999999999. Python 2.6.
19
@LarsWirzenius: en Python 2.2+, from __future__ import division; 3/10renvoie 0.3. Ce comportement est le comportement par défaut dans Python 3.x.
Benjamin Hodgson
2
la fonction round peut également être utilisée lst = [round (x * 0.10,2) pour x dans la plage (0,10)]
MARK
148

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 prenant en charge +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']
>>> 
gimel
la source
45
Cela a des problèmes d'arrondi. Veuillez regarder ici: code.activestate.com/recipes/66472
Christian Oudard
Je voudrais l'étendre un peu pour l'autre direction avec un (tout r> stop) et un r - = pas correspondant pour donner la direction opposée.
user318904
1
J'ai fait une fonction xfrange sans les problèmes de précision de flottement mentionnés ci-dessus. Vérifiez-le;) stackoverflow.com/questions/477486/…
Carlos Vega
1
Vous accumulez des erreurs d'arrondi. Veuillez utiliser ceci à la place: `i = 0; r = start tandis que r <stop: i + = 1; r = début + i * étape; yield r`
Cees Timmerman
1
Ceci provient de pythoncentral.io/pythons-range-function-explained (et d'autres sources de documentation Python)
Apostolos
31

Augmentez l'amplitude de ila 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 pas 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.

cmsjr
la source
Je pense que vous constaterez que range () fonctionne sur des entiers, auquel cas ce serait la seule solution, en utilisant au moins la même fonction.
Matthew Scharley
1
@cmsjr creative: D Juste une petite chose: divisez par 100,0 pour empêcher Python de tronquer le résultat si vous utilisez Python 2.x. Je pense qu'en 3.0, cela fonctionnera comme vous l'avez codé.
Dana
2
for i * 100 in range(0, 100, 10): SyntaxError: impossible d'attribuer à l'opérateur
Anne van Rossum
25

scipya une fonction intégrée arangequi généralise le range()constructeur de Python pour satisfaire votre exigence de gestion des flottants.

from scipy import arange

Catherine Ray
la source
8
C'est en fait exactement la même chose que arangevous pouvez trouver dans numpy: >>> import scipy >>> import numpy >>> numpy.arange is scipy.arangereviendra True.
iFreilicht
from nump import arange as range
shrewmouse
24

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]

De manière générale, faire un pas 1/xvers yvous ferait

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/xproduit moins de bruit d'arrondi lorsque j'ai testé).

Kalle
la source
18

Semblable à la seq fonction de R , celle-ci renvoie une séquence dans n'importe quel ordre étant donné 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([])

Résultats

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 ]

zeferino
la source
2
C'est une excellente réponse pour quelqu'un qui veut en obtenir un sans trop en python.
Chani
1
C'est presque ce que je cherchais - notez que cela seq(0.5, 3.0)revient [0.5, 1.5, 2.5, 3.5]. Pour éviter les entrées dernières étant hors de portée, remplacer n = int(round(...avec n = int(floor(...la ligne from math import flooren haut ( au- dessus def seq(...).
FriendFX
2
@FriendFX Ne faites pas ça! Si floorest utilisé, seq(0.2, 0.9, 0.1)ne parviendra pas à atteindre le point final droit et reviendra[0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7, 0.8]
fdermishin
@ user502144: Belle prise, merci. Je suppose que je dois me contenter d'une des solutions les plus complexes afin de la garder générale.
FriendFX
14

La fonction intégrée range () renvoie une séquence de valeurs entières, je le crains, vous ne pouvez donc pas l'utiliser pour faire un pas décimal.

Je dirais qu'il suffit d'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, c'est pourquoi il vous dit que l'argument ne peut pas être nul.

Dana
la source
2
Ne fais pas ça! Ajouter .110 fois n'est pas la même chose que d'ajouter 1! docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
cat
12

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)
Pramod
la source
Par souci d'exhaustivité, vous devez calculer la valeur absolue de la variable sample_count, de cette façon votre fonction fonctionnera également pour un démarrage négatif (c'est-à-dire de -10 à 10)
Deleteman
10
[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 souhaité:

[0,0, 0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7, 0,8, 0,9]

Nik
la source
5

Et si vous le faites souvent, vous voudrez peut-être enregistrer la liste générée r

r=map(lambda x: x/10.0,range(0,10))
for i in r:
    print i
RSabet
la source
5

more_itertoolsest une bibliothèque tierce qui implémente un numeric_rangeoutil:

import more_itertools as mit


for x in mit.numeric_range(0, 1, 0.1):
    print("{:.1f}".format(x))

Production

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Cet outil fonctionne également pour Decimalet Fraction.

pylang
la source
4

Mes versions utilisent la fonction de plage d'origine pour créer des indices multiplicatifs pour le décalage. Cela permet la même syntaxe à la fonction de plage d'origine. J'ai fait deux versions, une utilisant float et une utilisant Decimal, car j'ai trouvé que dans certains cas, je voulais éviter la dérive d'arrondi introduite par l'arithmétique à virgule flottante.

Il est cohérent avec des résultats d'ensemble vides comme dans range / xrange.

Le passage d'une seule valeur numérique à l'une ou l'autre fonction ramènera la sortie de la plage standard à la valeur plafond entière du paramètre d'entrée (donc si vous lui donniez 5.5, elle retournerait la plage (6).)

Edit: le code ci-dessous est désormais disponible en package 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)) # []
Nisan.H
la source
Comment peut frangefonctionner si l'arrêt est None? Cette partie du code ne prend même plus en compte la taille de l'étape.
josch
1
@josch rangea deux signatures :, range(stop)qui suppose une valeur par défaut start=0, step=1, et range(start, stop, step), où aucune hypothèse n'est faite. frangereflète cela. Lorsque vous utilisez la range(stop)signature, les deux frangeet drangecommencent à 0 et incrémentent de 1, de sorte que leur comportement est identique au range(stop)comportement normal avec un arrêt arrondi à l'entier le plus proche.
Nisan.H
4

Ceci est ma solution pour obtenir des plages avec des étapes flottantes.
En utilisant cette fonction, il n'est pas nécessaire d'importer numpy, ni de l'installer.
Je suis 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]
Carlos Vega
la source
1
Il y a une erreur avec la dernière valeur manquante si elle est à 1 pas de la valeur d'arrêt. ie xfrange (1,10,2) ne fait que 1,3,5,7, manque 9
Lobe
Pour référence et pour d'autres lecteurs, veuillez comparer cette implémentation à ce stackoverflow.com/a/477610/54964 . Cela ne semble pas avoir de gros problèmes de flottement.
Léo Léopold Hertz 준영
@carlosvega Pouvez-vous confirmer pourquoi Lobe obtient son résultat?
Léo Léopold Hertz 준영
3

Pour l'exhaustivité de 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)
Bijou Trouvaille
la source
2

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)))
user376536
la source
Ne semble pas fonctionner correctement, par exemplelist(frange(99.8, 100.1, 0.1)) => [99.7, 99.80000000000001, 99.9]
Shai Coleman
2

L'astuce pour éviter les problèmes d'arrondi consiste à utiliser un numéro distinct pour se déplacer dans la plage, qui commence et la moitié du pas avant le début .

# floating point range
def frange(a, b, stp=1.0):
  i = a+stp/2.0
  while i<b:
    yield a
    a += stp
    i += stp

Alternativement, numpy.arangepeut être utilisé.

wolfram77
la source
2

Cela peut être fait en utilisant la bibliothèque Numpy. La fonction arange () permet des étapes dans float. Mais, il retourne 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
user3654478
la source
2

Ma réponse est similaire à d'autres utilisant map (), sans avoir besoin de NumPy, et sans utiliser lambda (bien que vous puissiez). Pour obtenir une liste de valeurs flottantes de 0,0 à t_max par pas de dt:

def xdt(n):
    return dt*float(n)
tlist  = map(xdt, range(int(t_max/dt)+1))
Eric Myers
la source
2

Personne surpris 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 gamme adaptée aux applications à virgule flottante.

Une fois définie, la recette est facile à utiliser et ne nécessite numpyni aucune autre bibliothèque externe, mais fonctionne comme numpy.linspace(). Notez que plutôt qu'un stepargument, le troisième numargument 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 ci-dessous une version modifiée de la recette complète de Python 3 d'Andrew Barnert:

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))
Chris_Rands
la source
2

Pour contrer les problèmes de précision du flotteur, vous pouvez utiliser le Decimalmodule .

Cela nécessite un effort supplémentaire de conversion en ou Decimaldepuis l' écriture du code, mais vous pouvez à la place passer et modifier la fonction si ce genre de commodité est en effet nécessaire.intfloatstr

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

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')]
shad0w_wa1k3r
la source
2
Avec des Decimalentrées similaires , np.arangefonctionne de la même manière:np.arange(Decimal('-2.0'), Decimal('2.0'), Decimal('0.1'))
hpaulj
2
Oui, merci. Cependant, cela nécessiterait une bibliothèque externe (numpy).
shad0w_wa1k3r
1

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))]
BobH
la source
1

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
Jjen
la source
1

Meilleure solution: aucune 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]
Jason
la source
1

le démarrage et l'arrêt sont inclusifs plutôt que l'un ou l'autre (généralement l'arrêt est exclu) et sans importations, 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 bits (AMD64)]

Goran B.
la source
1

Je sais que je suis en retard à la fête ici, mais voici une solution de générateur triviale qui fonctionne en 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 l'original range()... il n'y a pas de gestion d'erreur, mais faites-moi savoir s'il y a une erreur qui peut être raisonnablement détectée, et je mettrai à jour. ou vous pouvez le mettre à jour. c'est StackOverflow.

David Culbreth
la source
0

Voici ma solution qui fonctionne très bien avec float_range (-1, 0, 0.01) et fonctionne sans erreurs de représentation en virgule flottante. Ce n'est pas très rapide, mais fonctionne très 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]
pymen
la source
0

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 étapes décimales.

Je suis également assez paresseux et j'ai donc eu du mal à écrire ma propre fonction de plage.

En fait ce que je l'ai fait est changé ma xrange(0.0, 1.0, 0.01)pour xrange(0, 100, 1)et utilisé par la division à l' 100.0intérieur de la boucle. J'étais également inquiet, s'il y aurait des erreurs d'arrondi. J'ai donc décidé de tester, s'il y en a. Maintenant, j'ai entendu dire que si, par exemple, à 0.01partir d'un calcul, le flotteur les 0.01comparant ne devait pas exactement retourner Faux (si je me trompe, faites-le moi savoir).

J'ai donc décidé de tester si ma solution fonctionnerait pour ma gamme en lançant 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 il a imprimé True pour chacun.

Maintenant, si je me trompe totalement, faites-le moi savoir.

user2836437
la source
0

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)]
Tjaart
la source