Grouping lists by elements in Netlogo - list

Let us say I have a model in Netlogo and am now interested in developing a reporter/procedure that groups lists according to their first element.
For the sake of example, let's say
globals[list1 list2 list3 listoflists
ordered-list-a
ordered-list-b
]
to setup
set list1 ["a" "b" "c"]
set list2 ["a" "c" "d"]
set list3 ["b" "a" "c"]
set listoflists (list list1 list2 list3)
end
I want to create a list of lists such that in each list you have lists starting with the same element. Hence, the desired output is
[[["a" "b" "c"]["a" "c" "d"]]["b" "a" "c"]]]
i.e, where the first element aggregates all lists with an a in first place, and the second all those starting with a "b".
Ideally, this should be scalable for a large number. I tried
to create-list
set ordered-list-a []
set ordered-list-b []
(foreach listoflists [[i] ->
if item 0 i = "a" [set ordered-list-a lput i ordered-list-a]
if item 0 i = "b" [set ordered-list-b lput i ordered-list-b]
])
end
and then creating a list from a and b, which does the trick, but a) it's incredibly messy, b) requires that I know beforehand the length of the list, which I don't in the real case (it's a turtle procedure) and c) it seems like a lot of unnecessary coding.
Is there some way to extend the procedure above to any number of starting elements (maybe inside a while cycle?) and does not require the creation of a list for each initial list element?
Many thanks

I'm assuming the desired output is supposed to be: [[["a" "b" "c"]["a" "c" "d"]][["b" "a" "c"]]]. I believe you were missing a [ before the second group.
Anyway, a simple, but somewhat inefficient way would be:
; items is a list of items to be grouped
; key is an anonymous reporter that extracts the group label from a single item
to-report group-by [ items key ]
let keys remove-duplicates map key items
report map [ k -> filter [ x -> (runresult key x) = k ] items ] keys
end
Note that the above is a completely generalized grouping function. To use it in your case, you would do:
group-by listoflists [ l -> first l ]
A more efficient way would be to use the table:group-items reporter from the table extension.
table:group-items listoflists [ l -> first l ]

Related

Netlogo code for sorting agents in lists according to their properties

I am trying to sort my agents [industries] into a list and make many further lists based on that initial list sequence.
Each industry has two properties [bid-cap] and [bid-price]. I want first, a sorted list [low-bid-ind] of agents according to their ascending bid-prices, then I want to use the same sequence and fetch the values of [bid-cap] and make a list [low-cap-list], then I want to fetch bid-price for same sequence as [low-bid-ind] list and save it in a list [low-cap-list]. I only make this list so I can make another list with cumulative sum values of the [bid-cap] of agents.
Following is my code. There's an error : LPUT expected input to be a list but got the number 0 instead.
But it doesn't show where the error occurs in the code.
set agentlist-low []
set low-bid-ind []
set low-cap-list []
set sum-low-bid []
set agentlist-low industries with [group = 0]
set low-bid-ind sort-on [bid-price] agentlist-low
foreach low-bid-ind
[ the-industry ->
set low-cap-list lput ([bid-cap] of the-industry) low-cap-list ]
let sum-low 0
let m 0
foreach low-bid-ind
[set m m + 1
set sum-low sum-low + item m low-cap-list
set sum-low-bid lput sum-low sum-low-bid ]
print sum-low-bid
Can anyone help with this problem?
I have looked at Netlogo help and other questions on the internet but I am unable to find an answer.
I cannot reproduce the error you are getting. Are you sure that you are setting low-cap-list and sum-low-bid to empty lists in your actual code? That kind of error normally comes when lput is given a list which has not been initialized, as uninitialized variables in NetLogo are set to 0.
But more generally, NetLogo has a map operation that eliminates the need for looping over lists in most cases. So the loop
foreach low-bid-ind
[ the-industry ->
set low-cap-list lput ([bid-cap] of the-industry) low-cap-list ]
can be written more economically as
set low-cap-list map [the-industry -> [bid-cap] of the-industry] low-bid-ind
and the code segment
let sum-low 0
let m 0
foreach low-bid-ind
[set m m + 1
set sum-low sum-low + item m low-cap-list
set sum-low-bid lput sum-low sum-low-bid ]
can be written as a single line
set sum-low-bid map [m -> sum sublist low-cap-list 0 (m + 1)] range (length low-bid-ind)
map creates a list by performing the operation in brackets on each item in the list it is given. In the latter case, that input list is simply the numbers from 0 to the length of the low-bid-ind list.

NETLOGO: adding lists of agents' attributes to a list of lists

Hello I am using Netlogo and I am trying to create a list of lists in which every sublist is a couple of agents' attributes. In particular I declare the list as a global variable and I initialize it to an empty list. Then I ask every agent to add a list of their attribute_1 and attribute_2 to the main list. Like this:
globals[mainlist]
set mainlist []
ask agents[
set mainlist sentence [mainlist] [attribute_1 attribute_2 ]
]
This should create a new list composed by the previous mainlist and the list [attribute_1 attribute_2].
Unfortunately this doesn't work and I get the error: EXPECTED LITERAL VALUE referring to "mainlist".
How should I write my code to create my list of lists in the correct way?
Tyr's answer is technically correct, but I would like to suggest a more netlogoish way to do it. Usually, in NetLogo, you don't have to build lists one element at a time. If you find yourself doing that, you might want to stop and try to approach the problem differently.
In this particular case, you can simply take advantage of the of primitive:
[ (list attribute_1 attribute_2) ] of agents
Here is a fully working example:
breed [agents an-agent]
agents-own [attribute_1 attribute_2]
globals [mainlist]
to setup
clear-all
create-agents 10 [
set attribute_1 random 10
set attribute_2 random 10
]
set mainlist [ (list attribute_1 attribute_2) ] of agents
print mainlist
end
You can use lput or fput to add further elements to a list (lput will add the new list item at the end (l=last), whereas fput will add the new item at front (f=first)).
Additionally, you have to use the (list ... ...) primitive instead of [ ] brackets when you want to store variables in a list (brackets only work with constants).
Here is a working example:
globals[mainlist]
to test
ask n-of 20 patches [sprout 1]
set mainlist []
ask turtles
[
set mainlist lput (list xcor ycor) mainlist
]
print mainlist
end

list3 = list1 - list2 (netlogo)

I am new to NetLogo, for patient-surgeon model, I need to create a list3 which is list1 - list2. The list1 and list2 are comprised of the who number of the patients extracted from another list (for example, SET list1 lput WHO list-n).
I am using below code to subtract the list but I am not able to do so as model does not recognize ? or ?1. Please help me how to subtract two lists for n values.
set list1 []
set list2 []
;; emptying the list to add the who number of patients
;; then setting the list as per the who number of agent
;; setup patients
SET list1 lput WHO list-n
SET list2 lput WHO list-k
What I want: list3 = list1 - list2
Query used:
to test-lists
let list3 []
foreach list2 [
if (item (position ? list2) list1 = 1)[
set list3 lput ? list3
]
]
end
But I get the error:
nothing named ? is defined
As per the manual, "? is always equivalent to ?1" and need not be defined. Kindly suggest how to go about with the subtraction of the list.

NetLogo: Summing up the first element of a list in a list

I have a list that contains elements that are lists with two entries themselves:
list1 [ [15, w1] [20, w2] [30, w3] ...]
now, in a different function, I want to include a sum of the first element in each of the lists in list1 (i.e. 15 + 20 +30 ...)
what I've tried is
let weigh_dem (t_d_f * price_w) - (sum (foreach list1 [[a b] -> a] ))
but I get the error message "expected reporter.
Since I'm new on netlogo I would be glad to get advice on this, also if there is a previous post about this that I didn't see, I'd be happy to get a hint! Thanks!
You're looking for map instead of foreach. It works pretty much just like foreach except that it returns the results in a list (whereas foreach just executes the command for each item in the list). Also, the argument to the anonymous procedure when using either foreach or map on list1 will be a list containing the two items. So you need to use first to get the first item out:
let weigh_dem t_d_f * price_w - sum map [ pair -> first pair ] list1

Netlogo adding to list of lists

I am looking to add patch variable values to a list of empty lists. The patches are divided into different zones, and I'm trying to see how certain patch variables differ by zone.
I have an empty list of lists (actually contains 12 lists, but for simplicity):
set mylist [[] [] [] []]
And a list corresponding to the different zones:
set zone-list [1 2 3 4]
Here's how I'm trying to build the lists:
(foreach mylist zone-list [set ?1 lput (sum-zone-variable ?2) ?1])
to-report sum-zone-variable [ n ]
report (sum [patch-variable] of patches with [zone = n])
end
When I run this, mylist stays empty (ie unchanged). I think the problem is with the foreach statement, but I can't figure out what it is. Any help?
I can see the thinking behind foreach mylist [ set ?1 ... ], but NetLogo doesn't work that way. set ?1 ... has no effect on the original list. NetLogo lists are immutable, and ?1 is not a reference to an updatable location in a list — it's just a temporary variable into which a value has been copied. So set ?1 ... is something you will basically never write.
If I understand your question correctly, the relevant primitive here is map. This should do the job:
set mylist (map [lput (sum-zone-variable ?2) ?1] mylist zonelist)
Your basic approach is ok except that you must assign to a name. E.g.,
globals [mylist zone-list n-zones]
patches-own [zone zone-variable]
to setup
set n-zones 4
set zone-list n-values n-zones [?]
ask patches [set zone one-of zone-list]
set mylist n-values n-zones [[]]
end
to go
ask patches [set zone-variable random-float 1]
foreach zone-list [
let total sum [zone-variable] of patches with [zone = ?]
let oldvals item ? mylist
set mylist replace-item ? mylist (lput total oldvals)
]
end
However, you might want to use the table extension for this.