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
range(5, 50, 5)
Und dann einfach jede Zahl durch 10 zu teilen.Antworten:
Ich weiß nicht , eine eingebaute Funktion, aber Schreiben ist eine wie diese nicht zu kompliziert sein sollte.Wie in den Kommentaren erwähnt, kann dies zu unvorhersehbaren Ergebnissen führen wie:
Um das erwartete Ergebnis zu erhalten, können Sie eine der anderen Antworten in dieser Frage verwenden oder, wie @Tadhg erwähnt,
decimal.Decimal
alsjump
Argument verwenden. Stellen Sie sicher, dass Sie es mit einem String und nicht mit einem Float initialisieren.Oder auch:
Und dann:
quelle
>>> print list(frange(0,100,0.1))[-1]==100.0
wird seinFalse
frange
kann unerwartet arbeiten. Aufgrund des Fluches der Gleitkomma-Arithmetik ergeben sich beispielsweisefrange(0.0, 1.0, 0.1)
11 Werte, wobei der letzte Wert ist0.9999999999999999
. Eine praktische Verbesserung wäre,while x + sys.float_info.epsilon < y:
obwohl selbst dies bei großen Zahlen wahrscheinlich scheitern kann .decimal.Decimal
als Schritt anstelle von Floats verwenden.Sie können entweder verwenden:
oder benutze Lambda / Karte:
quelle
arange(0.5, 5, 1.5)
IMO besser lesbar ist.list(frange(0, 1, 0.5))
, funktioniert es einwandfrei und 1 wird ausgeschlossen. Wenn Sie es jedoch versuchenlist(frange(0, 1, 0.1))
, liegt der letzte Wert nahe bei 1,0, was wahrscheinlich nicht das ist, was Sie wollen. Die hier vorgestellten Lösungen haben dieses Problem nicht.Früher habe ich verwendet, hatte
numpy.arange
aber aufgrund von Gleitkommafehlern einige Komplikationen bei der Steuerung der Anzahl der zurückgegebenen Elemente. Also benutze ich jetztlinspace
zB:quelle
decimal
zB:np.linspace(-.1,10,num=5050)[0]
np.linspace(-.1,10,num=5050)[0] == -.1
ist wahr. Es ist nur so, dass dasrepr(np.float64('-0.1'))
mehr Ziffern zeigt.print(numpy.linspace(0, 3, 148)[49])
druckt ,0.9999999999999999
wenn das ideale Ergebnis wäre1.0
.linspace
macht einen viel besseren Job alsarange
, aber es ist nicht garantiert, dass der minimal mögliche Rundungsfehler erzeugt wird.Pylab hat
frange
(eigentlich eine Hülle fürmatplotlib.mlab.frange
):quelle
Eifrig bewertet (2.x
range
):Faul bewertet (2.x
xrange
, 3.xrange
):Abwechselnd:
quelle
(x * .5 for x in range(10))
als Generatorausdruck für faule Auswertung?using
itertools
: träge ausgewerteter Gleitkommabereich:quelle
itertools.takewhile
. Leidet jedochitertools.count(start, step)
unter akkumulierten Gleitkommafehlern. (takewhile(lambda x: x < 100, count(0, 0.1))
Zum Beispiel auswerten .) Ich würdetakewhile(lambda x: x < stop, (start + i * step for i in count()))
stattdessen schreiben .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 integrierte Funktionsbereich, kann jedoch Gleitkomma-, Dezimal- und Bruchteile verarbeiten.quelle
Es gibt keine solche integrierte Funktion, aber Sie können Folgendes verwenden (Python 3-Code), um die Arbeit so sicher auszuführen, wie es Python zulässt.
Sie können alles überprüfen, indem Sie einige Zusicherungen ausführen:
Code auf GitHub verfügbar
quelle
Warum gibt es in der Standardbibliothek keine Implementierung eines Gleitkommabereichs?
Wie aus allen Beiträgen hier hervorgeht, gibt es keine Gleitkommaversion von
range()
. Das Auslassen ist jedoch sinnvoll, wenn man bedenkt, dass dierange()
Funktion häufig als Indexgenerator (und natürlich als Accessor- Generator) verwendet wird. Wenn wir also anrufenrange(0,40)
, sagen wir tatsächlich, wir wollen 40 Werte, die bei 0 beginnen, bis zu 40, aber ohne 40 selbst.Wenn wir bedenken, dass es bei der Indexgenerierung sowohl um die Anzahl der Indizes als auch um deren Werte geht, ist die Verwendung einer Float-Implementierung
range()
in der Standardbibliothek weniger sinnvoll. Wenn wir zum Beispiel die Funktionfrange(0, 10, 0.25)
aufrufen würden, würden wir erwarten, dass sowohl 0 als auch 10 enthalten sind, aber das würde einen Vektor mit 41 Werten ergeben.Daher zeigt eine
frange()
Funktion in Abhängigkeit von ihrer Verwendung immer ein kontraintuitives Verhalten. Entweder hat es zu viele Werte, wie aus der Indexierungsperspektive wahrgenommen, oder es enthält keine Zahl, die aus mathematischer Sicht vernünftigerweise zurückgegeben werden sollte.Der mathematische Anwendungsfall
Wie bereits erwähnt,
numpy.linspace()
führt die Generierung die Generierung mit der mathematischen Perspektive gut durch:Der Anwendungsfall für die Indizierung
Und für die Indizierungsperspektive habe ich einen etwas anderen Ansatz mit etwas kniffliger String-Magie geschrieben, mit dem wir die Anzahl der Dezimalstellen angeben können.
In ähnlicher Weise können wir auch die integrierte
round
Funktion verwenden und die Anzahl der Dezimalstellen angeben:Ein schneller Vergleich und Leistung
Angesichts der obigen Diskussion haben diese Funktionen natürlich einen ziemlich begrenzten Anwendungsfall. Hier ein kurzer Vergleich:
Die Ergebnisse sind jeweils identisch:
Und einige Timings:
Sieht aus wie 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:
Wenn der
skip
Parameter nicht durch denstop
Wert teilbar ist, kann es bei letzterem Problem zu einer gähnenden Lücke kommen:Es gibt Möglichkeiten, dieses Problem zu beheben, aber am Ende des Tages wäre es wahrscheinlich der beste Ansatz, nur Numpy zu verwenden.
quelle
Eine Lösung ohne numpy usw. Abhängigkeiten wurde von kichik bereitgestellt, aber aufgrund der Gleitkomma-Arithmetik verhält es sich oft unerwartet. Wie von mir und blubberdiblub festgestellt , schleichen sich zusätzliche Elemente leicht in das Ergebnis ein. Zum Beispiel
naive_frange(0.0, 1.0, 0.1)
würde0.999...
als letzter Wert ergeben und somit insgesamt 11 Werte ergeben.Eine robuste Version finden Sie hier:
Aufgrund der Multiplikation häufen sich die Rundungsfehler nicht an. Die Verwendung von
epsilon
sorgt für einen möglichen Rundungsfehler der Multiplikation, obwohl natürlich Probleme in den sehr kleinen und sehr großen Bereichen auftreten können. Nun, wie erwartet:Und mit etwas größeren Zahlen:
Der Code ist auch als GitHub Gist verfügbar .
quelle
Eine einfachere Version ohne Bibliothek
Aw, zum Teufel - ich werde eine einfache Version ohne Bibliothek einwerfen. Fühlen Sie sich frei, es zu verbessern [*]:
Die Kernidee ist, dass
nsteps
die Anzahl der Schritte von Anfang bis Ende reicht undrange(nsteps)
immer ganze Zahlen ausgibt, damit keine Genauigkeitsverluste auftreten. Der letzte Schritt besteht darin, [0..nsteps] linear auf [start..stop] abzubilden.bearbeiten
Wenn Sie wie bei alancalvitti eine exakte rationale Darstellung der Serie wünschen , können Sie immer Brüche verwenden :
[*] Gibt insbesondere
frange()
eine Liste zurück, keinen Generator. Aber es genügte für meine Bedürfnisse.quelle
frange(0,1.1,0.1)
mit noch mehr von denen mit einer Auswahl wiefrange(0,1.05,0.1)
Hinweis 1: Aus der Diskussion im Kommentarbereich hier: "Niemals verwenden
numpy.arange()
(die Numpy-Dokumentation selbst empfiehlt dagegen). Verwenden Sie numpy.linspace, wie von wim empfohlen, oder einen der anderen Vorschläge in dieser Antwort."Anmerkung 2: Ich habe die Diskussion hier in einigen Kommentaren gelesen, aber nachdem ich jetzt zum dritten Mal auf diese Frage zurückgekommen bin, sollte diese Information meiner Meinung nach besser lesbar sein.
quelle
Wie Kichik schrieb, sollte dies nicht zu kompliziert sein. Jedoch dieser Code:
Ist wegen der kumulativen Auswirkung von Fehlern bei der Arbeit mit Floats unangemessen . Deshalb erhalten Sie so etwas wie:
Während das erwartete Verhalten wäre:
Lösung 1
Der kumulative Fehler kann einfach mithilfe einer Indexvariablen reduziert werden. Hier ist das Beispiel:
Dieses Beispiel funktioniert wie erwartet.
Lösung 2
Keine verschachtelten Funktionen. Nur eine Weile und eine Zählervariable:
Diese Funktion funktioniert auch gut, außer in den Fällen, in denen Sie den umgekehrten Bereich wünschen. Z.B:
Lösung 1 funktioniert in diesem Fall wie erwartet. Damit diese Funktion in solchen Situationen funktioniert, müssen Sie einen Hack anwenden, der dem folgenden ähnelt:
Mit diesem Hack können Sie diese Funktionen mit negativen Schritten verwenden:
Lösung 3
Mit der einfachen Standardbibliothek können Sie noch weiter gehen und eine Bereichsfunktion für die meisten numerischen Typen erstellen:
Dieser Generator wurde aus dem Fluent Python-Buch (Kapitel 14. Iterables, Iteratoren und Generatoren) übernommen. Es funktioniert nicht mit abnehmenden Bereichen. Sie müssen einen Hack anwenden, wie in der vorherigen Lösung.
Sie können diesen Generator beispielsweise wie folgt verwenden:
Und natürlich können Sie es auch mit float und int verwenden .
Achtung
Wenn Sie diese Funktionen mit negativen Schritten verwenden möchten, sollten Sie das Schrittzeichen überprüfen, z. B.:
Die beste Option ist hier das Erhöhen
StopIteration
, wenn Sie dierange
Funktion selbst nachahmen möchten .Mimic Reichweite
Wenn Sie die
range
Funktionsschnittstelle nachahmen möchten , können Sie einige Argumentprüfungen durchführen:Ich denke, du hast den Punkt. Sie können mit jeder dieser Funktionen (außer der allerersten) arbeiten und alles, was Sie für sie benötigen, ist eine Python-Standardbibliothek.
quelle
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 einfach darum, die Bereichswerte wie Zeichenfolgen 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.
quelle
Verwendung
Rundung jedes Schritts auf N Dezimalstellen
Code
Warum diese Antwort wählen?
np.linspace
sind Treffer und Fehler. Sie können aufgrund von Schwierigkeiten bei der Auswahl der richtigen Anzahl von Abteilungen funktionieren oder auch nicht.np.linspace
Wirklich Probleme mit Dezimalinkrementen von 0,1, und die Reihenfolge der Unterteilungen in der Formel, um das Inkrement in eine Anzahl von Teilungen umzuwandeln, kann entweder zu korrektem oder fehlerhaftem Code führen.np.arange
sind veraltet.Versuchen Sie im Zweifelsfall die vier oben genannten Testfälle.
quelle
Bitte beachten Sie, dass der erste Buchstabe von Range Großbuchstaben ist. Diese Benennungsmethode wird für Funktionen in Python nicht empfohlen. Sie können Range in Drange oder Frange ändern, wenn Sie möchten. Die Funktion "Bereich" verhält sich so, wie Sie es möchten. Sie können das Handbuch hier überprüfen [ http://reference.wolfram.com/language/ref/Range.html ].
quelle
Ich denke, dass es eine sehr einfache Antwort gibt, die wirklich alle Funktionen des Bereichs emuliert, außer für float und integer. In dieser Lösung nehmen Sie einfach an, dass Ihre Annäherung standardmäßig 1e-7 ist (oder die von Ihnen gewählte) und Sie können sie ändern, wenn Sie die Funktion aufrufen.
quelle
Es wird natürlich einige Rundungsfehler geben, daher ist dies nicht perfekt, aber dies ist das, was ich allgemein für Anwendungen verwende, die keine hohe Präzision erfordern. Wenn Sie dies genauer machen möchten, können Sie ein zusätzliches Argument hinzufügen, um anzugeben, wie mit Rundungsfehlern umgegangen werden soll. Möglicherweise kann das Übergeben einer Rundungsfunktion diese erweiterbar machen und es dem Programmierer ermöglichen, anzugeben, wie mit Rundungsfehlern umgegangen werden soll.
Wenn ich schreibe:
Es wird ausgegeben:
quelle
Gibt es ein range () -Äquivalent für Floats in Python? NEIN Verwenden Sie diese:
quelle
f_range(0.01,0.02,0.001)
... Für die meisten praktischen Zwecke istarange
Numpy eine einfache, sichere und schnelle Lösung.Hier gibt es mehrere Antworten, die keine einfachen Kantenfälle wie negative Schritte, falscher Start, Stopp usw. behandeln. Hier ist die Version, die viele dieser Fälle korrekt behandelt und dasselbe Verhalten wie native gibt
range()
:Beachten Sie, dass dies genau wie native den Fehler step = 0 auslösen würde
range
. Ein Unterschied besteht darin, dass der native Bereich ein Objekt zurückgibt, das indizierbar und reversibel ist, während dies oben nicht der Fall ist.Sie können hier mit diesem Code und Testfällen spielen.
quelle