Reputation: 1825
I have list of objects "p", every object has some number "a" (for example p[3].a = 5) . I want to choose random object from the list, in the way that the probability of choosing an object is proportional to its value of a, i.e. the probability of choosing object with a=5 is five times the probability of choosing object with a=1. How can I do it with Python/Pylab/Numpy?
thank you!
Upvotes: 1
Views: 1147
Reputation: 13222
I'd suggest using bisect
from bisect import bisect
class Element(object):
def __init__(self, value):
self.a = value
def __repr__(self):
return 'Element({})'.format(self.a)
data = [Element(3), Element(5), Element(7), Element(1)]
last = 0
breakpoints = []
for element in data:
breakpoints.append(last + element.a)
last += element.a
print(breakpoints)
for random_value in xrange(last):
pos = bisect(breakpoints, random_value)
print(random_value, data[pos])
You have to build the list with the breakpoints just once. Then you can use it with the pretty fast bisect algorithm as long as you like.
The last loop is just to demonstrate the result.
Edit: An alternative approach to get the breakpoints (I didn't like the for-loop):
values = [value.a for value in data]
breakpoints = [sum(values[:pos+1]) for pos in xrange(len(values))]
Upvotes: 0
Reputation: 76762
Here's a more efficient way to do it.
import random
def weighted_choice(items):
# check if no items exist
if not items:
return None
# compute total of all weights
total = sum(item.weight for item in items)
# select a random point within the total
selection = random.randint(0, total - 1)
# find the corresponding item
count = 0
for item in items:
count += item.weight
if selection < count:
return item
Upvotes: 2
Reputation: 39451
This will work for integer counts, though it won't be efficient for large counts.
c = collections.Counter({k:k.a for k in stuff})
random.choice(list(c.elements()))
Upvotes: 2