range () для поплавків


140

Чи є range()еквівалент плавців у 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

1
Це не дроби, а поплавці. І поплавці ... ну, ймовірно, дадуть інші результати, ніж ви очікували.

6
Швидким рішенням було б трактувати цілі числа як десяткові знаки, наприклад:, range(5, 50, 5)а потім просто ділити кожне число на 10.
NullUserException

@delnan - оновлено. Я готовий прийняти хвилинні неточності для зручності мати float range
Джонатан,


@NullUserException - це лише приклад - реальний код, звичайно, параметричний :)
Джонатан

Відповіді:


97

Я не знаю вбудованої функції, але написання такої не повинно бути надто складним.

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

Як зазначається в коментарях, це може призвести до непередбачуваних результатів, таких як:

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

Щоб отримати очікуваний результат, ви можете скористатися однією з інших відповідей у ​​цьому питанні, або як згадується @Tadhg, ви можете використовувати decimal.Decimalяк jumpаргумент. Переконайтеся, що ініціалізувати його потрібно за допомогою рядка, а не поплавця.

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

Або навіть:

import decimal

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

І потім:

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

34
Девіз Python насправді. Має бути один - і бажано лише один - очевидний спосіб це зробити . Але Python у будь-якому випадку дивовижний :)
Джонатан

3
>>> print list(frange(0,100,0.1))[-1]==100.0будеFalse
Володимир Копей

frangeможе працювати несподівано Наприклад, через прокляття арифметики з плаваючою комою , наприклад, frange(0.0, 1.0, 0.1)виходить 11 значень, де останнє значення 0.9999999999999999. Практичне вдосконалення буде, while x + sys.float_info.epsilon < y:хоча навіть це може бути невдалим при великій кількості .
Акселі Пален

10
-1 Будь ласка, не використовуйте цей код , принаймні, не в програмному забезпеченні, яке коли-небудь може вплинути на моє життя. Немає можливості змусити її надійно працювати. Не використовуйте і відповідь Акселі Пален. Використовуйте відповідь Xaerxess або wim (крім ігнорування частини про помаранчевий).
benrg

3
це чудово працює, якщо ви використовуєтеdecimal.Decimal як крок замість плавців.
Tadhg McDonald-Jensen

112

Ви можете використовувати:

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

або використовувати лямбда / карту:

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

1
І масив (діапазон (5,50,15)) / 10,0 як масивні масиви мають операторів для обробки поділу, множення тощо
edvaldig

2
@edvaldig: ти маєш рацію, я про це не знав ... Тим не менш, я думаю arange(0.5, 5, 1.5), що IMO читабельніше.
Xaerxess

2
Я віддаю перевагу цій відповіді над прийнятою, тому що перші два представлені рішення базуються на ітерації над цілими числами та отриманні кінцевих поплавків з цілих чисел. Це більш надійно. Якщо ви робите це безпосередньо з плавцями, ви ризикуєте мати дивні разові помилки через те, як поплавці представлені всередині. Наприклад, якщо ви намагаєтеся list(frange(0, 1, 0.5)), це працює добре, і 1 виключається, але якщо ви спробуєте list(frange(0, 1, 0.1)), останнє значення, яке ви отримаєте, близьке до 1,0, що, мабуть, не те, що ви хочете. Представлені тут рішення не мають цієї проблеми.
blubberdiblub

3
Ніколи не використовуйте numpy.arange (сама документація numpy рекомендує проти цього). Використовуйте numpy.linspace, як рекомендує wim, або одну з інших пропозицій у цій відповіді.
benrg

79

Я використовував, numpy.arangeале мав деякі ускладнення, контролюючи кількість елементів, які він повертає, через помилки з плаваючою комою. Тому зараз я використовую linspace, наприклад:

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

Однак досі існують помилки з плаваючою комою, не використовуючи decimal, наприклад:np.linspace(-.1,10,num=5050)[0]
ТНТ

2
@TNT Ні, це не помилка. Ви знайдете, що np.linspace(-.1,10,num=5050)[0] == -.1це правда. Це просто те, що repr(np.float64('-0.1'))показує більше цифр.
Вім

1
Хоча цей конкретний приклад не показує надмірної помилки округлення, є випадки відмов. Наприклад, print(numpy.linspace(0, 3, 148)[49])друкує, 0.9999999999999999коли був би ідеальний результат 1.0. linspaceробить набагато кращу роботу, ніж це arange, але це не гарантує отримання мінімально можливої ​​помилки округлення.
user2357112 підтримує Моніку

Це буде гарантовано виконувати правильну обробку кінцевої точки, і завжди справляє саме ви запросили кількість елементів.
user2357112 підтримує Моніку

40

Pylab має frange(фактично обгортку 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. ])

4
Frange застаріла з часу використання matplotlib версії 2.2. numpy.arange слід використовувати.
kuzavas

13

Швидко оцінюється (2.x range):

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

Ліниво оцінені (2.x xrange, 3.x range):

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

По черзі:

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

4
+1; але чому б не (x * .5 for x in range(10))як генераторний вираз для ледачих оцінок?
Тім Піцкер

2
Тому що це було б занадто просто? :)
Карл Кнечтел

11

використовуючи itertools: ліниво оцінений діапазон плаваючої точки:

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

3
+1 для використання itertools.takewhile. Однак itertools.count(start, step)страждає від накопичених помилок з плаваючою комою. (Оцініть, takewhile(lambda x: x < 100, count(0, 0.1))наприклад.) Я б написав takewhile(lambda x: x < stop, (start + i * step for i in count()))замість цього.
musiphil

6

Я допоміг додати функцію numeric_range до пакету more-itertools .

more_itertools.numeric_range(start, stop, step) діє як вбудований діапазон функцій, але може обробляти типи плаваючої, десяткової та дробової.

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

4

Немає такої вбудованої функції, але ви можете використовувати наступне (код Python 3), щоб зробити роботу так безпечно, як це дозволяє Python.

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

Ви можете перевірити все це, виконавши кілька тверджень:

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

Код доступний на GitHub


4

Чому в стандартній бібліотеці відсутня реалізація діапазону плаваючої точки?

Як було зрозуміло в усіх публікаціях тут, версія з плаваючою комою не існує range(). Слід сказати, що упущення має сенс, якщо врахувати, що range()функція часто використовується як генератор індексів (і, звичайно, це означає аксесуар ). Отже, коли ми телефонуємоrange(0,40) , ми фактично заявляємо, що хочемо 40 значень, починаючи від 0, до 40, але не включаючи 40.

Коли ми вважаємо, що генерація індексів стільки ж відповідає кількості індексів, скільки їх значень, використання плавної реалізації range()в стандартній бібліотеці має менше сенсу. Наприклад, якщо ми викликали функціюfrange(0, 10, 0.25) , ми очікували б включення і 0, і 10, але це дасть вектор з 41 значенням.

Таким чином, frange()функція залежно від її використання завжди проявлятиме протилежну інтуїтивну поведінку; він або має занадто багато значень, сприйнятих з точки зору індексації, або не включає число, яке розумно слід повернути з математичної точки зору.

Математичний випадок використання

З урахуванням сказаного, як обговорювалося, numpy.linspace()непогано виконує покоління з математичної точки зору:

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

Випадок використання індексування

А щодо точки зору індексації я написав дещо інший підхід із деякою хитрою магією рядків, яка дозволяє нам визначати кількість десяткових знаків.

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

Аналогічно ми можемо також використовувати вбудовану roundфункцію та вказати кількість десяткових знаків:

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

Швидке порівняння та ефективність

Звичайно, враховуючи вищезгадане обговорення, ці функції мають досить обмежений випадок використання. Тим не менш, ось коротке порівняння:

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)

Результати однакові для кожного:

-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

І деякі терміни:

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

Схоже, метод форматування рядків виграє волосся в моїй системі.

Обмеження

І нарешті, демонстрація суті з обговорення вище та останнє обмеження:

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

Далі, коли skipпараметр не ділиться на stopзначення, може виникнути проміжок позіхання, враховуючи останній випуск:

# 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

Існують способи вирішити це питання, але в кінці дня найкращим підходом було б просто використовувати Numpy.


Це доволі скручений аргумент. range () слід просто подивитися на генератор ітерації, і чи він використовується для циклу, чи для індексації, слід залишити абонентам. Люди використовують поплавці в циклі протягом тисячоліть, і вищевикладені виправдання є безглуздими. Людей з комітетів Python тут закрутили великі часи, і добрі аргументи, ймовірно, були заглушені якимись вивернутими виправданнями, як вище. Це все просто і просто. Зараз існує занадто багато рішень, як вище, закріплених мовою Python.
Shital Shah

3

Розчин без Numpy і т.д. залежностей було надано Kichik але з - за точкову арифметику з плаваючою , вона часто поводиться несподівано. Як відзначили мене і blubberdiblub , додаткові елементи легко прокрадаються до результату. Наприклад naive_frange(0.0, 1.0, 0.1), ви отримаєте 0.999...останнє значення і таким чином отримаєте загалом 11 значень.

Тут надійна версія:

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

Оскільки множення, помилки округлення не накопичуються. Застосування epsilonозначає, що можлива помилка округлення множення, навіть якщо питання, звичайно, можуть виникати в дуже малих і дуже великих кінцях. Тепер, як і очікувалося:

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

І з дещо більшими цифрами:

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

Код також доступний як GitHub Gist .


Це не вдається з паличкою (2,0, 17,0 / 6,0, 1,0 / 6,0). Немає способу зробити це надійним.
benrg

@benrg Дякуємо, що вказали на це! Це призвело до того, що я зрозумів, що епсілон повинен залежати від стрибка, тому я переглянув алгоритм і відремонтував проблему. Ця нова версія набагато надійніша, чи не так?
Акселі Пален

2

Більш проста версія, що не містить бібліотеки

Ах, чорт забираю - я підкину в простій без бібліотеки версії. Не соромтеся покращувати це [*]:

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

Основна ідея полягає в тому, що nstepsце кількість кроків, щоб змусити вас від початку до зупинки і range(nsteps)завжди випускає цілі числа, щоб не було втрати точності. Завершальним кроком є ​​лінійне відображення [0..ступів] лінійно на [start..stop].

редагувати

Якщо, як аланкалвітті, ви хочете, щоб серія мала точне раціональне подання, ви завжди можете використовувати дроби :

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

[*] Зокрема, frange()повертає список, а не генератор. Але цього було достатньо для моїх потреб.


Якщо ви хочете включити значення стопу у висновок, додавши стоп + стрибок, цей метод потім повертається до наївного результату з поганими плаваючими точками посередині, спробуйте frange(0,1.1,0.1)ще більше тих, хто має вибір на кшталтfrange(0,1.05,0.1)
alancalvitti

@alancalvitti: Яке ваше визначення "поганої" плаваючої точки? Так, результати можуть не друкуватись добре, але frange () забезпечує найближчий набір рівномірно розташованих значень у межах подання з плаваючою точкою. Як би ви його покращили?
безстрашний_фол

хороший момент, я так звик до мови високого рівня, де ви могли б перейти до раціональних чисел для такого завдання, що Py відчуває себе як збірка.
alancalvitti

Асамблея? Хрумчу! ;) Звичайно, Python МОЖЕ надати точне представлення з дробами
безстрашний_fool

Правильно, дякую, але, наприклад, мова, яка мені подобається, автоматично перетворює ці типи, тому 1/2 є раціональним, тоді як 1 / 2.0 є плаваючим, немає необхідності декларувати їх як такі - залишайте декларації Java, що навіть більше нижній / складання, ніж Py.
alancalvitti

2

Це можна зробити за допомогою numpy.arange (запуск, зупинка, розмір кроку)

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]

Примітка 1: З обговорення в розділі коментарів тут "ніколи не використовуйте numpy.arange()(нумерована документація сама рекомендує проти цього). Використовуйте numpy.linspace, як рекомендує wim, або одну з інших пропозицій у цій відповіді".

Примітка 2: Я прочитав дискусію в декількох коментарях тут, але після повернення до цього питання вже втретє, я вважаю, що цю інформацію слід розмістити в більш зрозумілій позиції.


2

Як писав кічик , це не повинно бути надто складним. Однак цей код:

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

Недоцільно через сукупний ефект помилок при роботі з поплавками. Ось чому ви отримуєте щось на кшталт:

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

Хоча очікувана поведінка буде:

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

Рішення 1

Сукупну помилку можна просто зменшити за допомогою змінної індексу. Ось приклад:

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

Цей приклад працює як очікувалося.

Рішення 2

Ніяких вкладених функцій. Всього лише час та змінна лічильник:

def frange3(start, stop, step):
    res, n = start, 1

    while res < stop:
        yield res
        res = start + n * step
        n += 1

Ця функція також буде добре працювати, за винятком випадків, коли потрібно змінити діапазон. Наприклад:

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

Рішення 1 у цьому випадку працюватиме як очікувалося. Щоб ця функція працювала в таких ситуаціях, потрібно застосувати хак, подібний до наступного:

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

За допомогою цього злому ви можете використовувати ці функції з негативними кроками:

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

Рішення 3

Ви можете піти далі за допомогою звичайної стандартної бібліотеки і скласти функцію діапазону для більшості числових типів:

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

Цей генератор адаптований із книги Fluent Python (Глава 14. Ітерабелі, ітератори та генератори). Він не працюватиме зі зменшенням діапазонів. Ви повинні застосувати хак, як у попередньому рішенні.

Ви можете використовувати цей генератор наступним чином, наприклад:

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

І звичайно, ви можете використовувати його з float та int .

Будь обережний

Якщо ви хочете використовувати ці функції з негативними кроками, слід додати чек на знак кроку, наприклад:

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

Найкращий варіант тут - підняти StopIteration, якщо ви хочете імітувати саму rangeфункцію.

Мімічний діапазон

Якщо ви хочете наслідувати rangeінтерфейс функції, ви можете надати кілька перевірок аргументів:

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

Я думаю, ти маєш справу. Ви можете скористатися будь-якою з цих функцій (крім першої), і все, що вам потрібно для них, - це стандартна бібліотека python.


1

я написав функцію, яка повертає кордон діапазону подвійних точних чисел з плаваючою комою без жодних знаків після сотих. це було просто питання розбору значень діапазону, таких як рядки, та розділення надлишку. Я використовую його для відображення діапазонів для вибору з інтерфейсу користувача. Я сподіваюся, що хтось інший вважає це корисним.

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

1

Використання

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

Щоб округнути кожен крок до N десяткових знаків

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]

Код

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

Навіщо обирати цю відповідь?

  • Багато інших відповідей будуть висіти, коли їх попросять відлічити.
  • Багато інших відповідей дадуть неправильно округлені результати.
  • Інші відповіді, що базуються на " np.linspaceпотраплянні та пропущенні", можуть працювати або не працювати через труднощі у виборі правильної кількості поділів. np.linspaceДійсно бореться з десятковими кроками 0,1, і порядок поділів у формулі для перетворення приросту в ряд розщеплень може призвести до правильного або зламаного коду.
  • Інші відповіді на основі np.arangeзастаріли.

Якщо ви сумніваєтеся, спробуйте чотири вище тести.


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

Зверніть увагу, що перша літера діапазону - це велика літера. Цей метод іменування не рекомендується використовувати функції Python. Якщо ви хочете, ви можете змінити діапазон на щось на кшталт "помаранчевий". Функція "Діапазон" поводиться так, як ви цього хочете. Ви можете перевірити його посібник тут [ http://reference.wolfram.com/language/ref/Range.html ].


0

Я думаю, що є дуже проста відповідь, яка насправді наслідує всі функції діапазону, але як для float, так і для цілих чисел. У цьому рішенні ви просто припускаєте, що ваше наближення за замовчуванням дорівнює 1e-7 (або вибране вами), і ви можете змінити його під час виклику функції.

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)

0

Звичайно, будуть деякі помилки округлення, тому це не є ідеальним, але це те, що я зазвичай використовую для програм, які не вимагають високої точності. Якщо ви хочете зробити це більш точним, ви можете додати додатковий аргумент, щоб вказати, як поводитися з помилками округлення. Можливо, передача функції округлення може зробити це розширюваним і дозволить програмісту вказати, як поводитися з помилками округлення.

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

Якщо я напишу:

arange(0, 1, 0.1)

Він виведе:

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

-1

Чи є еквівалент діапазону () для плавців у Python? НЕ Використовуйте це:

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

3
Досить погане рішення, спробуйте f_range(0.01,0.02,0.001)... Для більшості практичних цілей arangeвід Numpy - це просте, безпечне та швидке рішення.
Барт

Ти правий. З numpy це на 1,8 швидше, ніж мій код.
Григор Колев

Ти правий. З numpy це на 1,8 швидше, ніж мій код. Але система, де я працюю, повністю закрита. Тільки Python і pyserial більше немає.
Григор Колев

-2

Тут є декілька відповідей, які не обробляють прості крайові випадки, такі як негативний крок, неправильний запуск, зупинка тощо. Ось версія, яка обробляє багато з цих випадків правильно, визначаючи таку ж поведінку, як і рідна 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  

Зауважте, що це призведе до помилки step = 0 так само, як і рідного range. Одна відмінність полягає в тому, що нативний діапазон повертає об'єкт, який піддається індексації та обороту, а вище - ні.

Ви можете грати з цим кодом і тестувати тут.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.