şamandıralar için aralık ()


140

range()Python'da şamandıralar için bir eşdeğer var mı ?

>>> 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
Bunlar kesirler değil, yüzüyorlar. Ve şamandıralar ... iyi, beklediğinizden farklı sonuçlar verecek.

6
Hızlı bir çözüm, tamsayılara ondalık sayı gibi davranmak, örneğin: range(5, 50, 5)ve sonra her sayıyı 10'a bölmek olacaktır.
NullUserException

@delnan - güncellendi. Bir şamandıra menziline sahip olmak için dakika yanlışlıkları kabul etmeye hazırım
Jonathan


@NullUserException - bu sadece bir örnek - gerçek kod elbette parametrik :)
Jonathan

Yanıtlar:


97

Yerleşik bir işlev bilmiyorum, ama bir tane yazıyorum bu çok karmaşık olmamalıdır.

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

Yorumların belirttiği gibi, bu öngörülemeyen sonuçlar üretebilir:

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

Beklenen sonucu almak için, bu sorudaki diğer yanıtlardan birini kullanabilirsiniz veya @Tadhg'ın belirttiği gibi, decimal.Decimal gibi, jumpbağımsız değişken olarak . Şamandıra yerine bir dize ile başlattığınızdan emin olun.

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

Ya da:

import decimal

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

Ve sonra:

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

34
Python'un sloganı aslında bir - ve tercihen sadece bir - olmalıdır . Ama yine de Python's awesome :)
Jonathan

3
>>> print list(frange(0,100,0.1))[-1]==100.0olacakFalse
Volodimir Kopey

frangebeklenmedik bir şekilde çalışabilir. Kayan noktalı aritmetiklerin laneti nedeniyle , örneğin frange(0.0, 1.0, 0.1)son değerin olduğu 11 değer verir 0.9999999999999999. Pratik bir gelişme olacağını while x + sys.float_info.epsilon < y:bile olsa bu muhtemelen çok sayıda başarısız olabilir .
Akseli Palén

10
-1 Lütfen bu kodu kullanmayın , en azından hayatımı etkileyebilecek yazılımlarda kullanmayın. Güvenilir bir şekilde çalışmasını sağlamanın bir yolu yoktur. Akseli Palén'in cevabını da kullanmayın. Xaerxess'in veya wim'in cevabını kullanın (arange ile ilgili kısmı dikkate almayın).
benrg

3
şamandıralar yerine adım olarak kullanırsanızdecimal.Decimal bu harika çalışır .
Tadhg McDonald-Jensen

112

Şunlardan birini kullanabilirsiniz:

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

veya lambda / map kullanın:

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

1
Ve dizi numpy diziler gibi (aralık (5,50,15)) / 10,0 üzerinde çok bölme, çarpma ve taşıma için operatörler
edvaldig

2
@ edvaldig: haklısın, bunu bilmiyordum ... Yine de arange(0.5, 5, 1.5)IMO'nun daha okunabilir olduğunu düşünüyorum .
Xaerxess

2
Bu cevabı kabul edilene tercih ediyorum, çünkü sunulan ilk iki çözüm tamsayıları tekrarlamaya ve son kayanları tamsayılardan türetmeye dayanıyor. Bu daha sağlam. Doğrudan şamandıralarla yaparsanız, şamandıraların dahili olarak nasıl temsil edildiği nedeniyle garip bir kerelik hatalar alma riskiniz vardır. Örneğin, denerseniz list(frange(0, 1, 0.5)), iyi çalışır ve 1 hariç tutulur, ancak denerseniz list(frange(0, 1, 0.1)), aldığınız son değer 1.0'a yakındır, bu muhtemelen istediğiniz şey değildir. Burada sunulan çözümlerin bu sorunu yok.
blubberdiblub

3
Asla numpy.arange kullanmayın (numpy belgelerinin kendisi buna karşı önerir). Wim tarafından önerilen numpy.linspace öğesini veya bu yanıttaki diğer önerilerden birini kullanın.
benrg

79

Eskiden kullanıyordum numpy.arangeama kayan nokta hataları nedeniyle döndürdüğü eleman sayısını kontrol eden bazı komplikasyonlar yaşadım . Şimdi kullanıyorum linspace, örneğin:

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

Yine de kayan nokta hataları var decimal, örneğin:np.linspace(-.1,10,num=5050)[0]
TNT

2
@TNT Hayır, bu bir hata değil. Doğru olduğunu göreceksiniz np.linspace(-.1,10,num=5050)[0] == -.1. Sadece repr(np.float64('-0.1'))daha fazla rakam gösteriyor.
wim

1
Bu belirli örnek fazla yuvarlama hatası göstermese de, hata durumları vardır. Örneğin , ideal sonuç ne zaman print(numpy.linspace(0, 3, 148)[49])yazdırılır . daha iyi bir iş çıkarır , ancak mümkün olan en düşük yuvarlama hatasını üretmesi garanti edilmez. 0.99999999999999991.0linspacearange
user2357112 Monica

Edilir doğru son nokta işleme gerçekleştirmek için garantili ve her zaman elemanların tam olarak istenilen sayıda üretirler.
user2357112 Monica

40

Pylab frange(aslında bir paketleyici matplotlib.mlab.frange) içerir:

>>> 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 sürüm 2.2'den beri kullanımdan kaldırılmıştır. numpy.arange kullanılmalıdır.
kuzavas

13

Hevesle değerlendirildi (2.x range):

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

Tembel olarak değerlendirildi (2.x xrange, 3.x range):

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

Alternatif:

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; ama neden (x * .5 for x in range(10))tembel değerlendirme için bir jeneratör ifadesi olarak olmasın ?
Tim Pietzcker

2
Çünkü bu çok kolay olurdu, sanırım? :)
Karl Knechtel

11

kullanarak itertools: tembel olarak değerlendirilen kayan nokta aralığı:

>>> 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
Kullanmak için +1 itertools.takewhile. Ancak, itertools.count(start, step)biriken kayan nokta hatalarından muzdariptir. (Değerlendirmektakewhile(lambda x: x < 100, count(0, 0.1)) Örneğin .) Bunun takewhile(lambda x: x < stop, (start + i * step for i in count()))yerine yazardım.
musiphil

6

Ben fonksiyonu eklemek yardımcı numeric_range paketine daha-itertools .

more_itertools.numeric_range(start, stop, step) yerleşik işlev aralığı gibi davranır ancak şamandıralar, Ondalık ve Kesir türlerini işleyebilir.

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

4

Böyle bir yerleşik işlev yoktur, ancak işi Python'un izin verdiği kadar güvenli yapmak için aşağıdaki (Python 3 kodu) kullanabilirsiniz.

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

Birkaç iddiada bulunarak hepsini doğrulayabilirsiniz:

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

Kod GitHub'da mevcut


3

Numich vb bağımlılıkları olmayan bir çözüm kichik tarafından sağlandı, ancak kayan nokta aritmetikleri nedeniyle genellikle beklenmedik şekilde davranıyor. Tarafından belirtildiği gibi benim ve blubberdiblub , ek elemanlar kolaylıkla sonucu gizlice. Örneğin , son değeri olarak naive_frange(0.0, 1.0, 0.1)verim 0.999...verir ve böylece toplamda 11 değer verir.

Burada sağlam bir sürüm sağlanmıştır:

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

Çarpma nedeniyle, yuvarlama hataları birikmez. Kullanılması epsilonelbette sorunlar çok küçük ve çok büyük uçlarında yükselmeye olsa bile, çarpma olası yuvarlama hatası ilgilenir. Şimdi, beklendiği gibi:

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

Ve biraz daha büyük sayılarla:

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

Kod, GitHub Gist olarak da kullanılabilir .


Bu, frange (2.0, 17.0 / 6.0, 1.0 / 6.0) ile başarısız olur. Sağlam hale getirilmesinin hiçbir yolu yoktur.
benrg

@benrg Bunu işaret ettiğiniz için teşekkürler! Bu, epsilonun atlamaya bağlı olması gerektiğini anlamamı sağladı, bu yüzden algoritmayı gözden geçirdim ve sorunu onardım. Bu yeni sürüm çok daha sağlam, değil mi?
Akseli Palén

3

Standart Kütüphanede Neden Kayan Nokta Aralığı Uygulaması Yok?

Buradaki tüm gönderiler tarafından açıkça belirtildiği gibi, kayan nokta sürümü yoktur range(). Bununla birlikte, range()işlevin genellikle bir dizin (ve tabii ki bu bir erişimci anlamına gelir ) üreticisi olarak kullanıldığını düşünürsek, ihmal mantıklıdır . Yani, dediğimizde range(0,40), 0'dan 40'a kadar olan ancak 40'ın kendisini içermeyen 40 değer istediğimizi söyleriz.

Endeks üretiminin değerleri kadar endeks sayısıyla ilgili olduğunu düşündüğümüzde range(), standart kütüphanede şamandıra uygulamasının kullanımı daha az mantıklıdır. Örneğin, işlevi çağırırsak frange(0, 10, 0.25), hem 0 hem de 10'un dahil edilmesini beklerdik, ancak bu 41 değere sahip bir vektör verir.

Böylece, frange() kullanımına bağlı işlev her zaman karşı sezgisel davranış sergileyecektir; ya indeksleme perspektifinden algılanan çok fazla değere sahiptir ya da matematiksel perspektiften döndürülmesi gereken bir sayıyı içermez.

Matematiksel Kullanım Örneği

Bununla birlikte, tartışıldığı gibi numpy.linspace(), nesli matematiksel bakış açısıyla güzel bir şekilde gerçekleştirir:

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

Dizin Oluşturma Kullanım Örneği

Ve indeksleme perspektifi için, ondalık basamak sayısını belirtmemize izin veren bazı hileli dize büyüsü ile biraz farklı bir yaklaşım yazdım.

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

Benzer şekilde, yerleşik roundişlevi de kullanabilir ve ondalık sayısını belirleyebiliriz:

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

Hızlı Karşılaştırma ve Performans

Tabii ki, yukarıdaki tartışma göz önüne alındığında, bu işlevlerin oldukça sınırlı bir kullanım durumu vardır. Yine de, hızlı bir karşılaştırma:

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)

Sonuçlar her biri için aynıdır:

-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

Ve bazı zamanlamalar:

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

Dize biçimlendirme yönteminin sistemimdeki bir saç tarafından kazanıldığı anlaşılıyor.

Sınırlamalar

Ve son olarak, yukarıdaki tartışmadaki noktanın bir gösterimi ve son bir sınırlama:

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

Ayrıca, skipparametre stopdeğere bölünemezse , ikinci sorun göz önüne alındığında esneme boşluğu olabilir:

# 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

Bu sorunu ele almanın yolları var, ancak günün sonunda en iyi yaklaşım muhtemelen sadece Numpy'yi kullanmak olacaktır.


Bu oldukça çarpık bir argüman. range () basitçe yineleme üreticisine bakılmalı ve döngü için mi yoksa indeksleme için mi kullanılıp kullanılmadığı arayanlara bırakılmalıdır. İnsanlar millenia için döngü için şamandıralar kullanıyorlar ve yukarıdaki gerekçeler saçma. Python komitelerindeki insanlar burada büyük zaman kaybettiler ve iyi argümanlar muhtemelen yukarıdaki gibi bükülmüş gerekçelerle boğuldu. O kadar sade ve basit. Şimdi yukarıdaki gibi Python dilinde çok fazla karar verilmiştir.
Shital Shah

2

Daha basit bir kütüphanesiz versiyon

Aw, heck - Kütüphanesiz basit bir versiyonda atacağım. Geliştirmek için çekinmeyin [*]:

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

Ana fikir şu ki nsteps, sizi baştan sona götürecek adımların sayısı verange(nsteps) götürecek her zaman tamsayılar yayar, böylece doğruluk kaybı olmaz. Son adım [0..nsteps] 'i doğrusal olarak [start..stop]' a eşlemektir.

Düzenle

Gibi Eğer alancalvitti sen tam rasyonel sunumunu elde dizi isteriz, her zaman kullanabilirsiniz Kesirler :

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

[*] Özellikle, frange()bir jeneratör değil bir liste döndürür. Ama ihtiyaçlarım için yeterliydi.


Durma değerini çıkışa dahil etmek istiyorsanız, stop + jump ekleyerek, bu yöntem daha sonra ortada kötü kayan noktalarla naif sonuca geri döner, deneyin frange(0,1.1,0.1)ve hatta daha fazlası gibi seçeneklerden daha fazlasını yapınfrange(0,1.05,0.1)
alancalvitti

@alancalvitti: "Kötü" bir kayan nokta tanımınız nedir? Evet, sonuçlar iyi yazdırılmayabilir, ancak frange (), kayan nokta gösterimi sınırları içinde en yakın eşit aralıklı değerler kümesini sağlar. Nasıl geliştirirdiniz?
fearless_fool

iyi bir nokta, böyle bir görev için rasyonel sayılar arasında yer alacağınız üst düzey dile alışkınım, Py montaj gibi geliyor.
alancalvitti

Montaj? Hrrumph! ;) Elbette Python, Kesirler ile tam temsil sağlayabilir: docs.python.org/3/library/fractions.html
fearless_fool

Doğru, teşekkürler, ancak örneğin, sevdiğim dil bu türleri otomatik olarak dönüştürür, bu nedenle 1/2 rasyoneldir, 1 / 2.0 yüzerken, bunları böyle bildirmeye gerek yoktur - beyanları Java'ya bırakın, daha da fazlası Py'den daha düşük / montaj.
alancalvitti

2

Bu numpy.arange (start, stop, stepsize) ile yapılabilir

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]

Not 1: Buradaki yorum bölümündeki tartışmadan "asla kullanmayın numpy.arange()(numpy belgelerinin kendisi buna karşı öneriyor). Wim tarafından önerilen numpy.linspace'i veya bu yanıttaki diğer önerilerden birini kullanın"

Not 2: Tartışmayı burada birkaç yorumda okudum, ancak şimdi bu soruya üçüncü kez döndükten sonra, bu bilgilerin daha okunabilir bir konuma yerleştirilmesi gerektiğini hissediyorum.


2

Gibi kichik yazdım, bu da karmaşık olmamalıdır. Ancak bu kod:

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

Şamandıralarla çalışırken hataların birikimli etkisi nedeniyle uygun değildir . Bu yüzden şöyle bir şey alırsınız:

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

Beklenen davranış olsa da:

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

Çözüm 1

Kümülatif hata, bir dizin değişkeni kullanılarak basitçe azaltılabilir. İşte örnek:

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

Bu örnek beklendiği gibi çalışır.

Çözüm 2

Yuvalanmış işlev yok. Sadece bir süre ve bir sayaç değişkeni:

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

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

Tersine çevrilmiş aralığı istediğiniz durumlar dışında bu işlev de iyi çalışır. Örneğin:

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

Bu durumda çözüm 1 beklendiği gibi çalışacaktır. Bu işlevin bu gibi durumlarda çalışması için aşağıdakine benzer bir kesmek uygulamanız gerekir:

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

Bu hack ile bu işlevleri negatif adımlarla kullanabilirsiniz:

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

Çözüm 3

Düz standart kitaplıkla daha da ileri gidebilir ve çoğu sayısal tür için bir aralık işlevi oluşturabilirsiniz:

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

Bu jeneratör Fluent Python kitabından uyarlanmıştır (Bölüm 14. Tekrarlar, Yineleyiciler ve jeneratörler). Azalan aralıklarla çalışmaz. Önceki çözümde olduğu gibi, bir saldırı uygulamalısınız.

Bu jeneratörü aşağıdaki gibi kullanabilirsiniz, örneğin:

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

Ve tabii ki şamandıra ve int ile de kullanabilirsiniz.

Dikkatli ol

Bu işlevleri negatif adımlarla kullanmak istiyorsanız, adım işareti için bir kontrol eklemelisiniz, örneğin:

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

Buradaki en iyi seçenek StopIteration, rangeişlevin kendisini taklit etmek istiyorsanız, yükseltmektir .

Mimik aralık

rangeİşlev arabirimini taklit etmek isterseniz, bazı bağımsız değişken denetimleri sağlayabilirsiniz:

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

Bence, amacın var. Bu işlevlerden herhangi biriyle (ilk hariç) gidebilirsiniz ve bunlar için ihtiyacınız olan tek şey python standart kütüphanesidir.


1

yüzlerce ötesinde herhangi bir ondalık basamak olmadan çift kesinlikli kayar nokta sayıları bir dizi dönen bir fonksiyon yazdı. sadece dizeler gibi aralık değerlerini ayrıştırma ve fazlalığı bölme meselesiydi. Bir kullanıcı arayüzünden seçim aralıkları görüntülemek için kullanıyorum. Umarım başkası faydalı bulur.

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

kullanım

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

Her adımı N ondalık basamağa yuvarlamak için

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]

kod

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

Neden bu cevabı seçmelisiniz?

  • Geri sayım istendiğinde, diğer birçok yanıt askıda kalacaktır.
  • Diğer birçok cevap yanlış yuvarlanmış sonuçlar verecektir.
  • Diğer cevaplar np.linspaceisabet ve özledimdir, doğru sayıda bölüm seçmekte zorluk çekebilirler veya çalışmayabilirler. np.linspacegerçekten 0.1 ondalık artışlarla mücadele eder ve artışı birkaç bölmeye dönüştürmek için formüldeki bölümlerin sırası doğru veya kırık kodla sonuçlanabilir.
  • Bazında verilen diğer cevaplar np.arangekullanımdan kaldırılmıştır.

Şüpheniz varsa, yukarıdaki dört test vakasını deneyin.


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)

Lütfen Range'nin ilk harfinin büyük olduğunu unutmayın. Bu adlandırma yöntemi Python'daki işlevler için teşvik edilmez. İsterseniz Aralığı'nı drange veya frange gibi bir değere değiştirebilirsiniz. "Aralık" işlevi tam istediğiniz gibi davranır. El kitabını buradan kontrol edebilirsiniz [ http://reference.wolfram.com/language/ref/Range.html ].


0

Bence, hem kayan nokta hem de tamsayı için aralığın tüm özelliklerini taklit eden çok basit bir cevap var. Bu çözümde, varsayılan olarak yaklaşımınızın 1e-7 (veya seçtiğiniz) olduğunu varsayalım ve işlevi çağırdığınızda değiştirebilirsiniz.

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

Elbette bazı yuvarlama hataları olacak, bu yüzden bu mükemmel değil, ancak genellikle yüksek hassasiyet gerektirmeyen uygulamalar için kullanıyorum. Bunu daha doğru hale getirmek istiyorsanız, yuvarlama hatalarının nasıl ele alınacağını belirtmek için ekstra bir argüman ekleyebilirsiniz. Belki bir yuvarlama işlevini geçmek bunu genişletilebilir ve programcının yuvarlama hatalarının nasıl ele alınacağını belirtmesine izin verebilir.

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

Eğer yazarsam:

arange(0, 1, 0.1)

Çıktı verecektir:

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

-1

Python'daki şamandıralar için bir range () eşdeğeri var mı? NO Bunu kullanın:

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
Oldukça kötü bir çözüm, deneyin f_range(0.01,0.02,0.001)... En pratik amaçlar için, arangeNumpy basit, güvenli ve hızlı bir çözümdür.
Bart

Haklısın. Numpy ile kodumdan 1.8 daha hızlı.
Grigor Kolev

Haklısın. Numpy ile kodumdan 1.8 daha hızlı. Ama çalıştığım sistem tamamen kapalı. Sadece Python ve pyserial artık yok.
Grigor Kolev

-2

Orada İşte versiyonu vb olumsuz adım, yanlış bir başlangıç, durdurma gibi basit kenar davalarını yok burada birkaç cevaplar o doğru yerli olarak aynı davranışı vererek bu vakaların çoğu kolları 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  

Bunun, tıpkı yerel gibi adım = 0 hatası verdiğini unutmayın range . Bir fark, yerel aralığın, dizinlenemeyen ve tersine çevrilebilir olan, ancak yukarıdakinin olmadığı nesneyi döndürmesidir.

Sen olabilir bu kod ile oynamak burada ve test durumlarda.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.