r ggplot2 ggproto

qqnorm y qqline en ggplot2



ggproto (8)

¿Por qué no lo siguiente?

Dado un poco de vector, por ejemplo,

myresiduals <- rnorm(100) ^ 2 ggplot(data=as.data.frame(qqnorm( myresiduals , plot=F)), mapping=aes(x=x, y=y)) + geom_point() + geom_smooth(method="lm", se=FALSE)

Pero parece extraño que tengamos que usar una función gráfica tradicional para apuntalar ggplot2.

¿No podemos obtener el mismo efecto de alguna manera comenzando con el vector para el que queremos el gráfico cuantílico y luego aplicando las funciones apropiadas "stat" y "geom" en ggplot2?

¿Hadley Wickham monitorea estas publicaciones? Quizás él pueda mostrarnos una mejor manera.

Supongamos que tenemos un modelo lineal LM que quiero un gráfico q de los residuos. Normalmente usaría los gráficos de base R:

qqnorm(residuals(LM), ylab="Residuals") qqline(residuals(LM))

Puedo averiguar cómo hacer que qqnorm forme parte de la trama, pero parece que no puedo gestionar la qqline:

ggplot(LM, aes(sample=.resid)) + stat_qq()

Sospecho que me estoy perdiendo algo bastante básico, pero parece que debería haber una manera fácil de hacerlo.

EDITAR: Muchas gracias por la solución a continuación. Modifiqué el código (muy ligeramente) para extraer la información del modelo lineal, de modo que la gráfica funciona como la gráfica de conveniencia en el paquete de gráficos de la base R.

ggQQ <- function(LM) # argument: a linear model { y <- quantile(LM$resid[!is.na(LM$resid)], c(0.25, 0.75)) x <- qnorm(c(0.25, 0.75)) slope <- diff(y)/diff(x) int <- y[1L] - slope * x[1L] p <- ggplot(LM, aes(sample=.resid)) + stat_qq(alpha = 0.5) + geom_abline(slope = slope, intercept = int, color="blue") return(p) }


Con ggplot2 versión 2.2.1.9000, se implementa la nueva función stat_qq_line ( https://github.com/tidyverse/ggplot2/blob/master/NEWS.md ) y se puede agregar una línea qq con:

ggplot(LM, aes(sample = .resid)) + geom_qq() + stat_qq_line()

La versión aún no está disponible en CRAN a partir de mayo de 2018, puede instalarla desde GitHub con: devtools::install_github(''tidyverse/ggplot2'')


Desde la versión 2.0, ggplot2 tiene una interfaz bien documentada para la extensión; así que ahora podemos escribir fácilmente una nueva estadística para la línea qq en sí misma (lo que hice por primera vez, por lo que las mejoras son welcome ):

qq.line <- function(data, qf, na.rm) { # from .com/a/4357932/1346276 q.sample <- quantile(data, c(0.25, 0.75), na.rm = na.rm) q.theory <- qf(c(0.25, 0.75)) slope <- diff(q.sample) / diff(q.theory) intercept <- q.sample[1] - slope * q.theory[1] list(slope = slope, intercept = intercept) } StatQQLine <- ggproto("StatQQLine", Stat, # http://docs.ggplot2.org/current/vignettes/extending-ggplot2.html # https://github.com/hadley/ggplot2/blob/master/R/stat-qq.r required_aes = c(''sample''), compute_group = function(data, scales, distribution = stats::qnorm, dparams = list(), na.rm = FALSE) { qf <- function(p) do.call(distribution, c(list(p = p), dparams)) n <- length(data$sample) theoretical <- qf(stats::ppoints(n)) qq <- qq.line(data$sample, qf = qf, na.rm = na.rm) line <- qq$intercept + theoretical * qq$slope data.frame(x = theoretical, y = line) } ) stat_qqline <- function(mapping = NULL, data = NULL, geom = "line", position = "identity", ..., distribution = stats::qnorm, dparams = list(), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { layer(stat = StatQQLine, data = data, mapping = mapping, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(distribution = distribution, dparams = dparams, na.rm = na.rm, ...)) }

Esto también se generaliza sobre la distribución (exactamente como stat_qq hace stat_qq ), y se puede usar de la siguiente manera:

> test.data <- data.frame(sample=rnorm(100, 10, 2)) # normal distribution > test.data.2 <- data.frame(sample=rt(100, df=2)) # t distribution > ggplot(test.data, aes(sample=sample)) + stat_qq() + stat_qqline() > ggplot(test.data.2, aes(sample=sample)) + stat_qq(distribution=qt, dparams=list(df=2)) + + stat_qqline(distribution=qt, dparams=list(df=2))

(Desafortunadamente, dado que la qqline está en una capa separada, no pude encontrar una manera de "reutilizar" los parámetros de distribución, pero eso solo debería ser un problema menor).


El diagnóstico QQ estándar para modelos lineales traza cuantiles de los residuos estandarizados vs. cuantiles teóricos de N (0,1). @ La función ggQQ de Peter traza los residuos. El fragmento a continuación lo enmienda y agrega algunos cambios cosméticos para hacer que la trama se parezca más a lo que se obtendría de la plot(lm(...)) .

ggQQ = function(lm) { # extract standardized residuals from the fit d <- data.frame(std.resid = rstandard(lm)) # calculate 1Q/4Q line y <- quantile(d$std.resid[!is.na(d$std.resid)], c(0.25, 0.75)) x <- qnorm(c(0.25, 0.75)) slope <- diff(y)/diff(x) int <- y[1L] - slope * x[1L] p <- ggplot(data=d, aes(sample=std.resid)) + stat_qq(shape=1, size=3) + # open circles labs(title="Normal Q-Q", # plot title x="Theoretical Quantiles", # x-axis label y="Standardized Residuals") + # y-axis label geom_abline(slope = slope, intercept = int, linetype="dashed") # dashed reference line return(p) }

Ejemplo de uso:

# sample data (y = x + N(0,1), x in [1,100]) df <- data.frame(cbind(x=c(1:100),y=c(1:100+rnorm(100)))) ggQQ(lm(y~x,data=df))


El siguiente código te dará la trama que quieras. El paquete ggplot no parece contener código para calcular los parámetros de la línea qq, por lo que no sé si es posible lograr una trama de este tipo en un trazador de líneas (comprensible).

qqplot.data <- function (vec) # argument: vector of numbers { # following four lines from base R''s qqline() y <- quantile(vec[!is.na(vec)], c(0.25, 0.75)) x <- qnorm(c(0.25, 0.75)) slope <- diff(y)/diff(x) int <- y[1L] - slope * x[1L] d <- data.frame(resids = vec) ggplot(d, aes(sample = resids)) + stat_qq() + geom_abline(slope = slope, intercept = int) }


Podrías robar una página de los veteranos que hicieron esto con papel de probabilidad normal. Una mirada cuidadosa a un gráfico ggplot () + stat_qq () sugiere que se puede agregar una línea de referencia con geom_abline (), como este

df <- data.frame( y=rpois(100, 4) ) ggplot(df, aes(sample=y)) + stat_qq() + geom_abline(intercept=mean(df$y), slope = sd(df$y))


También puede agregar Intervalos de confianza / bandas de confianza con esta función (Partes del código copiadas del car:::qqPlot )

gg_qq <- function(x, distribution = "norm", ..., line.estimate = NULL, conf = 0.95, labels = names(x)){ q.function <- eval(parse(text = paste0("q", distribution))) d.function <- eval(parse(text = paste0("d", distribution))) x <- na.omit(x) ord <- order(x) n <- length(x) P <- ppoints(length(x)) df <- data.frame(ord.x = x[ord], z = q.function(P, ...)) if(is.null(line.estimate)){ Q.x <- quantile(df$ord.x, c(0.25, 0.75)) Q.z <- q.function(c(0.25, 0.75), ...) b <- diff(Q.x)/diff(Q.z) coef <- c(Q.x[1] - b * Q.z[1], b) } else { coef <- coef(line.estimate(ord.x ~ z)) } zz <- qnorm(1 - (1 - conf)/2) SE <- (coef[2]/d.function(df$z)) * sqrt(P * (1 - P)/n) fit.value <- coef[1] + coef[2] * df$z df$upper <- fit.value + zz * SE df$lower <- fit.value - zz * SE if(!is.null(labels)){ df$label <- ifelse(df$ord.x > df$upper | df$ord.x < df$lower, labels[ord],"") } p <- ggplot(df, aes(x=z, y=ord.x)) + geom_point() + geom_abline(intercept = coef[1], slope = coef[2]) + geom_ribbon(aes(ymin = lower, ymax = upper), alpha=0.2) if(!is.null(labels)) p <- p + geom_text( aes(label = label)) print(p) coef }

Ejemplo:

Animals2 <- data(Animals2, package = "robustbase") mod.lm <- lm(log(Animals2$brain) ~ log(Animals2$body)) x <- rstudent(mod.lm) gg_qq(x)