Random weighted choice
Generate a Cumulative Distribution Function for each ID1 thus:
cdfs = defaultdict()
for id1,id2,val in d:
prevtotal = cdfs[id1][-1][0]
newtotal = prevtotal + val
cdfs[id1].append( (newtotal,id2) )
So you will have
cdfs = { 701 : [ (0.2,1), (0.5,2), (1.0,3) ],
702 : [ (0.2,1), (0.5,2) ],
703 : [ (0.5,3) ] }
Then generate a random number and search for it in the list.
def func(id1):
max = cdfs[id1][-1][0]
rand = random.random()*max
for upper,id2 in cdfs[id1]:
if upper>rand:
return id2
return None
Realizing that my first answer was quite buggy in its math, I have produced a new idea. I believe the algorithm here is similar to that of several of the other answers, but this implementation seems to qualify for the "pretty" (if that equals simple) requirement of the question:
def func(id):
rnd = random()
sum = 0
for row in d:
if row[0] == id:
sum = sum + row[2]
if rnd < sum:
return row[1]
With the example data from the OP it goes like this:
- Pick a random number between 0 and 1.0
- If the number is
< 0.2
return the first element - Else if the number is
< 0.5
return the second element - Else (if the number is
< 1.0
) return the third element