volc_nerd
volc_nerd

Reputation: 235

Variable step in a for loop

I am trying to loop between 0.01 and 10, but between 0.01 and 0.1 use 0.01 as the step, then between 0.1 and 1.0 use 0.1 as step, and between 1.0 and 10.0 use 1.0 as step.

I have the while loop code written, but want to make it more pythonic.

i = 0.01
while i < 10:
   # do something
   print i
   if i < 0.1:
       i += 0.01
   elif i < 1.0:
       i += 0.1
   else:
       i += 1

This will produce

0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 2, 3, 4, 5, 6, 7, 8, 9

Upvotes: 21

Views: 6464

Answers (7)

Bergi
Bergi

Reputation: 665527

I'd recommend a generator function as well, but if the steps are not such convenient powers of each other I'd write it like

def my_range():
    i = 0
    while i < 0.1:
        i += 0.01
        yield i
    while i < 1:
        i += 0.1
        yield i
    while i < 10:
        i += 1
        yield i

for x in my_range():
    print x

It might be a bit more repetitive, but illustrates much better what is going on and that the yielded values are monotonically increasing (regardless what numbers you put in).

If it gets too repetitive, use

def my_range():
    i = 0
    for (end, step) in [(0.1, 0.01), (1, 0.1), (10, 1)]:
        while i < end:
            i += step
            yield i

Upvotes: 2

Tonechas
Tonechas

Reputation: 13743

Just in case you wished to replace the loop with vectorized code...

In [63]: np.ravel(10.**np.arange(-2, 1)[:,None] * np.arange(1, 10)[None,:])
Out[64]: 
array([ 0.01,  0.02,  0.03,  0.04,  0.05,  0.06,  0.07,  0.08,  0.09,
        0.1 ,  0.2 ,  0.3 ,  0.4 ,  0.5 ,  0.6 ,  0.7 ,  0.8 ,  0.9 ,
        1.  ,  2.  ,  3.  ,  4.  ,  5.  ,  6.  ,  7.  ,  8.  ,  9.  ])

Upvotes: 2

hashcode55
hashcode55

Reputation: 5870

Just a single line of code through list comprehension -

for k in [i*j for j in (0.01, 0.1, 1) for i in range(1, 10)]

Can't be more pythonic!

Upvotes: 3

Tadhg McDonald-Jensen
Tadhg McDonald-Jensen

Reputation: 21474

You could have a nested loop, the outer one that iterates over the precision and inner one that is just range(1,10):

for precision in (0.01, 0.1, 1):
    for i in range(1,10):
        i*=precision
        print(i)

However floats are probably not going to work in this case as this shows values like 0.30000000000000004 on my machine, for precise decimal values you would want to use the decimal module:

import decimal
for precision in ("0.01", "0.1", "1"):
    for i in range(1,10):
        i*=decimal.Decimal(precision)
        print(i)

Upvotes: 3

Engineero
Engineero

Reputation: 12948

You could do something like:

import numpy as np
list1 = np.arange(0.01, 0.1, 0.01)
list2 = np.arange(0.1, 1, 0.1)
list3 = np.arange(1, 10, 1)
i_list = np.concatenate((list1, list2, list3))  # note the double parenthesis
for i in i_list:
    ...

Basically you create the entire list of values that you need up front, i_list, then just iterate through them in your for loop.

Upvotes: 1

Robᵩ
Robᵩ

Reputation: 168876

A special-purse generator function might be the right way to go. This would effectively separate the boring part (getting the list of numbers right) from the interesting part (the # do something in your example).

def my_range():
    for j in .01, .1, 1.:
        for i in range(1, 10, 1):
            yield i * j

for x in my_range():
    print x

Upvotes: 21

Robin Krahl
Robin Krahl

Reputation: 5308

One approach would be to use two loops: one for the order of magnitude, and one for the values from 1 to 9:

for exp in range(-2, 1):
    for i in range(1, 10):
        print("{:.2f}".format(i * 10 ** exp))

Upvotes: 3

Related Questions