hegdep
hegdep

Reputation: 596

Generating random numbers randomly

I have a column T, which decides the range of the value in the value column. T is incremented in steps of 0.5 and always has 4 occurrences.

If T=0.5,1.5,2.5 etc., I want to generate a random number between 1 and 2. If T=1,2,3,4 etc., I want to generate a random number between 2 and 3.

^This is the easy bit, switching the random generator or generating the random numbers separately and ordering them should do the trick.

However, once in every 10 integer Ts i.e. 1,2,3,4...10, I want a random function to pick a random T and set the value between 5 and 6, instead of 2 and 3.

Similarly, once in every 100 integer values of T, I want to pick a random T and make the Value column to be between 15 and 16.

T   Value
0.5 1.01
0.5 1.05
0.5 1.85
0.5 1.49
1   2.45
1   2.52
1   2.48
1   2.95
1.5 1.78
1.5 1.45
1.5 1.65
1.5 1.77
2   2.96
2   2.75
2   2.74
2   2.95
2.5 1.75
2.5 7.89
2.5 1.33
2.5 1.58
3   5.78
3   5.44
3   5.36
3   5.24

Upvotes: 0

Views: 276

Answers (3)

norok2
norok2

Reputation: 26886

I would do it this way:

(EDIT: I changed the last two conditions as they were actually redundant.)

(EDIT2: I updated so that the 10/100 offset change happen randomly - but set once at the definition of the function.)

import random

def rand_gen_t(
        t,
        n0=random.randint(1, 100),
        n1=random.randint(1, 10)):
    if t % n0 == 0:
        offset = 15
    elif t % n1 == 0:
        offset = 5
    elif t % 1 == 0:
        offset = 1
    elif t % 0.5 == 0:
        offset = 2
    return offset + random.random()

Compared to the solution proposed by @DillonDavis it would work for arbitrary t as long as it is a half-integer value, and it is assuming that the range you are interested in is always of size 1, and all is changing is the offset (which is true based on the content of your question).

I'll leave to you to define what should be done for other input values.

And if you want this to be able to cope with numpy.ndarray as your tagging suggests, I would just use the np.vectorize function decorator, e.g.:

import numpy as np

rand_gen_t_arr = np.vectorize(rand_gen_t)

Time-wise, the proposed Numpy's solution would not really shine here, but is not that bad either:

%timeit [rand_gen_t(x / 2) for x in range(1000)]
# 1000 loops, best of 3: 490 µs per loop

%timeit rand_gen_t_arr(np.arange(1000) / 2)
# 1000 loops, best of 3: 523 µs per loop

Perhaps using np.where() is faster, but I would not expect that, since you would probably have (hidden) a loop for each condition in that way.

EDIT (based on the comments)

If you want this to be be more flexible, you could try something like (assuming you have a predefined array t_arr, containing the value of T):

import numpy as np

# I assume that you have it somehow, but I generate one for completeness
t_arr = np.arange(1, 1000) / 2

# first generate random numbers between 0 and 1
val_arr = np.random.random(t_arr.shape)

# update for values of `T`
int_mask = np.where(t_arr % 1 == 0)[0]
half_int_mask = np.where(t_arr % 0.5 == 0)[0]
int_offset = 1
half_int_offset = 2
val_arr[int_mask] += int_offset
val_arr[half_int_mask] += half_int_offset


# update `Value` for exceptional cases
def gen_special_mask(n_special, n_max):
    return np.random.randint(1, n_special, int(n_max / n_special)) + np.arange(0, n_max, n_special)


def mask_intersect(mask1, mask2):
     return np.array(list(set(mask1).intersection(set(mask2))))

special_mask10 = gen_special_mask(10, val_arr.size)
special10_offset = 5
special_mask100 = gen_special_mask(100, val_arr.size)
special100_offset = 10

special_mask10_int = mask_intersect(int_mask, special_mask10)
val_arr[special_mask10_int] += (special10_offset - int_offset)
special_mask10_half_int = mask_intersect(half_int_mask, special_mask10)
val_arr[special_mask10_half_int] += (special10_offset - half_int_offset)

special_mask100_int = mask_intersect(int_mask, special_mask10)
val_arr[special_mask100_int] += (special100_offset - int_offset)
special_mask100_half_int = mask_intersect(half_int_mask, special_mask10)
val_arr[special_mask100_half_int] += (special100_offset - half_int_offset)

Upvotes: 1

Dillon Davis
Dillon Davis

Reputation: 7740

I believe you are looking for something like the following:

from random import random

def rand(T):
    bounds = None
    if T % 100 == 0:
        bounds = (15, 16)
    elif T % 10 == 0:
        bounds = (5, 6)
    elif T in [1, 2, 3]:
        bounds = (2, 3)
    elif T in [0.5, 1.5, 2.5]:
        bounds = (1, 2)

    lower, upper = bounds
    return random() * (upper - lower) + lower

You can use the T in [...] syntax to support arbitrary lists of numbers, and T % N == K to support every Nth number, with an offset of K.

Upvotes: 0

Nate Neumann
Nate Neumann

Reputation: 51

If I'm understanding you right, I think it sounds like you want to use a switch case. Just run the first random function, and then setup a switch that runs another random function (and this second random function varies based on the first result).

As you're working in Python, this switch might be better suited to being just a series of if statements, or you can see documentation about other example implementations in this article. Just make your functions that it maps to include the second random function inside them.

Upvotes: 0

Related Questions