With numpy, how would I select an item with weighted probability?
items = [["Item 1", 0.7],
["Item 2", 0.2],
["Item 3", 0.1]]
selected_item = select_item(items).
The chances of selecting "Item 1" should be 0.7 and "Item 2" 0.2 ...
numpy.random.choice would work:
from numpy.random import choice
items = ["Item 1", "Item 2", "Item 3"]
choice(items, p=[0.7, 0.2, 0.1])
Depending on your application, you might or might not want to build a large pre-calculated list, or even a generator to do this.
An advantage of a large pre-calculated list might be speed if selecting one object randomly over and over is slowing you down.
Here are a few ways, including two functions select_item_1(items)
and select_item_2(items)
. I always like to write a script that does what I need myself, before turning to an existing method. It's good practice and it helps me understand what that method might be doing internally.
Also included is use of numpy.random.choice
as suggested by @Oxbowerce
For the two functions I've run them n times and printed the results. I added a fourth item so that the weights don't necessarily sum to 1 so that I remember to renormalize them correctly.
group 1 [('Item 1', 4653), ('Item 2', 1332), ('Item 3', 666), ('Item 4', 3349)]
group 2 [('Item 1', 4637), ('Item 2', 1345), ('Item 3', 658), ('Item 4', 3360)]
group 3 [('Item 1', 4675), ('Item 2', 1336), ('Item 3', 649), ('Item 4', 3340)]
group 4 [('Item 1', 4733), ('Item 2', 1342), ('Item 3', 604), ('Item 4', 3321)]
Script:
import numpy as np
from numpy.random import random, choice
def select_item_1(items):
i, v = zip(*items)
a = np.cumsum(v)
r = a.max() * random()
j = np.argmax(a > r)
return i[j]
def select_item_2(items):
i, v = zip(*items)
vn = np.array(v) / sum(v)
return choice(a=i, size=1, p=n)
def count_them(group):
dic = dict()
for thing in group:
try:
dic[thing] += 1
except:
dic[thing] = 1
return dic
items = [["Item 1", 0.7], ["Item 2", 0.2], ["Item 3", 0.1], ["Item 3", 0.5]]
n = 10000
# get a list of n items selected randomly using weights
item_list, values = zip(*items)
values_norm = np.array(values) / sum(values)
a = np.cumsum(values)
r = a.max() * random(n)
j = [np.argmax(a > x) for x in r]
group_1 = [item_list[x] for x in j]
group_2 = [select_item_1(items) for i in range(n)]
group_3 = choice(a=item_list, size=n, p=values_norm)
group_4 = [choice(a=item_list, size=1, p=values_norm)[0] for i in range(n)]
groups = (group_1, group_2, group_3, group_4)
for i, group in enumerate(groups):
print('group', i+1, sorted(count_them(group).items()))
numpy.random.choice
and the parameterp
which can be used to set the probability weights. $\endgroup$