Question

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 indiqué que l'argument step ne peut pas être zéro, ce à quoi je ne m'attendais pas.

Était-ce utile?

La solution

Plutôt que d’utiliser directement une étape décimale, il est beaucoup plus prudent de l’exprimer en termes de nombre de points souhaités. 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 est relativement facile à obtenir). numpy.arange prend un certain nombre de 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 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])

Une erreur d’arrondi en virgule flottante causera cependant des problèmes. Voici un cas simple où une erreur d’arrondi a pour effet de <=> produire 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])

Autres conseils

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

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

(Remplace 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.

Construire sur 'xrange ([début], arrêt [, étape])' , 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']
>>> 

Augmentez la magnitude i de 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 désirée.

scipy possède une fonction intégrée arange qui généralise le constructeur range() de Python afin de répondre à vos besoins en matière de gestion des flottants.

from scipy import arange

NumPy est un peu excessif, 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, pour procéder étape par étape - 1/x jusqu'à y vous feriez

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]

(<=> produisait moins de bruit d'arrondi lors de mes tests).

Semblable à la fonction seq 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]

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 une étape décimale.

Je dirais simplement 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 ê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)
[x * 0.1 for x in range(0, 10)] 

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

import numpy as np
for i in np.arange(0, 1, 0.1): 
    print i 

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

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 en virgule flottante.

Il est cohérent avec les résultats d'ensemble vides comme dans l'intervalle / xrange.

Si vous ne donnez qu'une seule valeur numérique à l'une ou l'autre fonction, la sortie de la plage standard sera renvoyée à la valeur de plafond entière du paramètre d'entrée (ainsi, si vous lui avez attribué la valeur 5.5, elle renverra la plage (6).)

Modifier: le code ci-dessous est désormais disponible dans 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)) # []

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

Le résultat 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]

more_itertools est une bibliothèque tierce qui implémente un 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

Cet outil fonctionne également pour les Decimal et Fraction .

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

Le truc pour éviter le problème d'arrondi consiste à utiliser un numéro distinct pour vous déplacer dans la plage, ce qui commence et la moitié l'étape en avance sur démarrer .

# 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 numpy.arange utiliser.

Pour une boutique complète, 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)

Cela peut être fait en utilisant la bibliothèque Numpy. La fonction arange () autorise les é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

Ma réponse est similaire à celle des autres utilisateurs de map (), sans NumPy et sans lambda (bien que vous puissiez le faire). 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))

Ajouter 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))]

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

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]

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 juil 2017, 04:57:36) [MSC v.1900 64   bit (AMD64)]

Surpris, personne n'a encore mentionné la solution recommandée dans les documents Python 3. :

  

Voir aussi:

     
      
  • La recette de linspace montre comment implémenter une version paresseuse de la plage qui convient au flottant. applications ponctuelles.
  •   

Une fois définie, la recette est facile à utiliser et ne nécessite ni 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 num indique 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))

Pour contrer les problèmes de précision float, vous pouvez utiliser le module Decimal a>.

Cela nécessite un effort supplémentaire de conversion vers int à partir de float ou str lors de l'écriture du code, mais vous pouvez plutôt passer <=> 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 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')]

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]

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 tenté 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.

Ce que j’ai fait est de changer mon xrange(0.0, 1.0, 0.01) en xrange(0, 100, 1) et d’utiliser la division par 100.0 à l’intérieur de la boucle. J'étais également préoccupé par les erreurs d'arrondis. J'ai donc décidé de tester s'il y en avait. Maintenant, j’ai entendu dire que si par exemple 0.01 d’un calcul n’est pas exactement le flottant <=>, leur comparaison devrait renvoyer la valeur 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 il a imprimé vrai pour chaque.

Maintenant, si je me trompe complètement, faites-le-moi savoir.

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

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]
scroll top