4

The following might be a basic programming question for Netlogo. I'd like to write generic reporters that I can supply arguments for what they should report on. Suppose the following program:

turtles-own [
  houses
  cars
]

to setup
  clear-all
  create-turtles 10
  reset-ticks
end

to go
  ask turtles [
    set houses houses + random 2
    set cars cars + random 5
  ]
  
  tick
end

I can write a reporter on the mean of houses as such:

to-report mean-houses
  report mean [ houses ] of turtles
end

But I'd like to have a generic reporter that I can also use to report the mean of cars, like so:

to-report means [ param ]
  report mean [ param ] of turtles
end

However this does not work as intended:

setup
repeat 15 [go]
show means houses

> ERROR: You can't use HOUSES in an observer context, because HOUSES is turtle-only.*

How can I have Netlogo evealuate param in the context of turtles in such an instance? I am familiar with how to do this in R (e.g., via tidy evaluation masking with {{ x }}, or the earlier quoting mechanism), but unfamiliar with how to translate this to Netlogo.

Thomas K
  • 3,242
  • 15
  • 29
  • I am not able to reproduce your problem. By using your exact same code, running `mean-houses` as the observer in the Command Center gives me no error and reports the mean number of `houses` across turtles: `observer> setup observer> repeat 15 [go] observer> show mean-houses observer: 8.8` – Matteo Dec 20 '21 at 11:21
  • @Matteo Sorry for being unclear: `setup -> repeat 15 [go] -> show means houses`. And then in extension: `show means cars`. Goal: avoid duplication of code. – Thomas K Dec 20 '21 at 12:07
  • Ah sorry, I think it was my fault misinterpreting your question. To be honest I am not sure there is a way around this, or at least I cannot come up with one. I'll keep giving it a thought, but I also ask: is there a real advantage in having such a procedure being run by the observer where one still needs to specify the variable of interest? In other words, is there a real advantage in having `means [houses]` as opposed to `mean [houses] of turtles`? ...unless this example using `mean` is just for the sake of a minimal example, and what you are actually doing involves much longer calculations – Matteo Dec 20 '21 at 14:01
  • The example is mainly for the sake of simplicity (but I also had this exact use case). In my real model I calculate the gini coefficient for three different quantities - copying the code three times seemed very verbose. – Thomas K Dec 20 '21 at 14:10
  • Makes sense. There actually is a workaround, see answer – Matteo Dec 20 '21 at 14:29

3 Answers3

6

As the error states, you are trying to pass a turtle-only variable (houses) to a procedure that is being used in observer context which doesn't work because Netlogo will try passing the variable to the procedure before actually running the procedure.

Instead, you could use the following code. With this example, you first use the "of"-primitive to extract the houses/cars variables as a list. Only then do you pass them to the "means"-procedure, which is being run by the observer.

to-report means [ param ]
  report mean param 
end
show means [houses] of turtles
show means [cars] of turtles
LeirsW
  • 2,070
  • 4
  • 18
  • A good answer will always include an explanation why this would solve the issue, so that the OP and any future readers can learn from it. – Tyler2P Dec 20 '21 at 15:13
  • @Tyler2P Thank you for your remark. I have updated my answer to contain the reasoning behind this change. It might still be a little chaotic as I am not yet used to the jargon. – LeirsW Dec 20 '21 at 16:17
  • Yes. This s the correct and most simplest answer for delivering a list of values from an agentset to a reporter that acts on that list. Anything else is just adding complexity. I explore that in my answer. – TurtleZero Dec 29 '21 at 21:21
2

You can use runresult, provided you are happy to pass the procedure's argument as a string:

to-report means [varname]
  report mean [runresult varname] of turtles
end

Trying it in the Command Center:

observer> setup
observer> repeat 15 [go]
observer> show means "houses"
observer: 7.4
Matteo
  • 2,774
  • 1
  • 6
  • 22
2

The NetLogo Way

<reporter> [ <expression> ] of <agentset>

Note: I use "agents" below, since this answer applies to any NetLogo agent, be it turtles, turtle breeds, patches, or links.

Rather than passing an agent variable (or expression) to a procedure and then somehow make the agents make a list and then act on the list, NetLogo makes it easy to first make the list from an expression evaluated individually by every agent in an agentset, so you can pass that list to some reporter. The syntax (as you know) is:

;; return list of <expression>
;; calculated by each member of agentset
[ expression ] of agentset

So, if this is your reporter:

to-report gini [ samples ] 
  ;; samples will be sorted here, don't pre-sort!
  ;; best guess -- please correct if wrong
  ;; source:
  ;; https://en.wikipedia.org/wiki/Gini_coefficient#Calculation
  let n length samples 
  let indexes (range 1 (n + 1))
  let s1 2 * sum (map  [[i y] -> i * y] indexes sort samples)
  let s2 n * sum samples
  let G (s1 / s2) - ((n + 1) / n) 
  report G 
end

Then you might calculate the gini for your various measures like this:

print gini [ measure-1 ] of turtles
print gini [ measure-2 ] of turtles
print gini [ measure-3 ] of turtles

Which makes sense, since gini is a function that takes a set of samples. And this lets you easily use other sub-populations or breeds or whatever.

"Abbreviated" Syntax

I am about to lead you "down the garden path," so if you like what you've seen so far, you can stop here. If you want a little chuckle, read on.

Perhaps the above is just too verbose for you, and you really, really, want to get rid of "of turtles?" (even though you might later want to apply the generic function even more generically to sub-populations?)

Well, you could. We could write it using an "anonymous reporter":

print gini [-> houses ]

And then gini might look like this:

to-report gini [ sample-expression ]
  let samples sort [ run-result sample-expression ] of turtles
  ;;; ... the rest of your gini function here
  report G
end

OK, but you've embedded "of turtles" in your function. But what if later you want to evaluate this function for some sub-population or perhaps use patches instead of turtles?

You might fix that by taking the agentset as an input, as well.

to-report gini [ anon-reporter source ]
  let samples [ run-result anon-reporter ] of source
  ;; etc
end

and then write:

print gini [-> measure-1 ] turtles
print gini [-> measure-2 ] turtles
print gini [-> measure-3 ] turtles

Ugh, a bit awkward. Let's add a little syntax candy:

to-report of_ [ agents ] report agents end

NOW we can write:

print gini [-> measure-1 ] of_ turtles
print gini [-> measure-2 ] of_ turtles
print gini [-> measure-3 ] of_ turtles

Oh, Dear. That looks rather familiar, doesn't it? But rather more verbose than when we started.

So now we are back to:

print gini [ measure-1 ] of turtles
TurtleZero
  • 1,013
  • 7
  • 15
  • 1
    Many thanks for providing the worked examples. Your implementation of calculating the gini actually was way faster than what I was using, so this helped immensely. – Thomas K Jan 03 '22 at 11:30
  • Please, I think that function was wrong. The edit seems more correct. source:: https://en.wikipedia.org/wiki/Gini_coefficient#Calculation – TurtleZero Jan 05 '22 at 17:37