3

Suppose I want to create a set of float numbers starting from 0.1 to 0.00001 as first diving by two and then diving by 5. In other words, I'd like to get the numbers shown below.

0.1
0.05
0.01
0.005
0.001
0.0005
0.0001
0.00005
0.00001

For this small example, I wrote the following code snippet which works fine.

import numpy as np
def format_float(num):
    return np.format_float_positional(num, trim='-')

num = 0.1

for j in range(9):
    if j ==0:
        rate=np.round(num*(0.1),j+1)
        print(format_float(num))
    elif ( (j+1) % 2) != 0:
        num=np.round(num*(0.2),j+1)
        print(format_float(num))
    else:
        num =np.round(num/2,j+1)
        print(format_float(num))

My question is if there is a more elegant way to perform this operation given different rules. For instance, suppose I would like to get the number between x and y where the rule is to first divide by k and then divide by l in order. I believe this should be managable through linspace, but I could not do it.

whitepanda
  • 471
  • 2
  • 12
  • Can `0.5` be the first element of the sequence? – ddejohn Oct 11 '21 at 17:56
  • Why linspace? You don't want linear. Seems more like a job for geomspace (if one of them at all). – no comment Oct 11 '21 at 17:58
  • Any particular reason for this specific sequence? Would be easier with an exponential curve to get a very similar sequence (but with less "clean" values). – ddejohn Oct 11 '21 at 18:11
  • 1
    Can you give an example of what kinds of "different rules" you want to use? – ddejohn Oct 11 '21 at 18:43
  • @don'ttalkjustcode I was not familiar with geomspace and thought that linspace could be modified. – whitepanda Oct 11 '21 at 19:18
  • @ddejohn The different rules means that I would like to decrease the sequence uniformly at different order. For instance, something like "first divide by two and then divide by three" – whitepanda Oct 11 '21 at 19:20

3 Answers3

2

This works, but I'm not sure it's any better than your method.

import numpy as np

# Create the powers of ten:
a = 0.1 ** np.arange(1,6)
# Interleave the halves in between:
a = np.concatenate((a,a/2))
# Sort and reverse: 
a.sort()
a = a[-1::-1]

print(a)
Tim Roberts
  • 48,973
  • 4
  • 21
  • 30
2
In [1]: import numpy as np

In [2]: np.repeat(1 / 10**np.arange(1, 5), 2)[1:] * np.array([1., 5.]*4)[:-1]
Out[2]: array([0.1   , 0.05  , 0.01  , 0.005 , 0.001 , 0.0005, 0.0001])

Generalizing for any "pattern":

def rates(smallest_magnitude, pattern):
    n = len(pattern)
    pows = np.repeat(1 / 10**np.arange(1, smallest_magnitude), n)[(n-1):]
    mults = np.array(pattern * (smallest_magnitude - 1))[:-(n-1)]
    return np.round(pows * mults, smallest_magnitude)

Demo:

In [4]: print(*rates(5, [1, 5]))  # Your original 'pattern'
0.1 0.05 0.01 0.005 0.001 0.0005 0.0001

In [5]: print(*rates(5, [2, 4, 8]))
0.2 0.04 0.08 0.02 0.004 0.008 0.002 0.0004 0.0008 0.0002

In [6]: print(*rates(5, [3, 5, 7, 9]))
0.3 0.05 0.07 0.09 0.03 0.005 0.007 0.009 0.003 0.0005 0.0007 0.0009 0.0003
ddejohn
  • 8,775
  • 3
  • 17
  • 30
1

Two ways:

>>> np.cumprod([.1] + 4*[1/2, 1/5])
array([1.e-01, 5.e-02, 1.e-02, 5.e-03, 1.e-03, 5.e-04, 1.e-04, 5.e-05,
       1.e-05])
>>> 1 / np.cumprod([10] + 4*[2, 5])
array([1.e-01, 5.e-02, 1.e-02, 5.e-03, 1.e-03, 5.e-04, 1.e-04, 5.e-05,
       1.e-05])
no comment
  • 6,381
  • 4
  • 12
  • 30
  • I figured you'd just answer yourself so I didn't bother working on `np.cumprod`. This is a nice solution, and could potentially work well for their general use-case. – ddejohn Oct 11 '21 at 18:34
  • 1
    @ddejohn Yeah, didn't answer at first partially because their question isn't quite clear to me, so I just commented that suggestion, but then feared it might be wrong and I'd mislead you. Also partially because of inaccuracy of float multiplication streaks, although I just fixed that with my second version. – no comment Oct 11 '21 at 18:42