Hi,
In Netlogo v.6, I'm trying to calculate a time-weighted measure of a peer's cooperative behavior. The point is, I can't figure out exactly how to do a nested foreach in NetLogo.
My current approach:
setup a list: reputation_peer
setup a list: reputation_peer_list, of variable 1 (behavior) and variable 2 (time tick)
Each encounter, add reputation_list to reputation_peer, making the reputation_peer a list of lists
calculate the weighted reputation:
taking item 2 of each list in reputation_peer, which is the time tick of that encounter
and dividing it by the current time tick.
This gives a fraction: tick of that encounter/total time ticks
setup a list reputation_weighted
To weigh the reputations, multiply the time fraction with the first item of reputation_peer, which is the behavior variable
Then, I want to know the reputation_current, by taking all values from reputation_weighted and adding them up.
I'm messing around with the foreach function but I can't seem to figure it out.
How would such an approach be coded in Netlogo?
My current code is (setting reputation_peer myself to test it):
to calculate_reputation
set reputation_peer [[8 4][9 2][10 3][11 2][14 1]]
if ticks > 0 [
foreach reputation_peer [x -> set reputation_peer_list list (item 0 x) ((item 1 x) / ticks )
set reputation_peer_list_2 lput reputation_peer_list reputation_peer_list_2]
foreach reputation_peer_list_2 [x -> set reputation_peer_list_list (list (item 0 x * item 1 x))]
foreach reputation_peer_list_list [x -> set reputation_peer_current reputation_peer_current + x]
]
end
I don't really know whether I'm doing it right, but mainly, this code seems a very bulky with all the list_list things going on. I'm guessing it could be a lot simpler.
Would greatly help me out if you guys have some tips.
I'm not 100% sure of what you're expecting as final output, but maybe this does what you need?
to calc-rep-2
set reputation_peer [[8 4][9 2][10 3][11 2][14 1]]
let weighted_rep_list []
if ticks > 0 [
foreach reputation_peer [ x ->
; Pull out the values from reputation_peer for ease of use
let encounter_behavior item 0 x
let encounter_time item 1 x
; Calculate the time fraction for the current item
let time_fraction encounter_time / ticks
; Calculate the weighted reputations
let weighted_rep encounter_behavior * time_fraction
; Add the weighted rep to the list of weighted reps
set weighted_rep_list lput weighted_rep weighted_rep_list
]
; Now, weighted_rep_list is a list of weighted reputations
print weighted_rep_list
; Get the sum of the list
print sum weighted_rep_list
]
tick
end
Related
I have a list of custom data objects which track an increasing total value on a daily basis using one field total. Another field in the custom data type is the value new. Using a csv file I have read in the values for date and total and am trying to calculate and set the values for new from these values.
data item = Item{
date :: Day,
total :: Int,
new :: Int
}
Before
date
total
new
01/01/2021
0
0
02/01/2021
2
0
03/01/2021
6
0
04/01/2021
15
0
After
date
total
new
01/01/2021
0
0
02/01/2021
2
2
03/01/2021
6
4
04/01/2021
15
9
My understanding is that in haskell I should be trying to avoid the use of for loops which iterate over a list until the final row is reached, for example using a loop control which terminates upon reaching a value equal to the length of the list.
Instead I have tried to create a function which assigns the value of new which can used with map to update each item in the list. My problem is that such a function requires access to both the item being updated, as well as the previous item's value for total and I'm unsure how to implement this in haskell.
--Set daily values by mapping single update function across list
calcNew:: [Item] -> Int -> [Item]
calcNew items = map updateOneItem items
-- takes an item and a value to fill the new field
updateOneItem :: Item -> Int -> Item
updateOneItem item x = Item date item total item x
Is it possible to populate that value while using map? If not, is a recursive solution required?
We can do this by zipping the input list with itself, shifted by one step.
Assuming you have a list of items already populated with total values, which you want to update to contain the correct new values (building an updated copy of course),
type Day = Int
data Item = Item{ -- data Item, NB
date :: Day,
total :: Int,
new :: Int
} deriving Show
calcNews :: [Item] -> [Item]
calcNews [] = []
calcNews totalsOK#(t:ts) = t : zipWith f ts totalsOK
where
f this prev = this{ new = total this - total prev }
This gives us
> calcNews [Item 1 0 0, Item 2 2 0, Item 3 5 0, Item 4 10 0]
[Item {date = 1, total = 0, new = 0},Item {date = 2, total = 2, new = 2},
Item {date = 3, total = 5,new = 3},Item {date = 4, total = 10, new = 5}]
Of course zipWith f x y == map (\(a,b) -> f a b) $ zip x y, as we saw in your previous question, so zipWith is like a binary map.
Sometimes (though not here) we might need access to the previously calculated value as well, to calculate the next value. To arrange for that we can create the result by zipping the input with the shifted version of the result itself:
calcNews2 :: [Item] -> [Item]
calcNews2 [] = []
calcNews2 (t:totalsOK) = newsOK
where
newsOK = t : zipWith f totalsOK newsOK
f tot nw = tot{ new = total tot - total nw }
I'm writing up a code in Netlogo that basically should do the following:
Amongst directed links, interact and seek out their cooperative behavior (coop_b).
Store coop_b in a list variable together with the time of the interaction (reputation_now)
Every interaction, add the reputation_now to a bigger list, reputation_h (reputation history)
Now, add a time-weight to the reputation, so that the more recently had interactions weigh more in the total reputation. I do this by dividing the encounter time of an interaction by the current time tick, then multiplying that with the coop_b to retrieve a weighted reputation for each interaction. This is stored in the list reputation_h_w (historic reputations weighted). The thing is, this list should be updated every time the members interact, so that earlier additions to the list are now updated to the new time tick. My hunch is this is where my code goes in the mist (problems depicted below the code section).
My code:
to horizontal_interact
ask members [
;set random example variable for coop_b
set coop_b random-float 5 ; coop-b stands for cooperation behavior
if ticks > 0 [
ask my-out-links [ ;there are directed links between all members
set reputation_now (list [coop_b] of end2 ticks) ;list of coop_b and encounter time
set reputation_h lput reputation_now reputation_h ; history of reputations, a list of all reputation_now recorded
foreach reputation_h [ x ->
let cooperative_behavior item 0 x
let encounter_time item 1 x
let reputation_now_w (list cooperative_behavior encounter_time (encounter_time / ticks ))
]
]
]
]
end
If I test the content of reputation_h and reputation_h_w with 2 members, I get:
reputation_h is the coop_b variable of the member and the tick of encounter
links> show reputation_h
(link 1 0):
[[4.0900840358972825 1]
[0.8885953841506328 2]
[0.47017368072392984 3]]
(link 0 1): [[3.6805257472366164 1]
[3.6805257472366164 2]
[3.4201458793705326 3]]
reputation_h_w (containing the member's coop_b variable, the encounter time and the encounter time divided by the ticks):
links> show reputation_h_w
(link 0 1): [[3.6805257472366164 1 1]
[3.6805257472366164 1 0.5]
[3.6805257472366164 2 1]
[3.6805257472366164 1 0.3333333333333333]
[3.6805257472366164 2 0.6666666666666666]
[3.4201458793705326 3 1]]
(link 1 0): [[4.0900840358972825 1 1]
[4.0900840358972825 1 0.5]
[0.8885953841506328 2 1]
[4.0900840358972825 1 0.3333333333333333]
[0.8885953841506328 2 0.6666666666666666]
[0.47017368072392984 3 1]]
The problem is that reputation_h_w doesn't make sense to me - firstly there's six inputs instead of three, and secondly, the encounter time (item 1) and the encounter time/ticks (item 2) is off.
What am I doing wrong here?
Not sure where you update reputation_h_w in your code, but I'm guessing that you are not resetting it to a blank list before running your foreach loop again. So, it's lput-ing the values at the end of the list, which is not blank anymore.
Example setup:
breed [ as a ]
as-own [ coop_b ]
links-own [ reputation_now reputation_history reputation_history_w]
to setup
ca
create-as 2 [
set coop_b who + 1
setxy random-pxcor random-pycor
]
while [ any? as with [ not any? my-in-links ] ] [
ask one-of as with [ not any? my-out-links ] [
create-link-to one-of other as with [ not any? my-in-links ] [
set reputation_now []
set reputation_history []
]
]
]
reset-ticks
end
Note that here I will set reputation_history [] right before the foreach chunk runs:
to interact
if ticks > 0 [
ask links [
set reputation_now ( list [coop_b] of end2 ticks )
set reputation_history lput reputation_now reputation_history
; reset reputation history to a blank list, as you are
; recalculating the weighted value at each tick
set reputation_history_w []
foreach reputation_history [ x ->
let behavior item 0 x
let encounter_time item 1 x
let fraction encounter_time / ticks
set reputation_history_w lput (
list behavior encounter_time fraction ) reputation_history_w
]
show ( word "Current reputation: " reputation_now )
show ( word "Reputation history: " reputation_history )
show ( word "Weighted history rep list: " reputation_history_w )
]
]
tick
end
As far as why your ticks are off, I'd guess it's because you are calling tick after you run your horizontal_interact procedure. With the example above, my output looks like:
(link 0 1): "Current reputation: [2 2]"
(link 0 1): "Reputation history: [[2 1] [2 2]]"
(link 0 1): "Weighted history rep list: [[2 1 0.5] [2 2 1]]"
(link 1 0): "Current reputation: [1 2]"
(link 1 0): "Reputation history: [[1 1] [1 2]]"
(link 1 0): "Weighted history rep list: [[1 1 0.5] [1 2 1]]"
even though the ticks read 3. If you run it with tick at the start of the procedure, that might sort out your expected output.
I have a sub-routing in my code where each patch is asked to pick its closest & farthest turtle based on certain conditions. I keep getting this error after a couple of ticks
OF expected input to be a turtle agentset or turtle but got NOBODY instead.
error while patch 0 30 running OF
called by procedure UPDATE-SUPPORT
called by procedure GO
called by Button 'Go'
There are two other routines where a turtle dies or is born depending on a few other metrics that are measured. I am not able to debug the code but what i have figured so far is that it happens after a turtle dies or is born.
Below is the code based on which the closest & farthest turtles are assigned at each tick.
to update-support
ask patches [
let old-total sum [my-old-size] of parties
set f-party []
set h-party []
set party-list (sort parties)
set voteshare-list n-values length(party-list) [ (([my-old-size] of party ? ) + 1 ) / ( old-total + 1 ) ]
set party-citizen-dist n-values length(party-list) [ ( distance party ? ) ^ 2 ]
set f-list n-values length(party-list) [ ( ( 1 / ( item ? voteshare-list ) ) * ( item ? party-citizen-dist ) ) ]
set f-index position (min f-list) f-list
set h-list n-values length(party-list) [ ( ( item ? voteshare-list ) * ( item ? party-citizen-dist ) ) ]
set h-index position (max h-list) h-list
set f ((-1) * (min f-list))
set h max h-list
set f-party lput item f-index party-list f-party
set h-party lput item h-index party-list h-party
set closest-party first f-party
set farthest-party first h-party
]
After a turtle dies, when I inspected the patch which was throwing the error, i found the word nobody as an element in the list. The error is highlighted to be in the Party ? section while creating the voteshare-list in the above code
When I inspected the patch throwing the error, Party-list which is the list with all the current parties sorted was showing this:
Party-list: [(party 0) nobody (party 2)]
and my f-party list just had [(nobody)]
Has anyone faced such a situation.?
Below is the death & birth routine:
to party-death
ask parties [if (fitness < survival-threshold and count parties > 2)
[ die
] update-support
]
to party-birth
ifelse (endogenous-birth? = true)
[ ask one-of patches with [distancexy 0 0 < 30]
[ if (random-float 1 < (kpi * 1000)) [sprout-parties 1 [initialize-party] ]]
[ create-parties 1 [set heading random-float 360 jump random-float 30 initialize-party] ]
update-support
end
I have read from csv a list of lists named fileList [[id, id2, id3],[10,10,11]]
But i have problem that I want to iterate trough the list and in every iteration create a turtle that contain id1, id3 (not Id2) as variables. My idea in python syntax (I need help to transpose it to NetLogo):
for x, list in enumerate(fileList):
if x==0: #first list is names so I transpose the names to places in
index_id=list.index(id)
index_id3=list.index(id3)
else:
create-turtle_nr1 #not in python syntax but the idea is to create turte to assign variables from list below
ask turtle_nr1 [set id1 item (item as list[index_id])]
Overall output is three turtles with variables id and id3.
In this case, you should just be able use item to index your lists iteratively. Essentially, for each turtle you want it to index the appropriate list-of-variables from the list of lists and then index the appropriate variable from that list. You could start with something like:
turtles-own [
id
id2
id3
]
to list-of-lists
;;; these lists are just placeholders, of course, use your real list of lists
;;; as the "ids_list" variable in this case
let id1list [ 1 2 3]
let id2list [ 44 55 66 ]
let id3list [ "a" "b" "c" ]
let ids_list ( list id1list id2list id3list )
let n 0
while [ n < 3 ] [ ;;; or however many turtles you end up wanting,
;;; as long as you have list variables for them
create-turtles 1 [
set id item n (item 0 ids_list)
set id3 item n (item 2 ids_list)
]
set n n + 1
]
end
This procedure creates three turtles with ids of 1, 2, and 3, id2s of 0, and id3s of a, b, and c.
How to assign a string or integer variable to turtle, using probabilities of the variables in a group/list? For example it is 0.4 probability that one specific variable is used from specific group/list. The function selects randomly the variable based on probability. I need to use the same method afterwards to choose a variable (string) from a list according to probability.
In python it should be:
import random
def random_value(probability_list, values):
r = random.random()
index = 0
while(r >= 0 and index < len(probability_list)):
r -= probability_list[index]
index += 1
value=values[index - 1]
value_index=index-1
return value,value_index
I tried it in Netlogo like below (get error that index is -1) but is there a better way?
globals [random_nr probabilities some_list index]
to initialize-variables
set some_list[]
set probabilities[]
end
to random_pick
set random_nr random-float 1
set probabilities [0.1 0.2 0.4 0.3]
set some_list ["String1" "String2" "String3" "String4"]
set index 0
while [(random_nr >= 0) and (length probabilities < index)] [
set random_nr random_nr - item index probabilities
set index index + 1 ]
set index index - 1
end
is there a better way?
Yes there is.
NetLogo 6.0 comes with the rnd extension bundled. (You can also download the extension separately for earlier versions of NetLogo.)
The rnd extension offers the rnd:weighted-one-of-list primitive, which does exactly what you're trying to do:
extensions [ rnd ]
to-report pick
let probabilities [0.1 0.2 0.4 0.3]
let some_list ["String1" "String2" "String3" "String4"]
report first rnd:weighted-one-of-list (map list some_list probabilities) last
end
Let me unpack the last expression a bit:
The role of (map list some_list probabilities) is to "zip" the two lists together, in order to get a list of pairs of the form: [["String1" 0.1] ["String2" 0.2] ["String3" 0.4] ["String4" 0.3]].
That list of pairs is passed as the first argument to rnd:weighted-one-of-list. We pass last as the second argument of rnd:weighted-one-of-list to tell it that it should use the second item of each pair as the probability.
rnd:weighted-one-of-list then picks one of the pairs at random, and returns that whole pair. But since we're only interested in the first item of the pair, we use first to extract it.
To understand how that code works, it helps to understand how Anonymous procedures work. Note how we make use of the concise syntax for passing list to map and for passing last to rnd:weighted-one-of-list.
Don't overlook the rnd extension:
https://github.com/NetLogo/Rnd-Extension
But it is possible to do it essentially as you propose. I'll to that here, but it would be better to use explicit arguments.
to-report random-pick
let _r random-float 1
let _ps [0.1 0.2 0.4 0.3]
let _lst ["String1" "String2" "String3" "String4"]
let _i 0
while [_r >= item _i _ps] [
set _r (_r - item _i _ps)
set _i (_i + 1) ]
report item _i _lst
end