bereik() voor floats

Is er een range()equivalent voor floats in 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

Antwoord 1, autoriteit 100%


Ik ken geen ingebouwde functie, maar het schrijven van een functie zoals [this](https://stackoverflow.com/a/477610/623735) zou niet al te ingewikkeld moeten zijn.

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


Zoals de opmerkingen vermelden, kan dit onvoorspelbare resultaten opleveren, zoals:

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

Om het verwachte resultaat te krijgen, kunt u een van de andere antwoorden in deze vraag gebruiken, of zoals @Tadhg al zei, u kunt decimal.Decimalgebruiken als de jumpargument. Zorg ervoor dat je het initialiseert met een string in plaats van een float.

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

Of zelfs:

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

En dan:

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

[editor’s not: als je alleen positieve jumpen integer start en stop (xen y) gebruikt, werkt dit prima. Zie hiervoor een meer algemene oplossing.]


Antwoord 2, autoriteit 96%

U kunt het volgende gebruiken:

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

of gebruik lambda / map:

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

Antwoord 3, autoriteit 85%

Vroeger gebruikte ik numpy.arangemaar had wat complicaties bij het controleren van het aantal elementen dat het retourneert, vanwege drijvende-kommafouten. Dus nu gebruik ik linspace, bijvoorbeeld:

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

Antwoord 4, autoriteit 39%

Pylab heeft frange(eigenlijk een wrapper voor 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. ])

Antwoord 5, autoriteit 13%

Graag geëvalueerd (2.x range):

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

Lazily geëvalueerd (2.x xrange, 3.x range):

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

Anders:

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

Antwoord 6, autoriteit 11%

met behulp van itertools: lui geëvalueerd drijvende-kommabereik:

>>> from itertools import count, takewhile
>>> def frange(start, stop, step):
        return takewhile(lambda x: x< stop, count(start, step))
>>> list(frange(0.5, 5, 1.5))
# [0.5, 2.0, 3.5]

Antwoord 7, autoriteit 7%

Ik heb geholpen met het toevoegen van de functie numeric_rangeaan de pakket more-itertools.

more_itertools.numeric_range(start, stop, step)werkt als het ingebouwde functiebereik, maar kan overweg met floats, decimalen en breuken.

>>> from more_itertools import numeric_range
>>> tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)

Antwoord 8, autoriteit 5%

Er is niet zo’n ingebouwde functie, maar je kunt het volgende (Python 3-code) gebruiken om het werk zo veilig te doen als Python je toestaat.

from fractions import Fraction
def frange(start, stop, jump, end=False, via_str=False):
    """
    Equivalent of Python 3 range for decimal numbers.
    Notice that, because of arithmetic errors, it is safest to
    pass the arguments as strings, so they can be interpreted to exact fractions.
    >>> assert Fraction('1.1') - Fraction(11, 10) == 0.0
    >>> assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)
    Parameter `via_str` can be set to True to transform inputs in strings and then to fractions.
    When inputs are all non-periodic (in base 10), even if decimal, this method is safe as long
    as approximation happens beyond the decimal digits that Python uses for printing.
    For example, in the case of 0.1, this is the case:
    >>> assert str(0.1) == '0.1'
    >>> assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'
    If you are not sure whether your decimal inputs all have this property, you are better off
    passing them as strings. String representations can be in integer, decimal, exponential or
    even fraction notation.
    >>> assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
    >>> assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
    >>> assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
    >>> assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
    >>> assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
    >>> assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0
    """
    if via_str:
        start = str(start)
        stop = str(stop)
        jump = str(jump)
    start = Fraction(start)
    stop = Fraction(stop)
    jump = Fraction(jump)
    while start < stop:
        yield float(start)
        start += jump
    if end and start == stop:
        yield(float(start))

Je kunt alles verifiëren door een paar beweringen uit te voeren:

assert Fraction('1.1') - Fraction(11, 10) == 0.0
assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)
assert str(0.1) == '0.1'
assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'
assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0
assert list(frange(2, 3, '1/6', end=True))[-1] == 3.0
assert list(frange(0, 100, '1/3', end=True))[-1] == 100.0

Code beschikbaar op Github


9, Autoriteit 4%

Waarom is er geen implementatie van drijvende puntenbereik in de standaardbibliotheek?

Zoals duidelijk gemaakt door alle berichten hier, is er geen drijvende puntversie van range(). Dat gezegd hebbende, de omissie is logisch als we van mening zijn dat het range()-functie vaak wordt gebruikt als een index (en natuurlijk, dat betekent een accessor ) generator. Dus wanneer we range(0,40)noemen, zeggen we dat we willen dat we 40 waarden beginnen vanaf 0, maximaal 40, maar niet-inclusief 40 zelf.

Wanneer we overwegen dat de indexgeneratie evenveel is over het aantal indices, omdat het hun waarden is, maakt het gebruik van een vlotterimplementatie van range()in de standaardbibliotheek minder zinvol. Als we bijvoorbeeld de functie frange(0, 10, 0.25)noemen, verwachten we zowel 0 als 10 te worden opgenomen, maar dat zou een generator met 41 waarden opleveren, niet de 40 Verwacht van 10/0.25.

Dus, afhankelijk van het gebruik, zal een frange()functie altijd contra-intuïtief gedrag vertonen; het heeft ofwel te veel waarden zoals waargenomen vanuit het indexeringsperspectief of bevat geen getal dat redelijkerwijs zou moeten worden geretourneerd vanuit wiskundig perspectief. Met andere woorden, het is gemakkelijk in te zien hoe een dergelijke functie twee zeer verschillende use-cases zou samenvoegen – de naamgeving impliceert de indexing use-case; het gedrag impliceert een wiskundige.

De wiskundige use-case

Dat gezegd hebbende, zoals besproken in andere berichten, voert numpy.linspace()de generatie vanuit wiskundig perspectief goed uit:

numpy.linspace(0, 10, 41)
array([  0.  ,   0.25,   0.5 ,   0.75,   1.  ,   1.25,   1.5 ,   1.75,
         2.  ,   2.25,   2.5 ,   2.75,   3.  ,   3.25,   3.5 ,   3.75,
         4.  ,   4.25,   4.5 ,   4.75,   5.  ,   5.25,   5.5 ,   5.75,
         6.  ,   6.25,   6.5 ,   6.75,   7.  ,   7.25,   7.5 ,   7.75,
         8.  ,   8.25,   8.5 ,   8.75,   9.  ,   9.25,   9.5 ,   9.75,  10.
])

Het gebruiksscenario voor indexeren

En voor het indexeringsperspectief heb ik een iets andere benadering geschreven met een aantal trucjes, waarmee we het aantal decimalen kunnen specificeren.

# Float range function - string formatting method
def frange_S (start, stop, skip = 1.0, decimals = 2):
    for i in range(int(start / skip), int(stop / skip)):
        yield float(("%0." + str(decimals) + "f") % (i * skip))

Op dezelfde manier kunnen we ook de ingebouwde functie roundgebruiken en het aantal decimalen specificeren:

# Float range function - rounding method
def frange_R (start, stop, skip = 1.0, decimals = 2):
    for i in range(int(start / skip), int(stop / skip)):
        yield round(i * skip, ndigits = decimals)

Een snelle vergelijking & Prestaties

Natuurlijk, gezien de bovenstaande discussie, hebben deze functies een vrij beperkte use case. Niettemin, hier is een snelle vergelijking:

def compare_methods (start, stop, skip):
    string_test  = frange_S(start, stop, skip)
    round_test   = frange_R(start, stop, skip)
    for s, r in zip(string_test, round_test):
        print(s, r)
compare_methods(-2, 10, 1/3)

De resultaten zijn identiek voor elk:

-2.0 -2.0
-1.67 -1.67
-1.33 -1.33
-1.0 -1.0
-0.67 -0.67
-0.33 -0.33
0.0 0.0
...
8.0 8.0
8.33 8.33
8.67 8.67
9.0 9.0
9.33 9.33
9.67 9.67

En enkele tijden:

>>> import timeit
>>> setup = """
... def frange_s (start, stop, skip = 1.0, decimals = 2):
...     for i in range(int(start / skip), int(stop / skip)):
...         yield float(("%0." + str(decimals) + "f") % (i * skip))
... def frange_r (start, stop, skip = 1.0, decimals = 2):
...     for i in range(int(start / skip), int(stop / skip)):
...         yield round(i * skip, ndigits = decimals)
... start, stop, skip = -1, 8, 1/3
... """
>>> min(timeit.Timer('string_test = frange_s(start, stop, skip); [x for x in string_test]', setup=setup).repeat(30, 1000))
0.024284090992296115
>>> min(timeit.Timer('round_test = frange_r(start, stop, skip); [x for x in round_test]', setup=setup).repeat(30, 1000))
0.025324633985292166

Het lijkt erop dat de methode voor het opmaken van strings met een haartje wint op mijn systeem.

De beperkingen

En tot slot een demonstratie van het punt uit de bovenstaande discussie en een laatste beperking:

# "Missing" the last value (10.0)
for x in frange_R(0, 10, 0.25):
    print(x)
0.25
0.5
0.75
1.0
...
9.0
9.25
9.5
9.75

Verder, als de parameter skipniet deelbaar is door de waarde stop, kan er een gapende kloof ontstaan gezien het laatste probleem:

# Clearly we know that 10 - 9.43 is equal to 0.57
for x in frange_R(0, 10, 3/7):
    print(x)
0.0
0.43
0.86
1.29
...
8.14
8.57
9.0
9.43

Er zijn manieren om dit probleem aan te pakken, maar uiteindelijk zou de beste aanpak waarschijnlijk zijn om gewoon Numpy te gebruiken.


Antwoord 10, autoriteit 4%

Een oplossing zonder numpy etc afhankelijkhedenwerd geleverd door kichik maar vanwege de drijvende-kommaberekeningen, gedraagt het zich vaak onverwacht. Zoals opgemerkt door iken blubberdiblub, sluipen extra elementen gemakkelijk in het resultaat. Bijvoorbeeld naive_frange(0.0, 1.0, 0.1)zou 0.999...als laatste waarde opleveren en dus in totaal 11 waarden opleveren.

Een beetje robuuste versie wordt hier verstrekt:

def frange(x, y, jump=1.0):
    '''Range for floats.'''
    i = 0.0
    x = float(x)  # Prevent yielding integers.
    x0 = x
    epsilon = jump / 2.0
    yield x  # yield always first value
    while x + epsilon < y:
        i += 1.0
        x = x0 + i * jump
        if x < y:
          yield x

Omdat de vermenigvuldiging, de afrondingsfouten niet accumuleren. Het gebruik van epsilonzorgt voor mogelijke afrondingsfout van de vermenigvuldiging, hoewel problemen natuurlijk kunnen stijgen in de zeer kleine en zeer grote uiteinden. Nu, zoals verwacht:

> a = list(frange(0.0, 1.0, 0.1))
> a[-1]
0.9
> len(a)
10

en met enigszins grotere nummers:

> b = list(frange(0.0, 1000000.0, 0.1))
> b[-1]
999999.9
> len(b)
10000000

De code is ook beschikbaar als een github-gist .


Antwoord 11, autoriteit 3%

Dit kan met numpy.arange(start, stop, stepsize)

import numpy as np
np.arange(0.5,5,1.5)
>> [0.5, 2.0, 3.5, 5.0]
# OBS you will sometimes see stuff like this happening, 
# so you need to decide whether that's not an issue for you, or how you are going to catch it.
>> [0.50000001, 2.0, 3.5, 5.0]

Opmerking 1:
Uit de discussie in de commentaarsectie hier, “gebruik nooit numpy.arange()(de numpy-documentatie zelf raadt het af). Gebruik numpy.linspace zoals aanbevolen door wim, of een van de andere suggesties in dit antwoord”

Opmerking 2:
Ik heb de discussie in een paar opmerkingen hier gelezen, maar nadat ik nu voor de derde keer op deze vraag ben teruggekomen, vind ik dat deze informatie op een beter leesbare plaats moet worden geplaatst.


Antwoord 12, autoriteit 3%

Zoals kichikschreef, zou dit niet te ingewikkeld moeten zijn. Maar deze code:

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

Is ongepast vanwege het cumulatieve effect van foutenbij het werken met floats.
Daarom ontvang je zoiets als:

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

Terwijl het verwachte gedrag zou zijn:

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

Oplossing 1

De cumulatieve fout kan eenvoudig worden verminderd door een indexvariabele te gebruiken. Hier is het voorbeeld:

from math import ceil
    def frange2(start, stop, step):
        n_items = int(ceil((stop - start) / step))
        return (start + i*step for i in range(n_items))

Dit voorbeeld werkt zoals verwacht.

Oplossing 2

Geen geneste functies. Slechts een tijdje en een tellervariabele:

def frange3(start, stop, step):
    res, n = start, 1
    while res < stop:
        yield res
        res = start + n * step
        n += 1

Deze functie werkt ook goed, behalve in de gevallen waarin u het omgekeerde bereik wilt. Bijv.:

>>>list(frange3(1, 0, -.1))
[]

Oplossing 1 zal in dit geval werken zoals verwacht. Om deze functie in dergelijke situaties te laten werken, moet u een hack toepassen, vergelijkbaar met het volgende:

from operator import gt, lt
def frange3(start, stop, step):
    res, n = start, 0.
    predicate = lt if start < stop else gt
    while predicate(res, stop):
        yield res
        res = start + n * step
        n += 1

Met deze hack kunt u deze functies gebruiken met negatieve stappen:

>>>list(frange3(1, 0, -.1))
[1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.3999999999999999, 0.29999999999999993, 0.19999999999999996, 0.09999999999999998]

Oplossing 3

U kunt nog verder gaan met gewone standaardbibliotheek en een bereikfunctie samenstellen voor de meeste van numerieke typen:

from itertools import count
from itertools import takewhile
def any_range(start, stop, step):
    start = type(start + step)(start)
    return takewhile(lambda n: n < stop, count(start, step))

Deze generator is aangepast van het vloeiende Python-boek (hoofdstuk 14. Iterables, iterators en generatoren). het werkt niet met afnemende reeksen. Je moet een hack toepassen, zoals in de vorige oplossing.

U kunt deze generator als volgt gebruiken, bijvoorbeeld:

>>>list(any_range(Fraction(2, 1), Fraction(100, 1), Fraction(1, 3)))[-1]
299/3
>>>list(any_range(Decimal('2.'), Decimal('4.'), Decimal('.3')))
[Decimal('2'), Decimal('2.3'), Decimal('2.6'), Decimal('2.9'), Decimal('3.2'), Decimal('3.5'), Decimal('3.8')]

En je kunt het natuurlijk ook gebruiken met floaten int.

Wees voorzichtig

Als u deze functies met negatieve stappen wilt gebruiken, moet u een vinkje voor het stapteken toevoegen, bijvoorbeeld:

no_proceed = (start < stop and step < 0) or (start > stop and step > 0)
if no_proceed: raise StopIteration

De beste optie hier is om StopIterationte verhogen, als je de functie rangezelf wilt nabootsen.

Bereik nabootsen

Als u de functie-interface rangewilt nabootsen, kunt u enkele argumentcontroles uitvoeren:

def any_range2(*args):
    if len(args) == 1:
        start, stop, step = 0, args[0], 1.
    elif len(args) == 2:
        start, stop, step = args[0], args[1], 1.
    elif len(args) == 3:
        start, stop, step = args
    else:
        raise TypeError('any_range2() requires 1-3 numeric arguments')
    # here you can check for isinstance numbers.Real or use more specific ABC or whatever ...
    start = type(start + step)(start)
    return takewhile(lambda n: n < stop, count(start, step))

Ik denk dat je het punt hebt. Je kunt elk van deze functies gebruiken (behalve de allereerste) en allesdat je daarvoor nodig hebt is de standaardbibliotheek van Python.


Antwoord 13

ik heb een functie geschreven die een tupel van een reeks dubbele-precisie drijvende-kommagetallen retourneert zonder decimalen voorbij de honderdsten. het was gewoon een kwestie van het ontleden van de bereikwaarden zoals strings en het afsplitsen van het overschot. Ik gebruik het voor het weergeven van bereiken om vanuit een gebruikersinterface te selecteren. Ik hoop dat iemand anders het nuttig vindt.

def drange(start,stop,step):
    double_value_range = []
    while start<stop:
        a = str(start)
        a.split('.')[1].split('0')[0]
        start = float(str(a))
        double_value_range.append(start)
        start = start+step
    double_value_range_tuple = tuple(double_value_range)
   #print double_value_range_tuple
    return double_value_range_tuple

Antwoord 14

Gebruik

# Counting up
drange(0, 0.4, 0.1)
[0, 0.1, 0.2, 0.30000000000000004, 0.4]
# Counting down
drange(0, -0.4, -0.1)
[0, -0.1, -0.2, -0.30000000000000004, -0.4]

Elke stap afronden op N decimalen

drange(0, 0.4, 0.1, round_decimal_places=4)
[0, 0.1, 0.2, 0.3, 0.4]
drange(0, -0.4, -0.1, round_decimal_places=4)
[0, -0.1, -0.2, -0.3, -0.4]

Code

def drange(start, end, increment, round_decimal_places=None):
    result = []
    if start < end:
        # Counting up, e.g. 0 to 0.4 in 0.1 increments.
        if increment < 0:
            raise Exception("Error: When counting up, increment must be positive.")
        while start <= end:
            result.append(start)
            start += increment
            if round_decimal_places is not None:
                start = round(start, round_decimal_places)
    else:
        # Counting down, e.g. 0 to -0.4 in -0.1 increments.
        if increment > 0:
            raise Exception("Error: When counting down, increment must be negative.")
        while start >= end:
            result.append(start)
            start += increment
            if round_decimal_places is not None:
                start = round(start, round_decimal_places)
    return result

Waarom kiezen voor dit antwoord?

  • Veel andere antwoorden zullen hangen wanneer ze worden gevraagd om naar beneden te tellen.
  • Veel andere antwoorden geven onjuist afgeronde resultaten.
  • Andere antwoorden op basis van np.linspacezijn geraakt en misser, ze kunnen wel of niet werken vanwege moeilijkheden bij het kiezen van het juiste aantal divisies. np.linspaceHEEFTELIJK MET DECIMALE STAKEN VAN 0,1, en de volgorde van divisies in de formule om de verhoging in een aantal splitsingen om te zetten, kan resulteren in de juiste of gebroken code.
  • Andere antwoorden op basis van np.arangeWORDT VERDIEND.

Probeer in twijfel de vier tests-gevallen hierboven.


15

def Range(*argSequence):
    if len(argSequence) == 3:
        imin = argSequence[0]; imax = argSequence[1]; di = argSequence[2]
        i = imin; iList = []
        while i <= imax:
            iList.append(i)
            i += di
        return iList
    if len(argSequence) == 2:
        return Range(argSequence[0], argSequence[1], 1)
    if len(argSequence) == 1:
        return Range(1, argSequence[0], 1)

Houd er rekening mee dat de eerste letter van bereik kapitaal is. Deze naamgevingsmethode wordt niet aangemoedigd voor functies in Python. Je kunt het bereik veranderen in zoiets als Drange of Frange als je wilt. De functie “bereik” gedraagt ​​zich net zoals u wilt. U kunt het handleiding hier controleren [http://reference.wolfram.com/language/ref /Range.html ].


16

Ik denk dat er een heel eenvoudig antwoord is dat alle kenmerken van het bereik emuleert, behalve voor zowel float als integer. In deze oplossing veronderstel je gewoon dat je benadering standaard 1e-7 is (of degene die je kiest) en je kunt het veranderen wanneer je de functie aanroept.

def drange(start,stop=None,jump=1,approx=7): # Approx to 1e-7 by default
  '''
  This function is equivalent to range but for both float and integer
  '''
  if not stop: # If there is no y value: range(x)
      stop= start
      start= 0
  valor= round(start,approx)
  while valor < stop:
      if valor==int(valor):
          yield int(round(valor,approx))
      else:
          yield float(round(valor,approx))
      valor += jump
  for i in drange(12):
      print(i)

Antwoord 17

Over het maken van een berg van een mollenheuvel gesproken.
Als je de eis versoepelt om een float-analoog te maken van de functie range, en gewoon een lijst met floats maakt die gemakkelijk te gebruiken is in een for-lus, is de codering eenvoudig en robuust.

def super_range(first_value, last_value, number_steps):
    if not isinstance(number_steps, int):
        raise TypeError("The value of 'number_steps' is not an integer.")
    if number_steps < 1:
        raise ValueError("Your 'number_steps' is less than 1.")
    step_size = (last_value-first_value)/(number_steps-1)
    output_list = []
    for i in range(number_steps):
        output_list.append(first_value + step_size*i)
    return output_list
first = 20.0
last = -50.0
steps = 5
print(super_range(first, last, steps))

De uitvoer zal zijn

[20.0, 2.5, -15.0, -32.5, -50.0]

Merk op dat de functie super_rangeniet beperkt is tot floats. Het kan elk gegevenstype aan waarvoor de operators +, -, *en /zijn gedefinieerd, zoals als complex, Decimalen numpy.array:

import cmath
first = complex(1,2)
last = complex(5,6)
steps = 5
print(super_range(first, last, steps))
from decimal import *
first = Decimal(20)
last = Decimal(-50)
steps = 5
print(super_range(first, last, steps))
import numpy as np
first = np.array([[1, 2],[3, 4]])
last = np.array([[5, 6],[7, 8]])
steps = 5
print(super_range(first, last, steps))

De uitvoer zal zijn:

[(1+2j), (2+3j), (3+4j), (4+5j), (5+6j)]
[Decimal('20.0'), Decimal('2.5'), Decimal('-15.0'), Decimal('-32.5'), Decimal('-50.0')]
[array([[1., 2.],[3., 4.]]),
 array([[2., 3.],[4., 5.]]),
 array([[3., 4.],[5., 6.]]),
 array([[4., 5.],[6., 7.]]),
 array([[5., 6.],[7., 8.]])]

Antwoord 18

Terwijl op integers gebaseerde bereiken goed gedefinieerd zijn in de zin dat “wat je ziet is wat je krijgt”, zijn er dingen die niet gemakkelijk te zien zijn in floats die problemen veroorzaken bij het verkrijgen van wat lijkt op een goed gedefinieerd gedrag in een gewenst bereik .

Er zijn twee benaderingen die men kan nemen:

  1. een gegeven bereik in een bepaald aantal segmenten splitsen: de linspace-benadering waarin u het grote aantal decimale cijfers accepteert wanneer u een aantal punten selecteert dat het bereik niet goed verdeelt (bijv. 0 tot 1 in 7 stappen geven een eerste stapwaarde van 0,14285714285714285)

  2. Geef de gewenste WYSIWIG-stapsgehalte die u al weet, moet werken en wensen dat het zou werken. Je hoop zal vaak worden onderbroken door waarden te krijgen die het eindpunt missen dat je wilde slaan.

Multiples kan hoger of lager zijn dan u verwacht:

>>> 3*.1 > .3  # 0.30000000000000004
True
>>> 3*.3 < 0.9  # 0.8999999999999999
True

U zult proberen oplopende fouten te voorkomen door veelvouden van uw stap toe te voegen en niet te verhogen, maar het probleem zal zichzelf altijd presenteren en u zult gewoon niet krijgen wat u verwacht of u het met de hand op papier deed – met exacte decimalen . Maar u weet het zou mogelijk moeten zijn, aangezien Python u toont 0.1in plaats van de onderliggende integerverhouding met een nauwe benadering van 0,1:

>>> (3*.1).as_integer_ratio()
(1351079888211149, 4503599627370496)

In de aangeboden methoden als antwoorden, het gebruik van fractie hier met de optie om de invoer als snaren te verwerken is het beste. Ik heb een paar suggesties om het beter te maken:

  1. Maak het omgaan met bereikachtige standaardwaarden, zodat u vanaf 0 automatisch kunt beginnen
  2. Maak het omgaan met afnemende reeksen
  3. Maak de uitvoer eruit alsof je zou verwachten als je exact rekenkunde gebruikt

Ik bied een routine aan die dezelfde soort dingen doet, maar die het fractieobject niet gebruikt. In plaats daarvan gebruikt het roundom cijfers te maken met dezelfde schijnbare cijfers als de cijfers zouden hebben als u ze met Python bedrukt, b.v. 1 decimaal voor iets als 0,1 en 3 decimalen voor iets als 0,004:

def frange(start, stop, step, n=None):
    """return a WYSIWYG series of float values that mimic range behavior
    by excluding the end point and not printing extraneous digits beyond
    the precision of the input numbers (controlled by n and automatically
    detected based on the string representation of the numbers passed).
    EXAMPLES
    ========
    non-WYSIWYS simple list-comprehension
    >>> [.11 + i*.1 for i in range(3)]
    [0.11, 0.21000000000000002, 0.31]
    WYSIWYG result for increasing sequence
    >>> list(frange(0.11, .33, .1))
    [0.11, 0.21, 0.31]
    and decreasing sequences
    >>> list(frange(.345, .1, -.1))
    [0.345, 0.245, 0.145]
    To hit the end point for a sequence that is divisibe by
    the step size, make the end point a little bigger by
    adding half the step size:
    >>> dx = .2
    >>> list(frange(0, 1 + dx/2, dx))
    [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
    """
    if step == 0:
        raise ValueError('step must not be 0')
    # how many decimal places are showing?
    if n is None:
        n = max([0 if '.' not in str(i) else len(str(i).split('.')[1])
                for i in (start, stop, step)])
    if step*(stop - start) > 0:  # a non-null incr/decr range
        if step < 0:
            for i in frange(-start, -stop, -step, n):
                yield -i
        else:
            steps = round((stop - start)/step)
            while round(step*steps + start, n) < stop:
                steps += 1
            for i in range(steps):
                yield round(start + i*step, n)

Antwoord 19

Is er een bereik() equivalent voor floats in Python?
NEE
Gebruik dit:

def f_range(start, end, step):
    a = range(int(start/0.01), int(end/0.01), int(step/0.01))
    var = []
    for item in a:
        var.append(item*0.01)
    return var

Antwoord 20

Natuurlijk zullen er afrondingsfouten zijn, dus dit is niet perfect, maar dit is wat ik over het algemeen gebruik voor toepassingen die geen hoge precisie vereisen. Als u dit nauwkeuriger wilt maken, kunt u een extra argument toevoegen om aan te geven hoe afrondingsfouten moeten worden afgehandeld. Misschien kan het doorgeven van een afrondingsfunctie dit uitbreidbaar maken en de programmeur in staat stellen te specificeren hoe afrondingsfouten moeten worden afgehandeld.

arange = lambda start, stop, step: [i + step * i for i in range(int((stop - start) / step))]

Als ik schrijf:

arange(0, 1, 0.1)

Het zal uitvoeren:

[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]

Antwoord 21

Er zijn hier verschillende antwoorden die geen eenvoudige randgevallen behandelen, zoals negatieve stap, verkeerde start, stop enz. Hier is de versie die veel van deze gevallen correct afhandelt en hetzelfde gedrag geeft als native range():

def frange(start, stop=None, step=1):
  if stop is None:
    start, stop = 0, start
  steps = int((stop-start)/step)
  for i in range(steps):
    yield start
    start += step  

Houd er rekening mee dat dit een fout zou opleveren voor step=0, net als het native range. Een verschil is dat native bereik een object retourneert dat indexeerbaar en omkeerbaar is, terwijl het bovenstaande dat niet doet.

Je kunt met deze code spelenen hier cases testen.

Previous articleWat is een build-tool?
Next articleJavax vs Java-pakket

Other episodes