1

I am trying to take a list, and from it choose a number i randomly. Following which, I want to select a second element j. The probability of choosing a j decays as 1/|i-j|. For example, the relative probability of it choosing a j four steps away from my initial i is 1/4, the probability of selecting a j immediately next to my i.

So far what I have been trying to do is populate my list, choose my i, first then calculate weights using |i-j| for all the other elements in the list.

import numpy as np
import random as random
list = [1,2,3,4,5,6,7,8,9,10]
a = 1
n1 = random.choice(range(len(list)))
n1_coord = (n1, list[n1])
print(n1_coord)
prob_weights = []
for i in range(0, n1):
    wt = 1/((np.absolute(n1-i)))
    #print(wt)
    prob_weights.append(wt)
for i in range(n1+1,len(list)):
    wt = 1/((np.absolute(n1-i)))
    prob_weights.append(wt)

Is there a function built in python that I can feed these weights into which will choose a j with this probability distribution. Can I feed my array of weights in to:

numpy.random.choice(a, size=None, replace=True, p=None)

I suppose I will let p=prob_weights in my code?

 import numpy as np
    import random as random
    list = [1,2,3,4,5,6,7,8,9,10]
    a = 1
    n1 = random.choice(range(len(list)))
    n1_coord = (n1, list[n1])
    print(n1_coord)
    prob_weights = []
    for i in range(0, n1):
        wt = 1/((np.absolute(n1-i)))
        #print(wt)
        prob_weights.append(wt)
    for i in range(n1+1,len(list)):
        wt = 1/((np.absolute(n1-i)))
        prob_weights.append(wt)
    n2 = np.random.choice(range(len(list)), p=prob_weights)
    n2_coord = (n2, list[n2])

Running this above with np.random.choice gives me an error. I am not even sure if this is doing what I want it do in the first place. Is there an alternate way to do this?

jcp
  • 249
  • 1
  • 10

1 Answers1

1

There is a built in function for this: random.choices, which accepts a weights argument.

Given your first selected index n1, you can do something like

indices = range(len(mylist))
weights = [0 if i == n1 else 1 / abs(i - n1) for i in indices]
n2 = random.choices(indices, weights=prb_wts, k=1).

By setting the weight of the first item to zero, you prevent it form bering selected.

Numerical operations do tend to be faster when using numpy, so you can use np.random.choice, which accepts a p argument:

values = np.array([...])
indices = np.arange(values.size)

n1 = np.random.choice(indices)
i = values[n1]

delta = np.abs(indices - n1)
weights = np.divide(1.0, delta, where=delta)
n2 = np.random.choice(indices, p=weights)
j = values[n2]

As minor nitpicks, don't call a variable list, since that shadows a built-in, and import x as x is just import x.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • Oh wow! I did not need two for loops to make my array of weights. would the indices of the weights be ordered in the same way the indices of the original list? – jcp Jan 13 '19 at 07:30
  • @jcp yes. The list of weights is the same size as the list of numbers. The elements correspond by index. Since the weight only depends on position, not value, the list comprehension does not reference the list directly. – Mad Physicist Jan 13 '19 at 07:34
  • Oh great. Thank you. I guess the error I was having with my original code was a size mismatch. Doing it your way certainly fixes it. – jcp Jan 13 '19 at 07:37
  • Just to make sure: prb_wts = [0 if i == n1 else 1/abs(i - n1) for i in range(len(mylist))] n2 = np.random.choices(range(len(mylist)), weights=prb_wts, k=1). This throws a new error: AttributeError: module 'numpy.random' has no attribute 'choices'. I am running python 3.7.0. Is this function only available in more recent versions? – jcp Jan 13 '19 at 07:47
  • @jcp. Choices is in the plain Python random module, not np.random. The linked docs should make that clear. I've added a numpy solution too. You should probably add a numpy tag to your question. – Mad Physicist Jan 13 '19 at 07:49
  • @jcp. I've updated the non numpy portion of the answer – Mad Physicist Jan 13 '19 at 07:52