Search code examples
netlogo

How can we count agents according to specific variables?


We are trying to solve the following problem:

Each agent has its specific code and at each iteration the list of agents alive in that iteration is updated. We want to calculate how many agents are die of each code present in the list per iteration. We have these procedures below (code). Obs: We need to use the profile list because of the outputs that are exported by turtle profile

Sorry for the big code below, but we tried our best to reduce it to be reproducible code

Thanks in advance

globals [ ListProfiles Death ]

turtles-own [ profiles-code metabolism-code reproduction-code metabolism reproduction resource-turtle ]

patches-own [ resources ]

to setup
  ca
  prepare
  ask patches [ set resources random 100 ]
  let list1 ( list 2 4 )
  let list2 ( list 5 10 )
  (
    foreach list1
  [
    this-metabolism ->

      foreach list2
      [
        this-reproduction ->
        ask n-of 1 patches
        [
          sprout 1
          [
            set metabolism this-metabolism
            set reproduction this-reproduction
            setup-turtles
          ]
        ]
      ]
    ]
  )
  reset-ticks
end

to setup-turtles
  (
    ifelse
    metabolism = 2 [ set metabolism-code "M1" ]
    metabolism = 4 [ set metabolism-code "M2" ]
  )

  (
    ifelse
    reproduction = 5 [ set reproduction-code "R1" ]
    reproduction = 10 [ set reproduction-code "R2" ]
  )
  set profiles-code ( word metabolism-code reproduction-code )
  print ( word "profiles-code: " profiles-code )
end

to go
  ListProfilesProc
  MetaboProc
  ProbDieProc
  output

  tick
end

to ListProfilesProc
  set ListProfiles [ ]
  ask turtles [
    set ListProfiles lput profiles-code ListProfiles
  ]
  set ListProfiles remove-duplicates ListProfiles
end

to MetaboProc
  ask turtles [
    (
      ifelse
    metabolism = 2
      [
        set resource-turtle ( resources - metabolism )
        if resource-turtle <= 60 [ DieProc ]
        (
          ifelse
          reproduction = 5
          [
            if resource-turtle >= 5 [ hatch 1 ]
          ]
          reproduction = 10
          [
            if resource-turtle >= 10 [ hatch 1 ]
          ]
        )
      ]
    )
  ]
end

to DieProc
  let code profiles-code
  foreach ListProfiles [ lp ->    ;; I think, here's the problem... I can't individualize the kills by code. I've already tried several things. And therefore I can't get the output of the deaths by code. It is always repeated (general calculation)...
    
    if lp = code
    [
      set Death Death + 1
    ]
  ]
  die
end

to ProbDieProc
  ask turtles
  [
    let prob-die random-float 1.01
    if prob-die < 0.77 [ DieProc ]
  ]
end

to prepare
  carefully
    [ file-delete ( word "output.csv" ) ]
  [ ]
  file-open  ( word "output.csv" )
  file-print ( word "code_profile,death,tick" )
  file-close
end

to output
  file-open ( "output.csv" )
  foreach ListProfiles
  [
    t ->
    file-print ( word t "," Death "," ticks )
  ]
  file-close
end

Solution

  • Deaths as you have it is a global variable, which means that any time any turtle accesses it (either to update or read), they are all "sharing" the same value. Instead, you need to track deaths for each profile type. The way you have it, where profiles-code differentiates the different turtle types, you can tackle this a few ways- for example: you could create specific Death trackers for each profile type (eg. Death-M1R1, Death-M1R2... etc.) and output that as needed; or you could use a list, where you update the items in the list that correspond to a specific profile; or you could use a list of lists and use position to grab the death value for a different breed; or you could use the tables extension, which is the example I will show below because I think it's the cleanest and most explicit.

    The tables extension allows what is similar to a dictionary structure if you've used that in other coding languages, where you have a {KEY: VALUE} pair such that if you enter the KEY you get out the VALUE. So the general workflow here is to build a dictionary to store the name and death count for each profile type. Then, anytime a turtle dies it will update the death count for its profile type in the dictionary.

    To keep it simple, I've made only minor changes to your code above, and added comments where I made (most) updates:

    extensions [ table ]
    
    globals [ ListProfiles Death NInitial NFinal R Birth deaths-dict ]
    
    turtles-own [ profiles-code metabolism-code reproduction-code metabolism reproduction resource-turtle ]
    
    patches-own [ resources ]
    
    to setup
      ca
      prepare
      ; Define deaths-list as a list
      set deaths-dict table:make
      ask patches [ set resources random 100 ]
      let list1 ( list 2 4 )
      let list2 ( list 5 10 )
      (
        foreach list1
      [
        this-metabolism ->
    
          foreach list2
          [
            this-reproduction ->
            ask n-of 1 patches
            [
              sprout 1
              [
                set metabolism this-metabolism
                set reproduction this-reproduction
                setup-turtles
                ; Add each metabolism / reproduction to the deaths dictionary with 0 as initial deaths value
                table:put deaths-dict ( word metabolism-code reproduction-code ) 0
              ]
            ]
          ]
        ]
      )
      print deaths-dict
      reset-ticks
    end
    
    to setup-turtles
      (
        ifelse
        metabolism = 2 [ set metabolism-code "M1" ]
        metabolism = 4 [ set metabolism-code "M2" ]
      )
    
      (
        ifelse
        reproduction = 5 [ set reproduction-code "R1" ]
        reproduction = 10 [ set reproduction-code "R2" ]
      )
      set profiles-code ( word metabolism-code reproduction-code )
      print ( word "profiles-code: " profiles-code )
    end
    
    to go
      ; Stop the model if no turtles exist
      if not any? turtles [ stop ]
      ListProfilesProc
      MetaboProc
      ProbDieProc
      output
      tick   
    end
    
    to ListProfilesProc
      ; Simple way to get a sorted list of profile types
      set ListProfiles sort remove-duplicates [ profiles-code ] of turtles
    end
    
    to MetaboProc
      ask turtles [
        (
          ifelse
        metabolism = 2
          [
            set resource-turtle ( resources - metabolism )
            if resource-turtle <= 60 [ DieProc ]
            (
              ifelse
              reproduction = 5
              [
                if resource-turtle >= 5 [ hatch 1 ]
              ]
              reproduction = 10
              [
                if resource-turtle >= 10 [ hatch 1 ]
              ]
            )
          ]
        )
      ]
    end
    
    to ProbDieProc
      print "Running die proc..."
    
      ask turtles
      [
        let prob-die random-float 1
        if prob-die < 0.4 [
          DieProc
        ]
      ]
    end
    
    to DieProc
      ; Pull current death count for this profile
      let current-profile-death-count table:get deaths-dict profiles-code
      ; Increase that death count by one
      let current-profile-new-death-count current-profile-death-count + 1
      ; Update the death count in the master dictionary
      table:put deaths-dict profiles-code current-profile-new-death-count
      die
    end
    
    to prepare
      carefully
        [ file-delete ( word "output.csv" ) ]
      [ ]
      print "opening..."
      file-open  ( word "output.csv" )
      file-print ( word "code_profile,death,tick" )
      file-close
    end
    
    to output
      file-open ( "output.csv" )
      foreach ListProfiles
      [
        t ->
        let profile-deaths table:get deaths-dict t
        file-print ( word t "," profile-deaths "," ticks )
      ]
      file-close
    end
    

    Output then looks something like:

    enter image description here