Jak użyć wartości kroku dziesiętnego zakresu ()?

742

Czy istnieje sposób na krok od 0 do 1 o 0,1?

Myślałem, że mogę to zrobić w następujący sposób, ale się nie udało:

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

Zamiast tego mówi, że argument kroku nie może wynosić zero, czego się nie spodziewałem.

Evan Fosmark
źródło
17
int (0,1) == 0, więc krok faktycznie wynosi zero. Może to być nieoczekiwane, ale wynosi zero. Możesz powtórzyć swoje pytanie, aby odzwierciedlić fakt, że nie spodziewałeś się tego. Powiedzenie „to nie jest” jest fałszywe i mylące.
S.Lott,
3
BTW Krótką jedną linijkę można zwinąć za pomocą itertools.takewhilei itertools.count. Nie jest to jednak lepsze niż drangewydajność.
Kos
1
Żenujące jest to, że zasięg Pythona nie pozwala na to, biorąc pod uwagę, jak łatwo jest zaimplementować generator, który robi to nawet bez kumulowania błędów zaokrąglania. Do licha, nawet seqnarzędzie w jądrach GNU pozwala obejść się seq 0 0.1 1bez błędów zaokrąglania!
josch
3
@josch: sequżywa C long doubletypu wewnętrznego i podatne na błędy zaokrągleń. Na przykład na mojej maszynie seq 0 0.1 1daje 1jako ostatnie wyjście (zgodnie z oczekiwaniami), ale seq 1 0.1 2daje 1.9jako ostatnie wyjście (zamiast oczekiwanych 2).
Mark Dickinson
Dla wygody sugestia @ Kos może zostać zaimplementowana jako itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))lub itertools.islice(itertools.count(0,0.1), 10)(jeśli masz import itertools), chociaż nie przetestowałem, która jest bardziej wydajna
Anonim

Odpowiedzi:

905

Zamiast bezpośrednio używać dziesiętnego kroku, o wiele bezpieczniej jest wyrazić to w kategoriach liczby punktów. W przeciwnym razie błąd zaokrąglenia zmiennoprzecinkowego może dać zły wynik.

Możesz użyć funkcji linspace z biblioteki NumPy (która nie jest częścią standardowej biblioteki, ale jest stosunkowo łatwa do uzyskania). linspacewymaga wielu punktów do zwrócenia, a także pozwala określić, czy dołączyć odpowiedni punkt końcowy:

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

Jeśli naprawdę chcesz użyć wartości kroku zmiennoprzecinkowego, możesz za pomocą 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])

Zmiennoprzecinkową błąd zaokrąglenia będzie powodować problemy, choć. Oto prosty przypadek, w którym błąd zaokrąglenia powoduje arangeutworzenie tablicy o długości 4, gdy powinna ona wygenerować tylko 3 liczby:

>>> numpy.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])
Andrew Jaffe
źródło
51
numpy jest tak wszechobecnym składnikiem Pythona, że ​​uważam tę odpowiedź za najbardziej „pytoniczną” ze wszystkich.
nalot
21
@AndreTerra Problem polega na tym, że @ numpy @ jest pakietem strony trzeciej i powoduje znaczne obciążenie w zakresie zarządzania zależnościami, przechowywania (dla samego pakietu) itp. W zależności od tego, co robi programista, korzystanie z niego może być niemożliwe to.
rbaleksandar
Przepraszam, ale od tego czasu nie zrozumiałem błędu zaokrąglenia zmiennoprzecinkowego w ostatniej części np.linspace(1.,1.3,4)i np.arange(1.,1.3,0.1)podaję dokładnie to samo wyjście
deadcode
4
@deadcode Powodem jest to, że np.arange ma zdefiniować zakres [start,stop)(tzn. z wyłączeniem stop), więc nie można oczekiwać, że 1.3 zostanie uwzględniony na liście. Zobacz to pytanie, aby dowiedzieć się, dlaczego jest ono nadal uwzględniane i co zrobić z tym.
dennis
5
To, ile użyto pakietu, nie jest żadnym wskaźnikiem tego, czy jest to „Python”.
Alex Hall
213

Zakres () Pythona może wykonywać tylko liczby całkowite, a nie zmiennoprzecinkowe. W twoim konkretnym przypadku możesz zamiast tego użyć zrozumienia listy:

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

(Zamień wywołanie zakresu na to wyrażenie).

W bardziej ogólnym przypadku możesz napisać niestandardową funkcję lub generator.


źródło
36
Co więcej, możesz po prostu używać zrozumienia generatora, jeśli pracujesz z Python 2.4+. (x * 0.1 for x in range(0, 10)).
JAB
42
Jeszcze lepiej, x/10zamiast tego x * 0.1: D Właściwie nic specjalnego, ale niektóre liczby będą bardziej precyzyjne, np. Za 3*0.1dostaniesz 0.30000000000000004, a za 3/10 dostaniesz 0.3:)
Bloke
4
3/10 daje mi 0, a nie 0,3. 3 / 10,0 daje 0,2999999999999999999. Python 2.6.
19
@LarsWirzenius: w Python 2.2+ from __future__ import division; 3/10zwraca 0.3. To zachowanie jest domyślne w Pythonie 3.x.
Benjamin Hodgson
2
można również użyć funkcji zaokrąglenia lst = [zaokrąglenie (x * 0,10,2) dla xw zakresie (0,10)]
MAREK
148

Opierając się na „xrange ([start], stop [, krok])” , możesz zdefiniować generator, który akceptuje i produkuje dowolny wybrany typ (trzymaj się typów obsługujących +i <):

>>> 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
źródło
45
Ma to problemy z zaokrągleniem. Zajrzyj tutaj: code.activestate.com/recipes/66472
Christian Oudard
Wydłużyłbym to nieco w innym kierunku za pomocą (podczas gdy r> stop) i odpowiadającego kroku r - = dla podania przeciwnego kierunku.
user318904
1
Zrobiłem funkcję xfrange bez problemów z precyzją float wymienionych powyżej. Sprawdź to;) stackoverflow.com/questions/477486/…
Carlos Vega
1
Kumulujesz błędy zaokrąglania. Zamiast tego użyj tego: `i = 0; r = start, podczas gdy r <stop: i + = 1; r = start + i * krok; Wydajność r`
Cees Timmerman
1
Pochodzi z pythoncentral.io/pythons-range-function-explained (i innych źródeł dokumentacji Python)
Apostolos
31

Zwiększ wielkość ipętli, a następnie zmniejsz ją, gdy jej potrzebujesz.

for i * 100 in range(0, 100, 10):
    print i / 100.0

EDYCJA: Naprawdę nie pamiętam, dlaczego myślałem, że to działa składniowo

for i in range(0, 11, 1):
    print i / 10.0

To powinno mieć pożądaną moc wyjściową.

cmsjr
źródło
Myślę, że przekonasz się, że range () działa poza liczbami całkowitymi, w takim przypadku byłoby to jedyne rozwiązanie, wykorzystujące co najmniej tę samą funkcję.
Matthew Scharley,
1
@cmsjr creative: D Jedna mała rzecz: podziel przez 100,0, aby Python nie obcinał wyniku, jeśli używasz Python 2.x. Myślę, że w wersji 3.0 będzie działać tak, jak ją kodowałeś.
Dana
2
for i * 100 in range(0, 100, 10): Błąd składni: nie można przypisać do operatora
Anne van Rossum
25

scipyma wbudowaną funkcję, arangektóra uogólnia range()konstruktor Pythona, aby spełnić twoje wymagania dotyczące obsługi float.

from scipy import arange

Catherine Ray
źródło
8
To jest dokładnie to samo, arangeco można znaleźć w numpy: >>> import scipy >>> import numpy >>> numpy.arange is scipy.arangezwróci True.
iFreilicht,
from nump import arange as range
złośnica
24

Myślę, że NumPy to trochę przesada.

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

Ogólnie rzecz biorąc, aby zrobić krok po kroku 1/xw górę do yzrobiłbyś

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/xwytwarzałem mniej hałasu zaokrąglania podczas testowania).

Kalle
źródło
18

Podobnie jak seq funkcja R , ta zwraca sekwencję w dowolnej kolejności, biorąc pod uwagę prawidłową wartość kroku. Ostatnia wartość jest równa wartości stop.

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

Wyniki

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
źródło
2
To świetna odpowiedź dla kogoś, kto chce ją zdobyć bez zbytniego angażowania się w Pythona.
Chani,
1
To było prawie to, czego szukałem - zauważ, że seq(0.5, 3.0)powraca [0.5, 1.5, 2.5, 3.5]. Aby uniknąć sytuacji, w której ostatnie wpisy są poza zasięgiem, zastąp n = int(round(...je n = int(floor(...linią from math import flooru góry (powyżej def seq(...).
FriendFX,
2
@FriendFX Nie rób tego! Jeśli floorzostanie użyty, seq(0.2, 0.9, 0.1)nie osiągnie właściwego punktu końcowego i powróci[0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7, 0.8]
fdermishin
@ user502144: Niezły chwyt, dzięki. Myślę, że muszę zadowolić się jednym z bardziej złożonych rozwiązań, aby zachować ogólne.
FriendFX
14

Obawiam się, że wbudowana funkcja range () zwraca ciąg wartości liczb całkowitych, więc nie można jej użyć do zrobienia dziesiętnego kroku.

Powiedziałbym po prostu użyć pętli while:

i = 0.0
while i <= 1.0:
    print i
    i += 0.1

Jeśli jesteś ciekawy, Python konwertuje 0,1 na 0, dlatego mówi ci, że argument nie może wynosić zero.

Dana
źródło
2
Nie rób tego! Dodanie .110 razy to nie to samo, co dodanie 1! docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
kot
12

Oto rozwiązanie za pomocą 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)

Przykład użycia:

for i in seq(0, 1, 0.1):
    print(i)
Pramod
źródło
Dla kompletności powinieneś obliczyć wartość bezwzględną dla zmiennej sample_count, w ten sposób twoja funkcja będzie działać również dla ujemnego startu (tj. Od -10 do 10)
Deleteman
10
[x * 0.1 for x in range(0, 10)] 

w Pythonie 2.7x daje wynik:

[0,0, 0,1, 0,2, 0,30000000000000004, 0,4, 0,5, 0,6000000000000001, 0,7000000000000001, 0,8, 0,9]

ale jeśli użyjesz:

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

daje pożądane:

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

Nik
źródło
5

A jeśli robisz to często, możesz zapisać wygenerowaną listę r

r=map(lambda x: x/10.0,range(0,10))
for i in r:
    print i
RSabet
źródło
5

more_itertoolsto biblioteka innej firmy, która implementuje numeric_rangenarzędzie:

import more_itertools as mit


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

Wynik

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

To narzędzie działa również dla Decimali Fraction.

pylang
źródło
4

Moje wersje używają oryginalnej funkcji zakresu do tworzenia multiplikatywnych wskaźników dla zmiany. Pozwala to na taką samą składnię jak w pierwotnej funkcji zakresu. Zrobiłem dwie wersje, jedną z użyciem liczb zmiennoprzecinkowych i jedną z liczbami dziesiętnymi, ponieważ odkryłem, że w niektórych przypadkach chciałem uniknąć znoszenia zaokrągleń wprowadzonego przez arytmetykę zmiennoprzecinkową.

Jest to zgodne z wynikami pustego zestawu, jak w zakresie / zakres.

Przekazanie tylko jednej wartości liczbowej do dowolnej funkcji spowoduje zwrócenie wyjściowego zakresu standardowego do całkowitej wartości parametru wejściowego na wartość całkowitą (więc jeśli dasz mu wartość 5.5, zwróci zakres (6).)

Edycja: poniższy kod jest teraz dostępny jako pakiet na 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
źródło
Jak może frangedziałać, jeśli jest stop None? Ta część kodu nawet nie uwzględnia rozmiaru kroku.
josch
1
@josch rangema dwa podpisy: range(stop)który przyjmuje wartość domyślną start=0, step=1, i range(start, stop, step)gdzie nie są przyjmowane żadne założenia. frangeodzwierciedla to. Podczas korzystania z range(stop)podpisu, zarówno frangei drangezaczynają się od 0 i przyrost o 1, więc ich zachowanie jest identyczne do regularnych range(stop)zachowań z przystanku zaokrągla się w górę do najbliższej liczby całkowitej.
Nisan.H
4

To jest moje rozwiązanie, aby uzyskać zakresy z krokami zmiennoprzecinkowymi.
Korzystając z tej funkcji, nie trzeba importować numpy ani instalować jej.
Jestem prawie pewien, że można to poprawić i zoptymalizować. Możesz to zrobić i opublikować tutaj.

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)

Dane wyjściowe to:

[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
źródło
1
Wystąpił błąd z pominięciem ostatniej wartości, jeśli mieści się w zakresie 1 kroku od wartości zatrzymania. tzn. xfrange (1,10,2) ma tylko 1,3,5,7, brakuje 9
płat
Dla odniesienia i innych czytelników proszę porównać tę implementację z tym stackoverflow.com/a/477610/54964 . To nie wydaje się mieć dużych problemów z pływakiem.
Léo Léopold Hertz
@carlosvega Czy możesz potwierdzić, dlaczego Lobe osiąga swój wynik?
Léo Léopold Hertz
3

Dla kompletności butiku funkcjonalne rozwiązanie:

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
źródło
2

Możesz użyć tej funkcji:

def frange(start,end,step):
    return map(lambda x: x*step, range(int(start*1./step),int(end*1./step)))
użytkownik376536
źródło
Nie działa poprawnie, np.list(frange(99.8, 100.1, 0.1)) => [99.7, 99.80000000000001, 99.9]
Shai Coleman
2

Sztuką uniknąć zaokrąglić problemu jest użycie oddzielnego numeru do poruszania się w przedziale, który uruchamia i pół krok naprzód od początku .

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

Alternatywnie numpy.arangemożna zastosować.

wolfram77
źródło
2

Można to zrobić za pomocą biblioteki Numpy. Funkcja arange () umożliwia wykonywanie kroków w trybie zmiennoprzecinkowym. Zwraca jednak tablicę numpy, którą można konwertować na listę za pomocą tolist () dla naszej wygody.

for i in np.arange(0, 1, 0.1).tolist():
   print i
użytkownik3654478
źródło
2

Moja odpowiedź jest podobna do innych używających map (), bez potrzeby użycia NumPy i bez używania lambda (choć można). Aby uzyskać listę wartości zmiennoprzecinkowych od 0,0 do t_max w krokach dt:

def xdt(n):
    return dt*float(n)
tlist  = map(xdt, range(int(t_max/dt)+1))
Eric Myers
źródło
2

Zaskoczony, nikt jeszcze nie wspomniał o zalecanym rozwiązaniu w dokumentacji Python 3 :

Zobacz też:

  • Do przepisu linspace pokazuje jak zaimplementować leniwą wersję zakresie, który nadaje się do zastosowań pływający punkt.

Po zdefiniowaniu przepis jest łatwy w użyciu i nie wymaga numpyżadnych zewnętrznych bibliotek, ale działa podobnie numpy.linspace(). Zauważ, że zamiast stepargumentu trzeci numargument określa liczbę pożądanych wartości, na przykład:

print(linspace(0, 10, 5))
# linspace(0, 10, 5)
print(list(linspace(0, 10, 5)))
# [0.0, 2.5, 5.0, 7.5, 10]

Cytuję zmodyfikowaną wersję pełnego przepisu na Python 3 od Andrew Barnerta poniżej:

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
źródło
2

Aby przeciwdziałać problemom z precyzją zmiennoprzecinkową, możesz użyć Decimalmodułu .

Wymaga to dodatkowego wysiłku przekształcania się Decimalz intlub floatpodczas pisania kodu, ale można zamiast przejść stri modyfikować funkcję, jeśli tego rodzaju wygody jest rzeczywiście konieczne.

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

Przykładowe wyniki -

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
źródło
2
Z podobnymi Decimalwejściami np.arangedziała tak samo:np.arange(Decimal('-2.0'), Decimal('2.0'), Decimal('0.1'))
hpaulj
2
Tak, dzięki. Chociaż wymagałoby to zewnętrznej (numpy) biblioteki lib.
shad0w_wa1k3r
1

Dodaj automatyczną korektę pod kątem możliwości nieprawidłowego podpisania w kroku:

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
źródło
1

Moje rozwiązanie:

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
źródło
1

Najlepsze rozwiązanie: brak błędu zaokrąglania
_________________________________________________________________________________

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

_________________________________________________________________________________

Lub, dla ustawionego zakresu zamiast ustawionych punktów danych (np. Funkcja ciągła), użyj:

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

Aby zaimplementować funkcję zamienić x / pow(step, -1)z f( x / pow(step, -1) ), i zdefiniować f.
Na przykład:

>>> 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
źródło
1

start i stop są włączające, a nie jeden lub drugi (zwykle stop jest wykluczony) i bez importu oraz przy użyciu generatorów

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

Goran B.
źródło
1

Wiem, że spóźniłem się na przyjęcie tutaj, ale oto trywialne rozwiązanie generatora działające w wersji 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

wtedy możesz nazwać to tak, jak oryginał range()... nie ma obsługi błędów, ale daj mi znać, jeśli istnieje błąd, który można rozsądnie wykryć, a ja zaktualizuję. lub możesz to zaktualizować. to jest StackOverflow.

David Culbreth
źródło
0

Oto moje rozwiązanie, które działa dobrze z float_range (-1, 0, 0,01) i działa bez błędów reprezentacji zmiennoprzecinkowej. Nie jest bardzo szybki, ale działa dobrze:

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
źródło
0

Jestem tylko początkującym, ale miałem ten sam problem podczas symulacji niektórych obliczeń. Oto jak próbowałem to rozpracować, co wydaje się działać z krokami dziesiętnymi.

Jestem też dość leniwy i dlatego trudno mi było napisać własną funkcję zakresu.

Zasadniczo to, co zrobiłem, zmieniło moje xrange(0.0, 1.0, 0.01)na xrange(0, 100, 1)i zastosowało podział 100.0w pętli. Martwiłem się również, czy pojawią się błędy w zaokrąglaniu. Postanowiłem więc sprawdzić, czy są jakieś. Teraz słyszałem, że jeśli na przykład 0.01z obliczeń nie jest dokładnie zmiennoprzecinkowe 0.01porównanie ich powinno zwrócić False (jeśli się mylę, proszę dać mi znać).

Dlatego postanowiłem przetestować, czy moje rozwiązanie będzie działać dla mojego zakresu, uruchamiając krótki 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

I wydrukował True dla każdego.

Teraz, jeśli popełniam błąd, daj mi znać.

użytkownik 2836437
źródło
0

Ta jedna linijka nie zaśmieci twojego kodu. Ważny jest znak parametru kroku .

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
źródło