31

I can not find the documentation for the double dots around density

set.seed(1234)
df <- data.frame(cond = factor(rep(c("A","B"), each=200)), rating = c(rnorm(200),rnorm(200, mean=.8)))
print(head(df))
print(ggplot(df, aes(x=rating)) + 
    geom_histogram(aes(y=..density..),      # Histogram with density instead of count on y-axis
                   binwidth=.5,
                   colour="black", fill="white") +
    geom_density(alpha=.2, fill="#FF6666") +
    geom_vline(aes(xintercept=mean(rating, na.rm=T)),   # Ignore NA values for mean
               color="red", linetype="dashed", size=1))

Do you know what operator they represent ?

Edit

I know what it does when used in a geom, I would like to know what it is. For instance, the single dot operator is defined as

> .
function (..., .env = parent.frame()) 
{
    structure(as.list(match.call()[-1]), env = .env, class = "quoted")
}
<environment: namespace:plyr>

If I redefine density, then ..density.. has a different effect, so it seems XX -> ..XX.. is an operator. I would like to find how it is defined.

nicolas
  • 9,549
  • 3
  • 39
  • 83
  • That's the signal for `ggpolot2` to do it's own internal computation of the value rather than look for the value in the workspace. – Bryan Hanson Jul 06 '13 at 12:19
  • yes I see the effect. I can replace by ..count.. to get he regular histogram. but I wonder as language construct what is it really. if I redefine the function 'density' that impacts the drawing, so fun x -> ..x.. is an operator on its own – nicolas Jul 06 '13 at 13:20
  • for instance for . we have **> .** function (..., .env = parent.frame()) { structure(as.list(match.call()[-1]), env = .env, class = "quoted") } – nicolas Jul 06 '13 at 13:27
  • You'll probably have to dig into the source code for `ggplot2` to find the exact definition/mechanism. – Bryan Hanson Jul 06 '13 at 15:56
  • 4
    reading `?stat_bin` provides *no* insight to this question – Chris Fonnesbeck Oct 31 '13 at 18:24
  • 1
    This is a great tutorial http://www.cookbook-r.com/Graphs/Plotting_distributions_(ggplot2)/, but I have also wondered the same thing. Great question. – aaiezza Sep 13 '16 at 16:03
  • 2
    As of ggplot2 3.3.0, the `..var..` variables are superseded by the `after_stat()` function. – Gregor Thomas Mar 22 '21 at 20:21

1 Answers1

35

Unlike many other languages, in R, the dot is perfectly valid in identifiers. In this case, ..count.. is an identifier. However, there is special code in ggplot2 to detect this pattern, and to strip the dots. It feels unlikely that real code would use identifiers formatted like that, and so this is a neat way to distinguish between defined and calculated aesthetics.

The relevant code is at the end of layer.r:

# Determine if aesthetic is calculated
is_calculated_aes <- function(aesthetics) {
  match <- "\\.\\.([a-zA-z._]+)\\.\\."
  stats <- rep(FALSE, length(aesthetics))
  grepl(match, sapply(aesthetics, deparse))
}

# Strip dots from expressions
strip_dots <- function(aesthetics) {
  match <- "\\.\\.([a-zA-z._]+)\\.\\."
  strings <- lapply(aesthetics, deparse)
  strings <- lapply(strings, gsub, pattern = match, replacement = "\\1")
  lapply(strings, function(x) parse(text = x)[[1]]) 
}

It is used further up above in the map_statistic function. If a calculated aesthetic is present, another data frame (one that contains e.g. the count column) is used for the plot.

The single dot . is just another identifier, defined in the plyr package. As you can see, it is a function.

krlmlr
  • 25,056
  • 14
  • 120
  • 217
  • awesome. so this is reflection based wizardry. good to know as I imagine it represent a whole pattern in some R libs, and provides one documented entry point in ggplot. – nicolas Jul 07 '13 at 06:41
  • @nicolas: I'm not sure if the term "reflection" applies here. `ggplot2` is simply looking at the data and taking different action if the data is formatted in a certain way. – krlmlr Jul 08 '13 at 07:47
  • 1
    well the binding is not static, and is computed on the fly, based on some attribute. may be it is not reflection, but it looks close to it. – nicolas Jul 08 '13 at 11:15
  • 4
    In the ggplot2 package, the double dots approach will be replaced by the `calc()` function. Instead of using `..density..` to indicate that you want to use the density of the inherited aesthetic, you can use `calc(density)`. I don't believe this is in the distribution version as the writing of this comment. https://github.com/tidyverse/ggplot2/blob/2184d962b2326ffab0cec4977b22795df004c149/R/aes-calculated.r – Paul de Barros Dec 07 '17 at 16:09
  • 3
    Actually, I just stumbled onto this. And they renamed the `calc` function to `stat` in ggplot2 3 (Which seems to be a more fitting name) – Alex Recuenco May 23 '19 at 07:02
  • Now it's called `after_stat`. Variables documented in "Computed variables" sections of the docs tell you what you can use with it. Example usage: `geom_hex(aes(fill=after_stat(log1p(count))))`. Excited to see what it's called next year! – ivirshup Mar 02 '21 at 06:52