Many stat layers (notable exception is stat_identity()
) have 'computed variables'. You'll see these computed variables documented in the 'Computed variables' section of, for example ?stat_density
. These variables become columns in the working data for a layer, which you can inspect by calling layer_data(last_plot())
after printing a plot.
The after_stat()
function, as you pointed out, does nothing to the data. However, because the expressions are captured before being evaluated, ggplot2 can parse the language in the expression to see if the expression contains a call to after_stat()
(and a few others). The logic of this is in the source code that Jon Spring pointed out.
If you look at the compute_aesthetics
methods of a layer, you can see that the evaluation of these modified expressions is exempt from evaluation.
> geom_point()$compute_aesthetics
<ggproto method>
<Wrapper function>
function (...)
f(..., self = self)
<Inner function (f)>
function (self, data, plot)
{
# ... omitted for brevity
calculated <- is_calculated_aes(aesthetics)
modifiers <- is_scaled_aes(aesthetics)
aesthetics <- aesthetics[!set & !calculated & !modifiers]
# ...
evaled <- lapply(aesthetics, eval_tidy, data = data, env = env)
# ...
}
For the after_stat()
function, these aesthetics are evaluated after the statistic has been computed in e.g. the geom_point()$map_statistic
method. I'm not 100% what is going on here, but it looks like a special environment is build for the evaluation of the modified expressions. I think particular data masks that ensure that the evaluation takes place in the context of the working data and thus have access to the computed variables (rather than global or user-specified layer data which do not).
> geom_point()$map_statistic
<ggproto method>
<Wrapper function>
function (...)
f(..., self = self)
<Inner function (f)>
function (self, data, plot)
{
# ... omitted for brevity
new <- strip_dots(aesthetics[is_calculated_aes(aesthetics) |
is_staged_aes(aesthetics)])
if (length(new) == 0)
return(data)
env <- child_env(baseenv(), stat = stat, after_stat = after_stat)
stage_mask <- child_env(emptyenv(), stage = stage_calculated)
mask <- new_data_mask(as_environment(data, stage_mask), stage_mask)
mask$.data <- as_data_pronoun(mask)
new <- substitute_aes(new)
stat_data <- lapply(new, eval_tidy, mask, env)
# ...
}