webentwicklung-frage-antwort-db.com.de

bereich () für Schwimmer

Gibt es ein range()-Äquivalent für 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
109
Jonathan

Ich kenne keine eingebaute Funktion, aber das Schreiben von this sollte nicht zu kompliziert sein.

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

Wie in den Kommentaren erwähnt, kann dies zu unvorhersehbaren Ergebnissen führen, z.

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

Um das erwartete Ergebnis zu erhalten, können Sie eine der anderen Antworten in dieser Frage verwenden oder als @Tadhg erwähntes Argument decimal.Decimal als Argument jump verwenden. Stellen Sie sicher, dass Sie ihn mit einem String anstelle eines Floats initialisieren.

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

Oder auch:

import decimal

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

Und dann:

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

Sie können entweder verwenden:

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

oder benutze lambda/map:

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

Ich habe numpy.arange verwendet, hatte jedoch einige Komplikationen, um die Anzahl der Elemente zu kontrollieren, die aufgrund von Fließkommafehlern zurückgegeben werden. Also benutze ich jetzt linspace, z.

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

Pylab hat frange (eigentlich einen Wrapper für 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. ])
37
Pat

Eifrig bewertet (2.x range):

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

Lazily ausgewertet (2.x xrange, 3.x range):

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

Abwechselnd:

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

using itertools: faul ausgewerteter Fließkommabereich:

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

Ich habe geholfen, die Funktion numeric_range zum Paket more-itertools hinzuzufügen.

more_itertools.numeric_range(start, stop, step) verhält sich wie der eingebaute Funktionsbereich, kann jedoch mit Floats, Decimal und Fraction-Typen umgehen.

>>> from more_itertools import numeric_range
>>> Tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)
4
William Rusnack

Es gibt keine solche integrierte Funktion, aber Sie können den folgenden Code (Python 3-Code) verwenden, um den Job so sicher zu erledigen, wie es Python erlaubt.

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

Sie können dies alles überprüfen, indem Sie einige Assertions ausführen:

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 verfügbar auf GitHub

3
marcotama

Eine Lösung ohne numpy etc Abhängigkeiten wurde von kichik bereitgestellt, aber aufgrund von der Gleitkomma-Arithmetik verhält es sich oft unerwartet. Wie von me und blubberdiblub angemerkt, schleichen sich leicht zusätzliche Elemente in das Ergebnis ein. Zum Beispiel würde naive_frange(0.0, 1.0, 0.1)0.999... als letzten Wert und somit insgesamt 11 Werte ergeben.

Eine robuste Version wird hier bereitgestellt: 

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
        yield x

Wegen der Multiplikation häufen sich die Rundungsfehler nicht an. Die Verwendung von epsilon sorgt für mögliche Rundungsfehler der Multiplikation, auch wenn die Probleme in den sehr kleinen und sehr großen Enden natürlich steigen könnten. Nun wie erwartet:

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

Und mit etwas größeren Zahlen:

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

Der Code ist auch als a GitHub Gist verfügbar.

3
Akseli Palén

Warum gibt es keine Floating Point Range-Implementierung in der Standardbibliothek?

Wie alle Beiträge hier klargestellt haben, gibt es keine Fließkomma-Version von range(). Das Auslassen macht jedoch Sinn, wenn wir bedenken, dass die Funktion range() häufig als Index (und natürlich als Accessor) - Generator verwendet wird. Wenn wir also range(0,40) aufrufen, sagen wir tatsächlich, wir wollen 40 Werte von 0 bis 40, aber nicht von 40 selbst. 

Wenn wir der Ansicht sind, dass es bei der Indexerstellung genauso um die Anzahl der Indizes wie um ihre Werte geht, ist die Verwendung einer Float-Implementierung von range() in der Standardbibliothek weniger sinnvoll. Wenn wir zum Beispiel die Funktion frange(0, 10, 0.25) aufrufen, würden wir erwarten, dass sowohl 0 als auch 10 eingeschlossen werden. Dies würde jedoch einen Vektor mit 41 Werten ergeben. 

Daher zeigt eine frange()-Funktion, abhängig von ihrer Verwendung, immer ein intuitives Verhalten. Es hat entweder zu viele Werte, wie aus der Indexierungsperspektive wahrgenommen, oder enthält keine Zahlen, die aus mathematischer Sicht vernünftigerweise zurückgegeben werden sollten.

Der mathematische Anwendungsfall

Wie gesagt, führt numpy.linspace() die Generierung mit der mathematischen Perspektive gut aus:

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

Der Indizierungsanwendungsfall

Und für die Indizierungsperspektive habe ich einen etwas anderen Ansatz mit etwas trickreicher String-Magie geschrieben, mit dem wir die Anzahl der Dezimalstellen angeben können.

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

In ähnlicher Weise können wir auch die integrierte round-Funktion verwenden und die Anzahl der Dezimalstellen angeben:

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

Ein schneller Vergleich und Leistung

In Anbetracht der obigen Erörterung haben diese Funktionen natürlich einen ziemlich begrenzten Anwendungsfall. Trotzdem hier ein schneller Vergleich:

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)

Die Ergebnisse sind für jedes identisch:

-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

Und einige Timings:

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

Sieht aus, als ob die String-Formatierungsmethode durch ein Haar auf meinem System gewinnt. 

Die Einschränkungen

Und schließlich eine Demonstration des Punktes aus der obigen Diskussion und eine letzte Einschränkung:

# "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

Wenn der skip-Parameter nicht durch den stop-Wert teilbar ist, kann es aufgrund des letzteren Problems zu einer gähnenden Lücke kommen:

# 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

Es gibt Möglichkeiten, dieses Problem anzugehen, aber am Ende des Tages wäre es wahrscheinlich am besten, Numpy zu verwenden.

2
Greenstick

Eine einfachere Version ohne Bibliothek

Aw, verdammt noch mal - ich werfe in einer einfachen Version ohne Bibliothek. Fühlen Sie sich frei, um es zu verbessern [*]:

def frange(start=0, stop=1, jump=0.1):
    nsteps = int((stop-start)/jump)
    dy = stop-start
    # f(i) goes from start to stop as i goes from 0 to nsteps
    return [start + float(i)*dy/nsteps for i in range(nsteps)]

Die Grundidee ist, dass nsteps die Anzahl der Schritte ist, die Sie von Anfang bis Ende benötigen, und range(nsteps) immer ganze Zahlen ausgibt. Der letzte Schritt besteht darin, [0..nsteps] linear auf [start..stop] abzubilden.

bearbeiten

Wenn Sie, wie alancalvitti möchten, dass die Serie eine genaue rationale Darstellung hat, können Sie Fractions immer verwenden:

from fractions import Fraction

def rrange(start=0, stop=1, jump=0.1):
    nsteps = int((stop-start)/jump)
    return [Fraction(i, nsteps) for i in range(nsteps)]

[*] Insbesondere gibt frange() eine Liste zurück, keinen Generator. Aber es genügte für meine Bedürfnisse.

1
fearless_fool

ich habe eine Funktion geschrieben, die ein Tupel aus einem Bereich von Gleitkommazahlen mit doppelter Genauigkeit ohne Dezimalstellen jenseits der Hundertstel zurückgibt. Es ging lediglich darum, die Range-Werte wie Strings zu analysieren und den Überschuss abzuspalten. Ich verwende es zum Anzeigen von Bereichen, um innerhalb einer Benutzeroberfläche auszuwählen. Ich hoffe, jemand anderes findet es nützlich.

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
0
chris mcinnis

Hier gibt es mehrere Antworten, die keine einfachen Edge-Fälle wie negativen Schritt, falschen Start, Stopp usw. behandeln. Hier ist die Version, die viele dieser Fälle richtig behandelt und dasselbe Verhalten wie native range() angibt:

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  

Beachten Sie, dass dies zu einem Fehler von step = 0 führen würde, genau wie bei der range. Ein Unterschied besteht darin, dass der native Bereich ein Objekt zurückgibt, das indizierbar und reversibel ist, während das Objekt oben nicht gilt.

Sie können mit diesem Code und Testfällen hier spielen.

0
Shital Shah