Czy istnieje range()
odpowiednik dla pływaków w Pythonie?
>>> 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)
, a następnie dzielenie każdej liczby przez 10.Odpowiedzi:
Nie znam wbudowanej funkcji, ale napisanie takiej nie powinno być zbyt skomplikowane.Jak wspominają komentarze, może to spowodować nieprzewidywalne wyniki, takie jak:
Aby uzyskać oczekiwany wynik, możesz użyć jednej z pozostałych odpowiedzi w tym pytaniu lub, jak wspomniał @Tadhg, możesz użyć
decimal.Decimal
jakojump
argumentu. Pamiętaj, aby zainicjować go za pomocą łańcucha, a nie liczby zmiennoprzecinkowej.Lub nawet:
I wtedy:
źródło
>>> print list(frange(0,100,0.1))[-1]==100.0
będzieFalse
frange
może zadziałać nieoczekiwanie. Ze względu na klątwę arytmetyki zmiennoprzecinkowej , na przykładfrange(0.0, 1.0, 0.1)
daje 11 wartości, gdzie ostatnia wartość jest0.9999999999999999
. Praktyczna poprawa byłaby możliwa,while x + sys.float_info.epsilon < y:
chociaż nawet to może się nie udać w przypadku dużej liczby .decimal.Decimal
jako kroku zamiast pływaków.Możesz użyć:
lub użyj lambda / map:
źródło
arange(0.5, 5, 1.5)
IMO jest bardziej czytelny.list(frange(0, 1, 0.5))
, działa dobrze i 1 jest wykluczone, ale jeśli spróbujeszlist(frange(0, 1, 0.1))
, ostatnia otrzymana wartość jest bliska 1,0, co prawdopodobnie nie jest tym, czego chcesz. Przedstawione tutaj rozwiązania nie mają tego problemu.Kiedyś używałem,
numpy.arange
ale miałem pewne komplikacje w kontrolowaniu liczby elementów, które zwraca, z powodu błędów zmiennoprzecinkowych. Więc teraz używamlinspace
np:źródło
decimal
np .:np.linspace(-.1,10,num=5050)[0]
np.linspace(-.1,10,num=5050)[0] == -.1
jest prawdą. Po prosturepr(np.float64('-0.1'))
pokazuje więcej cyfr.print(numpy.linspace(0, 3, 148)[49])
drukuje,0.9999999999999999
gdy byłby idealny wynik1.0
.linspace
wykonuje znacznie lepszą pracę niżarange
, ale nie gwarantuje wygenerowania minimalnego możliwego błędu zaokrągleń.Pylab ma
frange
(właściwie opakowaniematplotlib.mlab.frange
):źródło
Chętnie oceniane (2.x
range
):Oceniane leniwie (2.x
xrange
, 3.xrange
):Na przemian:
źródło
(x * .5 for x in range(10))
jako wyrażenie generujące leniwą ocenę?używanie
itertools
: leniwie obliczany zakres zmiennoprzecinkowy:źródło
itertools.takewhile
. Jednakitertools.count(start, step)
cierpi na skumulowane błędy zmiennoprzecinkowe. (Oceńtakewhile(lambda x: x < 100, count(0, 0.1))
na przykład.)takewhile(lambda x: x < stop, (start + i * step for i in count()))
Zamiast tego napisałbym .Pomogłem dodać funkcję numeric_range do pakietu more-itertools .
more_itertools.numeric_range(start, stop, step)
działa jak wbudowany zakres funkcji, ale może obsługiwać typy zmiennoprzecinkowe, dziesiętne i ułamkowe.źródło
Nie ma takiej wbudowanej funkcji, ale możesz użyć następującego (kod Python 3), aby wykonać zadanie tak bezpiecznie, jak pozwala na to Python.
Możesz to wszystko zweryfikować, uruchamiając kilka asercji:
Kod dostępny na GitHub
źródło
Dlaczego w bibliotece standardowej nie ma implementacji zakresu zmiennoprzecinkowego?
Jak wyjaśniono we wszystkich zamieszczonych tutaj postach, nie ma wersji zmiennoprzecinkowej
range()
. To powiedziawszy, pominięcie ma sens, jeśli weźmiemy pod uwagę, żerange()
funkcja jest często używana jako generator indeksu (i oczywiście oznacza to akcesor ). Tak więc, kiedy wywołujemyrange(0,40)
, w efekcie mówimy, że chcemy 40 wartości zaczynających się od 0, do 40, ale bez wartości 40.Kiedy weźmiemy pod uwagę, że generowanie indeksu dotyczy w takim samym stopniu liczby indeksów, jak ich wartości, użycie implementacji typu float
range()
w bibliotece standardowej ma mniejszy sens. Na przykład, jeśli nazwiemy funkcjęfrange(0, 10, 0.25)
, spodziewalibyśmy się uwzględnienia zarówno 0, jak i 10, ale dałoby to wektor z 41 wartościami.Zatem
frange()
funkcja zależna od jej użycia zawsze będzie wykazywać sprzeczne z intuicją zachowanie; albo ma zbyt wiele wartości postrzeganych z perspektywy indeksowania, albo nie obejmuje liczby, która powinna zostać zwrócona z matematycznego punktu widzenia.Matematyczny przypadek użycia
Powiedziawszy to, jak omówiono,
numpy.linspace()
ładnie wypada generowanie z perspektywy matematycznej:Przypadek użycia indeksowania
A jeśli chodzi o perspektywę indeksowania, napisałem nieco inne podejście z pewną sztuczną magią ciągów, która pozwala nam określić liczbę miejsc dziesiętnych.
Podobnie możemy również skorzystać z funkcji wbudowanej
round
i określić liczbę miejsc po przecinku:Szybkie porównanie i wydajność
Oczywiście, biorąc pod uwagę powyższe omówienie, funkcje te mają dość ograniczony przypadek użycia. Niemniej jednak, oto krótkie porównanie:
Wyniki są identyczne dla każdego:
I niektóre czasy:
Wygląda na to, że metoda formatowania ciągów wygrywa w moim systemie.
Ograniczenia
Na koniec zademonstrowanie punktu z powyższej dyskusji i ostatnie ograniczenie:
Ponadto, gdy
skip
parametr nie jest podzielny przezstop
wartość, może wystąpić luka ziewania, biorąc pod uwagę ten drugi problem:Istnieją sposoby rozwiązania tego problemu, ale ostatecznie najlepszym podejściem byłoby po prostu użycie Numpy.
źródło
Rozwiązanie bez NumPy itp zależnościami były dostarczone przez Kichik ale ze względu na pływających arytmetyki punktowych , często zachowuje się niespodziewanie. Jak zauważyłem przeze mnie i blubberdiblub , dodatkowe elementy łatwo wkradają się do wyniku. Na przykład
naive_frange(0.0, 1.0, 0.1)
dałby0.999...
jako ostatnią wartość, a tym samym dałby łącznie 11 wartości.Solidna wersja jest dostępna tutaj:
Ponieważ mnożenie, błędy zaokrąglania nie kumulują się. Użycie
epsilon
znaku eliminuje ewentualny błąd zaokrąglenia mnożenia, chociaż oczywiście mogą pojawić się problemy na bardzo małych i bardzo dużych końcach. Teraz, zgodnie z oczekiwaniami:I z nieco większymi liczbami:
Kod jest również dostępny jako GitHub Gist .
źródło
Prostsza wersja bez biblioteki
Do diabła - dorzucę prostą wersję bez biblioteki. Zapraszam do ulepszania go [*]:
Podstawową ideą jest
nsteps
liczba kroków, które prowadzą od początku do końca irange(nsteps)
zawsze emituje liczby całkowite, więc nie ma utraty dokładności. Ostatnim krokiem jest liniowe odwzorowanie [0..nsteps] na [start..stop].edytować
Jeśli, tak jak alancalvitti, chcesz, aby szereg miał dokładną racjonalną reprezentację, zawsze możesz użyć ułamków :
[*] W szczególności
frange()
zwraca listę, a nie generator. Ale to wystarczyło na moje potrzeby.źródło
frange(0,1.1,0.1)
i jeszcze więcej tych z wyborem, takim jakfrange(0,1.05,0.1)
Uwaga 1: Z dyskusji w sekcji komentarzy tutaj: „nigdy nie używaj
numpy.arange()
(sama dokumentacja numpy odradza). Użyj numpy.linspace zgodnie z zaleceniami wim lub jednej z innych sugestii w tej odpowiedzi”Uwaga 2: Przeczytałem dyskusję w kilku komentarzach tutaj, ale po powrocie do tego pytania po raz trzeci uważam, że ta informacja powinna zostać umieszczona w bardziej czytelnym miejscu.
źródło
Jak napisał Kichik , nie powinno to być zbyt skomplikowane. Jednak ten kod:
Jest nieodpowiedni ze względu na skumulowany efekt błędów podczas pracy z pływakami. Dlatego otrzymujesz coś takiego:
Chociaż oczekiwane zachowanie byłoby:
Rozwiązanie 1
Skumulowany błąd można po prostu zmniejszyć za pomocą zmiennej indeksującej. Oto przykład:
Ten przykład działa zgodnie z oczekiwaniami.
Rozwiązanie 2
Brak funkcji zagnieżdżonych. Tylko chwila i zmienna licznika:
Ta funkcja również będzie działać dobrze, z wyjątkiem przypadków, w których chcesz odwrócić zakres. Na przykład:
Rozwiązanie 1 w tym przypadku będzie działać zgodnie z oczekiwaniami. Aby ta funkcja działała w takich sytuacjach, musisz zastosować hack, podobny do następującego:
Dzięki temu hackowi możesz używać tych funkcji z negatywnymi krokami:
Rozwiązanie 3
Możesz pójść jeszcze dalej dzięki zwykłej bibliotece standardowej i skomponować funkcję zakresu dla większości typów liczbowych:
Ten generator jest zaadaptowany z książki Fluent Python (Rozdział 14. Iterables, Iterators and Generators). Nie będzie działać przy malejących zakresach. Musisz zastosować hack, podobnie jak w poprzednim rozwiązaniu.
Możesz użyć tego generatora w następujący sposób, na przykład:
I oczywiście możesz go również używać z float i int .
Bądź ostrożny
Jeśli chcesz używać tych funkcji z krokami ujemnymi, powinieneś dodać czek na znak kroku, np:
Najlepszą opcją jest podbicie
StopIteration
, jeśli chcesz naśladowaćrange
samą funkcję.Zasięg naśladowania
Jeśli chcesz naśladować
range
interfejs funkcji, możesz zapewnić sprawdzanie argumentów:Myślę, że masz rację. Możesz korzystać z dowolnej z tych funkcji (z wyjątkiem pierwszej) i wszystko, czego potrzebujesz do nich, to standardowa biblioteka Pythona.
źródło
Napisałem funkcję, która zwraca krotkę z zakresu liczb zmiennoprzecinkowych o podwójnej precyzji bez żadnych miejsc dziesiętnych poza częściami setnymi. chodziło po prostu o przeanalizowanie wartości zakresu, takich jak łańcuchy, i oddzielenie nadmiaru. Używam go do wyświetlania zakresów do wyboru w interfejsie użytkownika. Mam nadzieję, że ktoś inny uzna to za przydatne.
źródło
Stosowanie
Zaokrąglić każdy krok do N miejsc dziesiętnych
Kod
Dlaczego wybrać tę odpowiedź?
np.linspace
trafieniach i chybieniach mogą, ale nie muszą, działać z powodu trudności w wyborze prawidłowej liczby dywizji.np.linspace
naprawdę zmaga się z przyrostami dziesiętnymi wynoszącymi 0,1, a kolejność podziałów w formule służąca do konwersji przyrostu na liczbę podziałów może skutkować poprawnym lub uszkodzonym kodem.np.arange
są przestarzałe.W razie wątpliwości wypróbuj cztery powyższe przypadki testowe.
źródło
Należy pamiętać, że pierwsza litera zakresu jest duża. Ta metoda nazewnictwa nie jest zalecana dla funkcji w Pythonie. Jeśli chcesz, możesz zmienić zakres na coś takiego jak drange lub frange. Funkcja „Zakres” zachowuje się tak, jak tego chcesz. Możesz sprawdzić jego instrukcję tutaj [ http://reference.wolfram.com/language/ref/Range.html ].
źródło
Myślę, że istnieje bardzo prosta odpowiedź, która naprawdę emuluje wszystkie cechy zakresu, ale zarówno dla liczby zmiennoprzecinkowej, jak i całkowitej. W tym rozwiązaniu po prostu załóżmy, że twoje przybliżenie domyślnie to 1e-7 (lub takie, które wybierzesz) i możesz je zmienić, wywołując funkcję.
źródło
Będą oczywiście pewne błędy zaokrągleń, więc nie jest to idealne, ale to jest to, co zwykle używam w aplikacjach, które nie wymagają dużej precyzji. Jeśli chcesz, aby to było dokładniejsze, możesz dodać dodatkowy argument, aby określić, jak postępować z błędami zaokrąglania. Być może przekazanie funkcji zaokrąglającej może uczynić ją rozszerzalną i pozwolić programiście określić, jak postępować z błędami zaokrąglania.
Jeśli napiszę:
Wyświetli:
źródło
Czy istnieje odpowiednik range () dla liczb zmiennoprzecinkowych w Pythonie? NIE Użyj tego:
źródło
f_range(0.01,0.02,0.001)
... Dla większości celów praktycznych,arange
z NumPy jest proste, bezpieczne i szybkie rozwiązanie.Istnieje kilka odpowiedzi, które nie obsługują prostych przypadków skrajnych, takich jak krok ujemny, zły start, stop itp. Oto wersja, która obsługuje wiele z tych przypadków poprawnie, zachowując takie samo zachowanie jak natywny
range()
:Zauważ, że spowoduje to błąd step = 0, tak jak native
range
. Jedna różnica polega na tym, że zakres natywny zwraca obiekt, który jest indeksowalny i odwracalny, podczas gdy powyższy nie.Możesz bawić się tym kodem i testami tutaj.
źródło