consider the following 5 events that can occur in netlogo,
[a b c d e f]
Each event has a specific probability of occurring.
Say [0 0 0.3 0.5 0.1 0.1],
that is, p(a) = 0, p(b) = 0, p (d) = 0.5
One (and only one event)must occur . How do I model it in Netlogo.
What i did:
ask turtles
[
if random-float 1 < 0
[EventA]
if random-float 1 < 0.5
[EventD] ; and so on
]
But with this approach, sometimes no event will occur or sometimes more than 1.
Short answer: This is a classic use case for "roulette-wheel" selection.
You can use the NetLogo RND extension to do this, or roll your own following the Lottery example in the model library.
More:
The method abstracts the probabilities to weights, so any set of values from a list or from an agentset can be used; the probability of selection is the ratio of the individual value to the sum of all values in the set or list.
It does not require the set or lists to be sorted, and is scalable to any number of items.
You can write it to produce either the list index of the selected value, or a related value (like an agent or a value from another list).
It can be used for both with-replacement and without-replacement selection from the set.
Without-replacement can be done either by removing individuals from the selection list/set or by setting the selection weight/value of an individual to 0 once it is selected(use a copy of the real values if the selection is repeated.)
Link to NetLogo RND Extension
Simple roll-your-own example:
;; List weight has values to consider
;; Returns the index of the selected item
To RW-Select [ weight ]
Let last-index length weight
Let total sum weight
Let index 0
Let cumulative 0
Let rnd random-float total
While [cumulative <= rnd and index < last-index]
[
Set cumulative cumulative + item index weight
Set index index + 1
]
Report (index - 1)
End
For this, I suggest using just a single random-float between 0 and 1, saving it and then evaluating every probability based on that single number. The trick here is summing up your probabilities. If it is not p(a), check if it is p(a) + p(b). If it is not p(b), check if it is p(a) + p(b) + p(c) etc. In using this method, each option will have its own probability chance of being the chosen option and since the sum of all probabilities is 1, there is always 1 probability that will be chosen.
I use ifelse to evaluate this so option 2 is only considered is option 1 is rejected, option 3 only if 1 and 2 are rejected and so on. Normally ifelse only has 2 options but by including brackets around the entire thing, you can give it as many options as you want.
to go
let options ["a" "b" "c" "d" "e" "f"]
let probabilities [0 0 0.3 0.5 0.1 0.1]
let the-number random-float 1
show the-number
(ifelse
the-number < sum sublist probabilities 0 1 [show item 0 options]
the-number < sum sublist probabilities 0 2 [show item 1 options]
the-number < sum sublist probabilities 0 3 [show item 2 options]
the-number < sum sublist probabilities 0 4 [show item 3 options]
the-number < sum sublist probabilities 0 5 [show item 4 options]
the-number < sum sublist probabilities 0 6 [show item 5 options]
)
end
In your case, this would result in:
to go-2
let x random-float 1
(ifelse
x < 0 [show "a"] ;p(a) = p(x < 0) = 0
x < 0 [show "b"] ;p(b) = p(0 =< x < 0) = 0
x < 0.3 [show "c"] ;p(c) = p(0 =< x < 0.3) = 0.3
x < 0.8 [show "d"] ;p(d) = p(0.3 =< x < 0.8) = 0.5
x < 0.9 [show "e"] ;p(e) = p(0.8 =< x < 0.9) = 0.1
x < 1.0 [show "f"] ;p(f) = p(0.9 =< x < 1.0) = 0.1
)
end
First Edit:
Autogenerating this cumulative lists can for example be done using a map procedure. Here each new value of incremented-probabilities is the sum of the probability and all those that came before it.
let probabilities n-values 20 [0.05]
let incremented-probabilities (map [[this-probability index] -> sum sublist probabilities 0 (index + 1) ] probabilities range length probabilities)
show incremented-probabilities
Second Edit:
Now if you have a variable/high number of different options, you might not want to manually write this entire ifelse structure. Luckily Netlogo has the run primitive which allows you to read the content of a string and treat is as a command.
In the following example, I use foreach and word to, one by one, add an ifelse condition for each different option.
to go-3
let outcomes range 20
let probabilities n-values 20 [0.05]
let additive-probabilities (map [[this-probability index] -> sum sublist probabilities 0 (index + 1) ] probabilities range length probabilities)
let the-number random-float 1
show the-number
; Create your big ifelse code block as a string
let ifelse-string "(ifelse"
(foreach outcomes additive-probabilities [ [outcome probability] ->
set ifelse-string (word
ifelse-string
"\n the-number < "
probability
" [show "
outcome
" ]"
)
])
set ifelse-string word ifelse-string "\n)"
print ifelse-string
; Now run the string as if it was code
run ifelse-string
end
Lastly, you can take a look at "Lottery Example" in the models library. It looks a lot simpler than what i did here.
Thank you for posting the answers. The solution i used was to use cumulative probabilities.
let probabilities [0 0 0.3 0.5 0.1 0.1]
let cum-pmf partial-sums probabilities ; returns [0 0 0.3 0.8 0.9 1]
let n random-float 1
report length filter [ [?1] -> ?1 < n ] cum-pmf ; indexing starts at 0
and the function partial-sums is
to-report partial-sums [lst]
report butfirst reduce [[result-so-far next-item] -> lput (next-item + last
result-so-far) result-so-far] fput [0] lst
end
Related
I have a list of sublists, something like the following
[[0 “coal” 500 1430] [0 “gas” 300 1300] [1 “coal” 600 1500] [1 “gas” 700 1400]]
I would like to do four things:
1. Sort the main list by item 3 of sublists
2. Cumulatively sum item 2 of sublists until a certain value is reached.
3. Identify item 3 of the last list added.
4. Then I’d like to identify items 0 and 1 of lists that were added to the loop in point 2 and ask those turtles to do something.
I've been exploring tables, lists, etc but struggling with this complex bit of code. Can people suggest how they would code this?
Thanks in advance for the help!
The following seems to answer the case, I think...though there may be more elegant ways
to go
create-turtles 2
let l [[0 "coal" 500 1430] [0 "gas" 300 1300] [1 "coal" 600 1500] [1 "gas" 700 1400]]
;sort the list by item 2
let sorted sort-by bigger l
show sorted
;accumulate item 3 until limit reached
let k 0
let n 0
let limit 2800
let turtleNos []
let fuels []
while [k < limit]
[
set k k + item 3 ( item n sorted )
;accumulate item 0 and 1
set turtleNos lput item 0 ( item n sorted ) turtleNos
set fuels lput item 1 ( item n sorted ) fuels
set n n + 1
]
show k
;show item 3 for the last item added to k
show item 3 (item ( n - 1 ) sorted)
;accumulated lists - note non-unique
show turtleNos
show fuels
(foreach turtleNos fuels
[ [x y] -> ask turtle x [show y] ])
end
to-report bigger [l1 l2]
report item 2 l1 > item 2 l2
end
I have a 2*2 matrix called orders:
0 0
0 0
created by:
set orders matrix:from-row-list [[0 0] [0 0]]
and I would like to change it to be a 3*2 matrix:
0 0
0 0
10 50
How to do this please? (I guess you have to create a new matrix also called orders to overwrite the existing orders, but I couldn't figure out the syntax.)
The easiest way would probably be to first convert your matrix to a list, than add the new row to the list, and convert it back to a matrix. Not very elegant, but the report function below should do the trick:
extensions [ matrix ]
to-report matrix-add-row [matrix row-added]
let temp-list matrix:to-row-list matrix ;; converts the matrix to a list
set temp-list lput row-added temp-list ;; the new row is added to the list
report matrix:from-row-list temp-list ;; converts the list back to a matrix
end
to test
let orders matrix:from-row-list [[0 0] [0 0]]
show orders
show matrix-add-row orders [ 10 50 ]
end
This would return you:
observer> test
observer: {{matrix: [ [ 0 0 ][ 0 0 ] ]}}
observer: {{matrix: [ [ 0 0 ][ 0 0 ][ 10 50 ] ]}}
Of course, you have to make sure that the dimensions of the matrix and the row added match.
Just in case this helps someone else, what I did in the end was use a new agent breed 'orders' in place of the matrix, with each orders turtle essentially being what would have been a row in the matrix. I told the orders to sit on the same patch as the turtle that owned it, which was easy as the turtles in that model don't move. The advantage is that I had access to a wide range of processing possibilities that I didn't have with the matrix. Of course if you do this and the order of the rows matters, you need to include some way of managing this (something like orders-own [index] would do).
Good morning,
I need to create a list of [0,1], which length is defined by the user (global variable). There is an easy solution for this:
set listInd (list n-values numOfInd [random 2])
But I need to make sure, that the list has exactly three ones in it and they are placed on random positions. Is there a way to do this?
Regards.
You can create 3 random numbers (indices) and then create a list with 1 on the positions defined by this index list:
to-report rand-list[n k]
let ind-list n-of k n-values n [?]
report n-values n [ifelse-value (member? ? ind-list) [1][0]]
end
Usage:
show rand-list 10 3
; result:
; [0 1 0 0 0 0 1 0 0 1]
An alternative to bergant's solution would be to gather the desired amount of ones and zeros, then mix them together randomly:
to-report rand-list [n k]
let zeros n-values (n - k) [0]
let ones n-values k [1]
report shuffle sentence zeros ones
end
At each tick i am asking each patch to update its count dependent on that of its 8 neighbours. If more than 4 neighbours have a count more than or equal to 1 then they update by 1. If more than 4 neighbours have a count less than or equal to 1 then the patch count should be set to 0.
When i run the code i get the following error:
"The >= operator can only be used on two numbers, two strings, or two agents of the same type, but not on a TRUE/FALSE and a number.error while patch 27 -22 running >= called by procedure SPREAD-ERRORS called by procedure GO"
to spread-errors ;; Errors spread prior to addition of random error
;; This is dependent upon majority of neighbors with errors
ask patches [
ifelse count neighbors with [n >= 1] > 4
[update-n 1]
[set n n = 0]
]
end
You mean set n 0, not set n n = 0.
But why is set n n = 0 actually valid NetLogo syntax, and how did it end up leading to the error message you get?
Well, n = 0 is a boolean expression whose value is either true or false. Then you're taking that value and storing it in n. The effect is as if you had written:
ifelse n = 0 [ set n true ] [ set n false ]
after this command runs, n holds a boolean. Then the next time n >= 1 runs, you get the error message above, since n is no longer a number and can't be compared with 1.
I have two lists and a single value (in each patch). I would like to combine the single value with the list and then set a specific weights value for each interaction. For example
let single random 3; represent the center patch
let list1 [ 0 1 2 2 1 0 0 2 ]; represent the moore neighbors values
let weights [0.2 0.3 0.5 0.1 0.8 0.1 0.6 0.2 0.2 ]; combination of centerpatch and neighb.
I would like to create a new list (list2) , in where for each combination of single and list1 get a specific weight values. For example
If single=0 and list1 =0 , then list2 will have 0.2
If single=1 and list1 =1 , then list2 will have 0.8 and so on..
I tried to use “ifelse-value” but without success.
let single_and_list sentence single list1
let condition1 map [ifelse-value (? = 0 and list1 = 0 ) [0.2][?]] single_and_list
let condition2 map [ifelse-value (? = 1 and list1 = 1 ) [0.8][?]] single_and_list
let list2 [condition1, condition2,….]
I will greatly appreciate any insights thanks
I would recommend writing an explicit reporter procedure to transform the value from the list and the single value into the weight. For instance:
to-report weight [ single list-val ]
if single = 0 and list-val = 0 [ report 0.2 ]
if single = 1 and list-val = 1 [ report 0.8 ]
...
report <default-value>
end
Then, to get the weights, you can just do
let weights map [ weight single ? ] list1
By dividing the code up like this, it becomes much clearer.
I agree with Bryan Head's suggestion, but will add a further suggestion. It looks as if single and list1 always contain small integers. If that's correct, then one option is to put the values that you want to return into a list of lists. For example, you can put lines like these in the reporter:
let weights [[0.2 0.3 0.5]
[0.1 0.8 0.1]
[0.6 0.2 0.2]]
report (item list-val (item single weights))
In the previous line, single must be 0, 1, or 2, and chooses which inner list (row) to use. Then list-val--which also must be be 0, 1, or 2--chooses a number from within the list chosen by item single weights. (Obviously, you can expand this pattern with more rows or longer inner lists.)