Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Weighted Random Choice
#1
I could probably write something using [random.choice] in python easier than in QM, but wanted to know if there's a way to return values from a weighted dictionary without reinventing the wheel. I'm passing the results back to a QM dialog, so i'd rather not have to package in python dependencies on work computers if i could avoid it.

Ideally, the array would have two dimensions, result and weight, but be dynamic so that updates could be made to the dictionary without recompiling in QM.
Percents would work, but it would also be preferable if it didn't need to add to 100%

;; dictionary fruit.txt would have several arrays similar to this:
{
"apple", 9
"pear", 1
"banana", 10
"orange", 20
}

When ran on this set, apple should be the result 9/40 times, pear 1/40, banana 10/40, and orange 20/40.

If it is going to be a nightmare to do in QM, i will use python and deal with it..
All of the users here are brilliant and way more experienced than i am, so i thought i would ask if there's a simple way to do this (before i pull out any more hair).

Thank you all.
#2
I'm not sure what you really want, but try this to see if it fits:
Macro Weighted Random Choice
Code:
Copy      Help
;dictionary fruit.txt would have several arrays similar to this:
;{
;"apple", 9
;"pear", 1
;"banana", 10
;"orange", 20
;}

;Suppose the file dictionary fruit.txt is in the "Desktop" folder.
;You could change the directory to where the file is actually belongs to.
str s1.getfile("$desktop$\fruit.txt")

;Create an IStringMap variable to hold the result and weight values.
IStringMap m._create
lpstr fruit_mapping=sub.createDictMapping(s1)

;Assigned fruit list to IStringMap
m.AddList(fruit_mapping)

;Retrieve the result
out
ARRAY(str) allKeys, allValues
m.GetAll(allKeys, allValues)
int i, sum
for i 0 allValues.len
,sum +=val(allValues[i])

for i 0 allKeys.len
,_s = allKeys[i]
,out F"{_s} = {m.Get(_s)}/{sum}"


#sub createDictMapping
function~ $dict

str line
foreach line dict
,if(line = "{") continue
,if(line = "}") continue
,line.findreplace("''" "" 8)
,line.findreplace("," "" 8)
,_s.addline(line)

ret _s
#3
Thanks for your response! I didn't even think about IStringMap. That will be super helpful in for holding the array dictionary.

The end result was to have it only return one value.
I was giving an example of how often the result would appear with the 1/40. (2.5%) but wanted it to be dynamic so that if you added grapes to the list with a weight of 100, pear would go down to 1/140 (~0.7%)
I think you're onto something with summing them up. I'm just not sure how to do a RandomInt of the sum and get them to cooperate with their weights.

The super inefficient way would be to populate an array with each item a number of times equal to their weight, then pull a random line. That would make sure that pears would only be in the list once, apples would be in there nine times, and so on... so there would be a higher chance for RandomInt to pick apples than pears.

in python it would be:
random.choices([apple,pear,banana,orange],[9,1,10,20])

I was hoping to be able to recreate this function in QM.
#4
not really offering a solution as of yet but did you by chance see this topic
now it's only 2 choices but may give you an idea on how to proceed further.
http://www.quickmacros.com/forum/showthr...6#pid31186
#5
Hello!
Please, try this:

Macro Use WeightedRandomChoices
Code:
Copy      Help
lpstr T=
;apple 9
;pear 1
;banana 10
;orange 20

out
out WeightedRandomChoices(T)

Function WeightedRandomChoices
Code:
Copy      Help
;/
function'str lpstr'T

str s s1
int i
ARRAY(str) a

foreach s T
,s.trim
,if findrx(s "\d+$" 0 2 s1)<0
,,end F"line: '{s}' needs a number"
,s.findreplace(s1)
,for i 0 val(s1)
,,a[]=s

a.shuffle

ret a[0]

Regards Smile
#6
here is a working weighted random choice code
Alex i borrowed a few lines from yours and adapted it to made it work for the weights.
output shows the random order and the number of times each item was chosen.
lines 23-35 are there just to show the weighted count is correct. You can remove them 

Function WeightedRandomChoice
Code:
Copy      Help
int apples pears bananas oranges 
str ss=
;apple 9
;pear 1
;banana 10
;orange 20
str s s1
int
foreach s ss
,s.trim
,if findrx(s "\d+$" 0 2 s1)<0
,,end F"line: '{s}' needs a number"
,s.findreplace(s1)
,ARRAY(str) a
,for i 0 val(s1)
,,a[]=s
for i a.len-1 -1 -1
,a.shuffle    
,_s=a[i]
,_s.trim
,out _s
,a.remove(i)
,sel _s
,,case "apple"
,,apples+1
,,case "pear"
,,pears+1
,,case "banana" 
,,bananas+1
,,case "orange"
,,oranges+1
out apples
out pears
out bananas
out oranges
#7
You dudes are always so rad.
These were as simple and elegant as i had hoped. My arrays might end up being thousands of elements long, so mileage may vary for response time, but you nailed it.

Thank you!


Forum Jump:


Users browsing this thread: 3 Guest(s)