I would like to sum up one numeric properties (AT1) of patches that share the same ID and store the value for that ID (procedure simulation
here-below). I started with the idea of looping through the patches to find the ones that share the same ID (based on a external file).
See below a reproducible code that is not working, it is printing the sum for all patches one after another but not for just one ID, I tried several ways.
globals [
AT-data
ABC
area
]
patches-own [
ID
AT1
AT2
seed
sum_AT1
]
to setup
;; here I just create patches with different values that also appear in the list
ca
set ABC [ "A" "B" "C" "D" "E" "F" "G" "H" "I" ]
ask patches [ set seed random 10 set ID one-of ABC
ifelse (seed = 4)
[ set pcolor orange] [set pcolor white]
]
end
to load
reset-timer
; first, we load the database file
; We check to make sure the file exists first
ifelse ( file-exists? "AT_data.txt" )
[
; We are saving the data into a list, so it only needs to be loaded once.
set AT-data []
file-open "AT_data.txt"
while [ not file-at-end? ]
[
; file-read gives variables stored in a double list
; Each iteration we append the next three-tuple to the current list: ID AT1 AT2
set AT-data sentence AT-data (list (list file-read file-read file-read))
]
user-message "File loading complete!"
file-close
assign-data
stop
]
[ user-message "There is no AT_data.txt file in current directory!" ]
file-close-all
print timer
end
to assign-data
reset-timer
ask patches with [seed = 4] [
let i 1
while [i < length AT-data] [
let current-inner-list item i AT-data
ifelse (ID = item 0 current-inner-list)
[ set AT1 item 1 current-inner-list set AT2 item 2 current-inner-list
stop]
[ set i i 1 ]
]
]
print timer
end
to simulation
reset-timer
ask patches [
let i 1
while [i < length AT-data] [
let current-inner-list item i AT-data
ifelse (ID = item 0 current-inner-list)
;; I tried with and without this following line
;[ask patches with [ID = item 0 current-inner-list] [
[ set area area AT1
print area
print ID
stop
]
;]
;; this one is an alternative
;[ print sum [AT1] of patches with [ID = item 0 current-inner-list]
;print ID
;]
[ set i i 1 ]
]
]
print timer
end
AT_data.txt is
"A" 65 81
"B" 21 71
"C" 54 18
"D" 23 41
"E" 85 27
"F" 35 88
"G" 29 4
"H" 78 2
"I" 99 60
Thanks for your time !
CodePudding user response:
My first remark is that you have patches asking patches again in simulation
.
Your second solution was the simpler one to work with. The main thing here was to take it out of patch context and let the explorer run it.
For printing outputs, I suggest using a format such as print (word current-ID ": " current-sum)
. That is much cleaner for when you quickly want to check it after running the model.
to simulation-2
reset-timer
let i 0
while [i < length AT-data] [
let current-inner-list item i AT-data
let current-ID item 0 current-inner-list
let current-sum sum [AT1] of patches with [ID = current-ID]
print (word current-ID ": " current-sum)
set i i 1
]
print timer
end
For your first solution, your problem was that you only had a single area variable that you incremented. In the following example, I made area into a list, with the same length as AT-data, containing lists. Each inner list consists of an ID and a counter set to 0 [["A" 0] ["B" 0] ... ["I" 0]]
.
For this, I use the map
procedure. map
takes each separate element of a list, does a certain operation with it, and return them all as a new list. It is in general a very useful procedure to learn when you will be working with lists.
Next I iterate through all patches as you did and increment the counter of my area list for the correct ID. I have two different versions of this incrementing. The first one has a lot of local variables to clearly show how it works. You dig out the correct sublist, then dig out the correct variable, increment that variable, replace it in the sublist and replace the sublist in the main list. The second one does exactly the same but in a single line of code.
to simulation-1
reset-timer
set area map [inner-list -> list item 0 inner-list 0] AT-data ;creates a new list of lists of the form [["A" 0] ["B" 0] ... ].
ask patches [
let i 0
while [i < length AT-data] [
let current-inner-list item i AT-data
ifelse (ID = item 0 current-inner-list)
[ let inner-area-list item i area ;grab the correct innerlist
let increased-count item 1 inner-area-list AT1 ;increment the second part of this inner list
set inner-area-list replace-item 1 inner-area-list increased-count ;put the incremented count back into the inner list
set area replace-item i area inner-area-list ;put the inner list back into the complete list
;; all these can be combined into a single line of code but that is more prone to errors
;set area replace-item i area (replace-item 1 item i area (item 1 item i area AT1))
stop
]
[ set i i 1 ]
]
]
print area
print timer
end
It was only after making this entire nested list structure that I thought of the fact that you can do it with a normal list where you only have the different counters and not the ID's but this structure does make it very compact and clear and good to use for followup processing.