ggplot2: Agregar información de tamaño de muestra a las etiquetas de marca del eje x

Esta pregunta está relacionada conCree geom personalizado para calcular estadísticas de resumen y mostrarlas * fuera * de la región de trazado (NOTA: todas las funciones se han simplificado; no se realizan comprobaciones de errores para los tipos de objetos correctos, NA, etc.)

En la base R, es bastante fácil crear una función que produzca un diagrama de tira con el tamaño de muestra indicado debajo de cada nivel de la variable de agrupación: puede agregar la información de tamaño de muestra utilizandomtext() función:

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")

o puede agregar la información del tamaño de la muestra a las etiquetas de marcación del eje x utilizandoaxis()&nbsp;función:

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")

Si bien esta es una tarea muy fácil en la base R, es terriblemente compleja en ggplot2 porque es muy difícil obtener los datos que se utilizan para generar el diagrama, y si bien hay funciones equivalentes aaxis()&nbsp;(p.ej.,scale_x_discrete, etc.) no hay equivalente amtext()&nbsp;que le permite colocar fácilmente texto en coordenadas específicas dentro de los márgenes.

Traté de usar el construido enstat_summary()&nbsp;función para calcular los tamaños de muestra (es decir,fun.y = "length") y luego coloque esa información en las etiquetas de marca del eje x, pero por lo que puedo decir, no puede extraer los tamaños de muestra y luego agregarlos de alguna manera a las etiquetas de marca del eje x utilizando la funciónscale_x_discrete(), tienes que decirstat_summary()&nbsp;qué geom quieres que use. Usted podría establecergeom="text", pero luego debe proporcionar las etiquetas, y el punto es que las etiquetas deben ser los valores de los tamaños de muestra, que es lo questat_summary()&nbsp;es computación pero a la que no puede llegar (y también tendría que especificar dónde desea colocar el texto, y de nuevo, es difícil determinar dónde colocarlo para que quede directamente debajo del eje x marque las etiquetas).

La viñeta "Extendiendo ggplot2" (http://docs.ggplot2.org/dev/vignettes/extending-ggplot2.html) le muestra cómo crear su propia función estadística que le permite acceder directamente a los datos, pero el problema es que siempre tiene que definir una geom para que vaya con su función estadística (es decir,ggplot&nbsp;cree que desea trazar esta información dentro de la trama, no en los márgenes); Por lo que puedo decir, no puede tomar la información que calcula en su función estadística personalizada, no trazar nada en el área de trazado, y en su lugar pasar la información a una función de escala comoscale_x_discrete(). Aquí estaba mi intento de hacerlo de esta manera; lo mejor que pude hacer fue colocar la información del tamaño de la muestra en el 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()

Pensé que había resuelto el problema simplemente creando una función de contenedor 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")

Sin embargo, esta función no funciona correctamente con facetas. Por ejemplo:

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

muestra los tamaños de muestra para ambas facetas combinadas para cada faceta. Tendría que construir facetas en la función de envoltura, lo que frustra el punto de tratar de usar todoggplot&nbsp;tiene que ofrecer.

Si alguien tiene alguna idea de este problema, estaría agradecido. ¡Muchas gracias por tu tiempo!