1
$\begingroup$

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 ...

$\endgroup$
1
  • 2
    $\begingroup$ Have a look at numpy.random.choice and the parameter p which can be used to set the probability weights. $\endgroup$
    – Oxbowerce
    Commented Aug 28, 2021 at 9:45

2 Answers 2

2
$\begingroup$

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])
$\endgroup$
1
$\begingroup$

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()))
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.