ggplot2: Adicionando informações de tamanho de amostra aos rótulos de marcação do eixo x

Esta questão está relacionada aCrie geom personalizado para calcular estatísticas resumidas e exibi-las * fora * da região de plotagem (NOTA: Todas as funções foram simplificadas; nenhuma verificação de erro para tipos de objetos corretos, NAs, etc.)

Na base R, é bastante fácil criar uma função que produza um stripchart com o tamanho da amostra indicado abaixo de cada nível da variável de agrupamento: você pode adicionar as informações de tamanho da amostra usando omtext() função:

stripchart_w_n_ver1 <- function(data, x.var, y.var) {
    x <- factor(data[, x.var])
    y <- data[, y.var]
# Need to call plot.default() instead of plot because 
# plot() produces boxplots when x is a factor.
    plot.default(x, y, xaxt = "n",  xlab = x.var, ylab = y.var)
    levels.x <- levels(x)
    x.ticks <- 1:length(levels(x))
    axis(1, at = x.ticks, labels = levels.x)
    n <- sapply(split(y, x), length)
    mtext(paste0("N=", n), side = 1, line = 2, at = x.ticks)
}

stripchart_w_n_ver1(mtcars, "cyl", "mpg")

ou você pode adicionar as informações de tamanho da amostra aos rótulos de marcação do eixo x usando oaxis()&nbsp;função:

stripchart_w_n_ver2 <- function(data, x.var, y.var) {
    x <- factor(data[, x.var])
    y <- data[, y.var]
# Need to set the second element of mgp to 1.5 
# to allow room for two lines for the x-axis tick labels.
    o.par <- par(mgp = c(3, 1.5, 0))
    on.exit(par(o.par))
# Need to call plot.default() instead of plot because 
# plot() produces boxplots when x is a factor.
    plot.default(x, y, xaxt = "n", xlab = x.var, ylab = y.var)
    n <- sapply(split(y, x), length)
    levels.x <- levels(x)
    axis(1, at = 1:length(levels.x), labels = paste0(levels.x, "\nN=", n))
}

stripchart_w_n_ver2(mtcars, "cyl", "mpg")

Embora essa seja uma tarefa muito fácil na base R, ela é extremamente complexa no ggplot2 porque é muito difícil obter os dados que estão sendo usados para gerar o gráfico, e embora existam funções equivalentes aaxis()&nbsp;(por exemplo.,scale_x_discreteetc.) não há equivalente amtext()&nbsp;que permite colocar texto facilmente nas coordenadas especificadas dentro das margens.

Eu tentei usar o construído emstat_summary()&nbsp;para calcular os tamanhos das amostras (ou seja,fun.y = "length") e, em seguida, coloque essas informações nos rótulos dos marcadores do eixo x, mas até onde eu sei, você não pode extrair os tamanhos das amostras e, de alguma forma, adicioná-los aos rótulos dos marcadores do eixo x usando a funçãoscale_x_discrete()você tem que dizerstat_summary()&nbsp;qual geom você deseja que ele use. Você pode definirgeom="text", mas você deve fornecer os rótulos, e o ponto é que os rótulos devem ter os valores dos tamanhos das amostras, e é isso questat_summary()&nbsp;é computação, mas não é possível chegar lá (e você também precisa especificar onde deseja colocar o texto e, novamente, é difícil descobrir onde colocá-lo para que fique diretamente abaixo do eixo x marcadores de seleção).

A vinheta "Estendendo o ggplot2" (http://docs.ggplot2.org/dev/vignettes/extending-ggplot2.html) mostra como criar sua própria função stat que permite acessar diretamente os dados, mas o problema é que você sempre precisa definir um geom para acompanhar sua função stat (ou seja,ggplot&nbsp;pensa que você deseja plotar essas informações dentro da plotagem, não nas margens); até onde eu sei, você não pode pegar as informações que você calcula em sua função estatística personalizada, não plotar nada na área de plotagem e, em vez disso, passar as informações para uma função de balança comoscale_x_discrete(). Aqui estava minha tentativa de fazê-lo dessa maneira; o melhor que pude fazer foi colocar as informações de tamanho da amostra no valor mínimo de y para cada grupo:

StatN <- ggproto("StatN", Stat,
    required_aes = c("x", "y"), 
    compute_group = function(data, scales) {
    y <- data$y
    y <- y[!is.na(y)]
    n <- length(y)
    data.frame(x = data$x[1], y = min(y), label = paste0("n=", n))
    }
)

stat_n <- function(mapping = NULL, data = NULL, geom = "text", 
    position = "identity", inherit.aes = TRUE, show.legend = NA, 
        na.rm = FALSE, ...) {
    ggplot2::layer(stat = StatN, mapping = mapping, data = data, geom = geom, 
        position = position, inherit.aes = inherit.aes, show.legend = show.legend, 
        params = list(na.rm = na.rm, ...))
}

ggplot(mtcars, aes(x = factor(cyl), y = mpg)) + geom_point() + stat_n()

Eu pensei que tinha resolvido o problema simplesmente criando uma função de invólucro paraggplot:

ggstripchart <- function(data, x.name, y.name,  
    point.params = list(), 
    x.axis.params = list(labels = levels(x)), 
    y.axis.params = list(), ...) {
    if(!is.factor(data[, x.name]))
    data[, x.name] <- factor(data[, x.name])
    x <- data[, x.name]
    y <- data[, y.name]
    params <- list(...)
    point.params    <- modifyList(params, point.params)
    x.axis.params   <- modifyList(params, x.axis.params)
    y.axis.params   <- modifyList(params, y.axis.params)

    point <- do.call("geom_point", point.params)

    stripchart.list <- list(
        point, 
        theme(legend.position = "none")
    )

    n <- sapply(split(y, x), length)
    x.axis.params$labels <- paste0(x.axis.params$labels, "\nN=", n)
    x.axis <- do.call("scale_x_discrete", x.axis.params)
    y.axis <- do.call("scale_y_continuous", y.axis.params)
    stripchart.list <- c(stripchart.list, x.axis, y.axis)           

    ggplot(data = data, mapping = aes_string(x = x.name, y = y.name)) + stripchart.list
}


ggstripchart(mtcars, "cyl", "mpg")

No entanto, esta função não funciona corretamente com facetas. Por exemplo:

ggstripchart(mtcars, "cyl", "mpg") + facet_wrap(~am)

mostra os tamanhos de amostra para as duas facetas combinadas para cada faceta. Eu precisaria criar facetas na função wrapper, o que anula o ponto de tentar usar tudoggplot&nbsp;tem a oferecer.

Se alguém tiver alguma idéia desse problema, ficaria grato. Muito obrigado pelo seu tempo!