netlogo convert string to numbers within nested lists - netlogo

I am transferring between NetLogo and igraph (in R). Some of the information returned from igraph is 2-level nested lists of strings. Typical example looks like:
[ ["1" "2" "3"] ["4"] ]
I want to convert the internal strings into numbers, while retaining the list structure. So the example would become:
[ [1 2 3] [4] ]
I am guessing I need a combination of map and read-from-string (and perhaps other list manipulation like lput and foreach due to the nesting), but I just can't make it work.
Any ideas?

Essentially, map each list to a mapped list with only int values.
Try the following:
show map [ map [ read-from-string ? ] ?] [ ["1" "2" "3"] ["4"] ]

Just for fun, here is a version that can convert an arbitrary number of nested levels:
to-report read-from-list [ x ]
report ifelse-value is-list? x
[ map read-from-list x ]
[ read-from-string x ]
end
Example:
observer> print read-from-list [ ["1" "2" "3" ] ["4" [ "5" "6" ] ] ]
[[1 2 3] [4 [5 6]]]

Related

Netlogo, Mapping one to one over two lists

I want to find out how to map one to one from two lists. Where length of both the lists are the same.
For Example,
list A = [turtle-0 turtle-3 turtle-5]
list B = [Node-27 node-21 node-29]
I want to map,
turtle-0 to node-27
turtle-3 to node-21
turtle-5 to node-29
and store each of the nodes in turtle-own variable called travel-to.
I have feeling you can achieve this the map or foreach functions, but unfortunately I haven't been successful. Any ideas, Thanks
Using indices, as in geruter's answer, will work, but isn't necessary.
Both map and foreach let you process multiple lists by surrounding the whole call in parentheses, so for example:
observer> show (map [[?a ?b] -> word ?a ?b] [1 2 3] ["a" "b" "c"])
observer: ["1a" "2b" "3c"]
or, shorthand syntax,
observer> show (map word [1 2 3] ["a" "b" "c"])
observer: ["1a" "2b" "3c"]
re:
I have feeling you can achieve this the map or foreach functions, but unfortunately I haven't been successful.
It would help us help you if you showed us your best attempt, and described where you got stuck (was there an error message? incorrect behavior?).
Maybe the pros here will come up with a better solution, but the code below should work.
Actually you could use foreach or map, both return the same results.
The 2 report functions below work for lists, not quiet sure of the also will work with agentsets.
to test
let list1 [1 2 3 4 5 6]
let list2 ["a" "b" "c" "d" "e" "f"]
show foreach-2-lists list1 list2
show map-2-lists list1 list2
end
to-report foreach-2-lists [list1 list2]
let result []
if length list1 != length list2 [ report "Error" ]
let index range length list1
foreach index [ i ->
set result lput ( list ( item i list1 ) ( item i list2 ) ) result
]
report result
end
to-report map-2-lists [list1 list2]
if length list1 != length list2 [ report "Error" ]
let index range length list1
let result map [ i -> ( list ( item i list1 ) ( item i list2 ) ) ] index
report result
end
test will return you the result of both functions:
observer> test
observer: [[1 "a"] [2 "b"] [3 "c"] [4 "d"] [5 "e"] [6 "f"]]
observer: [[1 "a"] [2 "b"] [3 "c"] [4 "d"] [5 "e"] [6 "f"]]
See below for a version using foreach. The trick is to access the two lists through parallel iteration. This won't work with map because you are wanting to do a command (that is, change things) rather than a reporter (extract information).
turtles-own [ my-destination ]
to testme
clear-all
; set up test data lists
create-turtles 5
let agents shuffle sort-on [who] n-of 3 turtles
print agents
let destinations (list "A" (one-of patches) 33 )
print destinations
; do the matching with foreach
let indices range length agents
foreach indices
[ idx ->
ask item idx agents
[ set my-destination item idx destinations
]
]
ask turtles [ show my-destination ]
end

Trouble with advanced netlogo code involving a question mark

I am using Netlogo v6.0.4 and I'm getting the following error message when I try to run sample code from an answer I found here on Stack Overflow.
Nothing named ? has been defined
In this answer the following netlogo code is proposed as an answer:
to-report split [ string delim ]
report reduce [
ifelse-value (?2 = delim)
[ lput "" ?1 ]
[ lput word last ?1 ?2 but-last ?1 ]
] fput [""] n-values (length string) [ substring string ? (? + 1) ]
end
The specific ? it does like is the first one in this section substring string ? (? + 1) .
When this answer was written in 2014, Netlogo v5 was in active use and it had a feature called tasks that were lambda methods. But in v6 tasks were replaced by anonymous-procedures.
Is that what the ? are here? How can I fix this error?
You got it in one- the ? in the versions were essentially placeholders for whatever variable was being passed to the task. The 5.3 dictionary entry for foreach has a good example:
foreach [1.1 2.2 2.6] [ show (word ? " -> " round ?) ]
=> 1.1 -> 1
=> 2.2 -> 2
=> 2.6 -> 3
In that case, foreach was taking the input list of [1.1 2.2 2.6] and iterating over it, where the ? takes the place in the command block of the current item being processed. As I understand it, the main syntactical difference in 6.X is that now you explicitly state what that placeholder is, by using the -> operator. So, the exact same idea as above, translated to 6.0 in the foreach example in the 6.0 dictionary entry, looks like this:
foreach [1.1 2.2 2.6] [ x -> show (word x " -> " round x) ]
=> 1.1 -> 1
=> 2.2 -> 2
=> 2.6 -> 3
There, you can see that the x is explicitly being defined as the placeholder. This really improves the clarity of code- you can define the placeholder however you like to be as clear and explicit as you'd like- this (over the top) example works just as well:
foreach [ 1.1 2.2 2.6 ] [ round_me -> show (word round_me " -> " round round_me) ]
If you're using multiple lists, do note that you have to surround the anonymous procedure with ( ) and your placeholder declaration with [ ]- for example:
( foreach [ 1 2 3 ] [ 10 20 30 ] [ [ a b ] -> print a * b ] )
If you're translating your code example, then, you can just focus on explicitly stating the placeholders. It also might help to break it down into the component parts to clarify- more detail in comments:
to-report split2 [ string delim ]
; split the string up into individual characters
let characters fput [""] n-values ( length string ) [ a -> substring string a ( a + 1 ) ]
; build words, cutting where the delimiter occurs
let output reduce [ [ b c ] ->
ifelse-value ( c = delim )
[ lput "" b ]
[ lput word last b c but-last b ]
] characters
report output
end
Now, to follow Nicolas' example from your linked answer, you can call that to-report to split up your text:
to read-example
let line "Example\tof\tsome\ttext"
show split2 line "\t"
end
Gives you:
observer: ["Example" "of" "some" "text"]
Hope that is helpful!

Netlogo: Obtaining a third list from the relation of other two lists

I want to extract a third list from the relation of two previous lists. Here is the example:
I have a process in NetLogo that outputs a list of 0 and 1, let's call it List_A:
let List_A [0 1 0 1 1 ]
The positions of 0 and 1 always vary depending on several factors that are not relevant to this question.
There is a second list related to List_A called List_B:
let List_B [“residential” “industrial” “commercial” “farmland” “preservation”]
The positions of this list items never vary.
The third list I want to obtain is items of List_B that have a position corresponding to positions in items of List_A that have a value of 1. So, according to the previous examples, this would be a list made of [“industrial” “farmland” “preservation”], because “residential” and and “commercial” would have position that corresponds to a value of 0 and thus removed from the list.
Made some progress with this code that outputs a list of the positions of List_A of items that have value 1:
to-report comp-positions
report filter [ i -> item i List_A = 1 ]
n-values (length List_A) [ i -> i ]
end
but don’t know how to apply it to List_B to obtain the third list.
For completeness, three more ways:
print map last filter [ p -> first p = 1 ] (map list List_A List_B)
print reduce sentence (map [ [a b] -> item a (list [] b) ] List_A List_B)
print reduce [ [acc val] ->
ifelse-value (first val = 1) [ lput last val acc ] [ acc ]
] fput [] (map list List_A List_B)
There is probably a one-step way, but maybe use a combination of map and filter? A map using ifelse-value can produce a list of the zeroes and "List_B" items, then filter to drop the zeroes:
to filter-map
let List_A [0 1 0 1 1 ]
let List_B [ "residential" "industrial" "commercial" "farmland" "preservation"]
print filter [ i -> i != 0 ] ( map [ [ a b ] -> ifelse-value (a = 1) [b] [0] ] List_A List_B)
end

Counting the number of different links

In my code all turtles own n-features represented by a n-tuple (a1,a2,...,an). where each ai can take values 0 or 1.
I have created some links between turtles. If two turtles share k-features (coordinate-wise matching) and there is a link between them then we call the link as k-link.
How can I find for each k (between 0 to n) how many k-links are there in total?
You don't tell us much about how you have structured your code, so I'm going to assume that your n-tuples are implemented as lists (which would make the most sense in NetLogo).
Here is a full example:
turtles-own [ a ]
links-own [ k ]
globals [ n ]
to setup
ca
set n 5
crt 10 [ ; create turtles with random feature lists
set a n-values n [ random 2 ]
]
ask turtles [ ; make a full network
create-links-with other turtles
]
ask links [ ; calculate k for all links
set k k-of-feature-lists ([a] of end1) ([a] of end2)
]
foreach n-values (n + 1) [ ? ] [ ; display number of k-links
show (word ? "-links: " count links with [ k = ? ])
]
end
to-report k-of-feature-lists [ a1 a2 ]
report length filter [?] (map = a1 a2)
end
Apart from k-of-feature-lists, this is fairly trivial code. What k-of-feature-lists does is to:
transform two lists of features into a single list of booleans containing a true value if the corresponding element is equal in both feature lists and false if it is not. This is accomplished using map and the concise task syntax for =;
filter the list of booleans to keep only the true values;
report the length of that filtered list, which is equal to the number of features that where the same in a1 and a2;
There are plenty of other ways to do that (some more efficient) but this one is nice and concise.

Changing filtered values in a nested list

I have a nested list, in which each sublist is structured as follows: [[xcor ycor] weight].
Each tick I'd like to update the weight in a sample of these sublists.
I produce the sample (e.g. of size 2) from the nested list total using the Rnd extension (and very helpful answers/comments):
set total [ [[0 1] 1] [[2 3] 2] [[4 5] 3] [[6 7] 4] [[0 1] 1] ]
set sample rnd:weighted-n-of 2 total [ last ? ]
Then I update the weights in the sample (let's say multiplying them by 2) and map them to their respective [xcor ycor]-pair.
let newWeights (map [last ? * 2] sample)
let updatedSample (map list (map [first ?] sample) newWeights)
How can I then replace those entries in total, bearing in mind that it may hold duplicate entries?
This seems to be the perfect job for replace-item, but I don't know how to construct an appropriate index and then pass the respective value from updatedSample.
This is a great problem. The data structure you're using is known as an association list, or alist for short, where the keys are [xcor ycor] and the values are weights. Given your task, it's better to use the keys to look things up rather than indices. Thus, replace-item doesn't really help here. Instead, we can run map on total, using the values from updatedSample if they're there, and defaulting to the values in total. First, we need a convenience function to look things up in the alists. In lisp (a language which influenced NetLogo), this is called assoc. Here it is:
to-report assoc [ key alist ]
foreach alist [ if key = (first ?) [ report ? ] ]
report false
end
Notice that false is returned if alist doesn't contain the key. We want to use the entry returned by this function if it's not false, otherwise use something else. Thus, we need another helper function:
to-report value-or-else [ value default ]
report ifelse-value (value = false) [ default ] [ value ]
end
Finally, we can write a function that does the mapping:
to-report update-alist [ alist updated-entries ]
report map [ value-or-else (assoc first ? updated-entries) ? ] alist
end
Here's it in action:
observer> show update-alist [[[0 1] 1] [[2 3] 2] [[4 5] 3] [[6 7] 4] [[0 1] 1]] [[[0 1] 10] [[4 5] 45]]
observer: [[[0 1] 10] [[2 3] 2] [[4 5] 45] [[6 7] 4] [[0 1] 10]]
You would want to call it like update-alist total updatedSample.