'Looping through patches that share a common properties in Netlogo

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 !



Solution 1:[1]

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 observer 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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1