web-dev-qa-db-de.com

qqnorm und qqline in ggplot2

Angenommen, ich habe ein lineares Modell LM, das ich möchte, dass ein Qq-Diagramm der Residuen erstellt wird. Normalerweise würde ich die R-Basisgrafik verwenden:

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

Ich kann herausfinden, wie man den qqnorm-Teil des Diagramms erhält, aber ich scheine nicht mit der qqline fertigzuwerden:

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

Ich vermute, ich vermisse etwas ziemlich grundlegendes, aber es scheint eine einfache Möglichkeit zu geben, dies zu tun.

EDIT: Vielen Dank für die Lösung unten. Ich habe den Code (sehr leicht) geändert, um die Informationen aus dem linearen Modell zu extrahieren, damit die Darstellung wie die Komfortdarstellung im R-Basis-Grafikpaket funktioniert.

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)
}
51
Peter

Mit dem folgenden Code erhalten Sie die gewünschte Darstellung. Das Paket ggplot scheint keinen Code für die Berechnung der Parameter von qqline zu enthalten. Ich weiß nicht, ob es möglich ist, eine solche Darstellung in einem (verständlichen) Einzeiler zu erzielen.

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)

}
50
Aaron

Sie können mit dieser Funktion auch Vertrauensintervalle/Vertrauensbereiche hinzufügen (Teile des aus car:::qqPlot kopierten Codes).

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
}

Beispiel:

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

enter image description here

21
Rentrop

Ggplot2 verfügt seit Version 2.0 über eine gut dokumentierte Schnittstelle zur Erweiterung. so können wir jetzt leicht selbst einen neuen stat für die qqline schreiben (was ich zum ersten mal gemacht habe, also verbesserungen sind welcome ):

qq.line <- function(data, qf, na.rm) {
    # from stackoverflow.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, ...))
}

Dies verallgemeinert auch die Verteilung (genau wie stat_qq) und kann wie folgt verwendet werden:

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

(Da sich qqline auf einer separaten Ebene befindet, konnte ich leider keine Möglichkeit finden, die Verteilungsparameter erneut zu verwenden, aber das sollte nur ein kleines Problem sein.)

11
phg

Die Standard-Q-Q-Diagnose für lineare Modelle zeigt Quantile der standardisierten Residuen gegenüber theoretischen Quantilen von N (0,1). @ Peter's ggQQ-Funktion zeichnet die Residuen auf. Das folgende Snippet korrigiert das und fügt einige kosmetische Änderungen hinzu, um das Plot eher zu dem zu machen, was man von plot(lm(...)) bekommt.

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

Anwendungsbeispiel:

# 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))
11
jlhoward

Warum nicht das Folgende?

In Anbetracht eines Vektors sagen wir 

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)

Es scheint aber merkwürdig, dass wir eine herkömmliche Grafikfunktion verwenden müssen, um ggplot2 zu unterstützen.

Können wir nicht irgendwie den gleichen Effekt erzielen, indem wir mit dem Vektor beginnen, für den die Quantilendarstellung gewünscht wird, und dann die entsprechenden "stat" - und "geom" -Funktionen in ggplot2 anwenden?

Überwacht Hadley Wickham diese Posts? Vielleicht kann er uns einen besseren Weg zeigen.

9
Jacob Wegelin

Mit der neuesten ggplot2-Version (> = 3.0) ist die neue Funktion stat_qq_line implementiert ( https://github.com/tidyverse/ggplot2/blob/master/NEWS.md ) und eine qq-Zeile für Modellrestwerte hinzugefügt mit:

library(ggplot2)
model <- lm(mpg ~ wt, data=mtcars)
ggplot(model, aes(sample = rstandard(model))) + geom_qq() + stat_qq_line()

rstandard(model) wird benötigt, um das standardisierte Residuum zu erhalten. (Kredit @jlhoward und @qwr)

Wenn Sie in stat_qq_line () einen Fehler erhalten: Funktion "stat_qq_line" konnte nicht gefunden werden, ist Ihre ggplot2-Version zu alt und Sie können dies durch ein Upgrade Ihres ggplot2-Pakets beheben: install.packages("ggplot2").

6
LmW.

Sie könnten eine Seite von den Oldtimern stehlen, die dieses Zeug mit normalem Wahrscheinlichkeitspapier erledigt haben. Ein genauerer Blick auf eine ggplot () + stat_qq () - Grafik legt nahe, dass eine Referenzlinie mit geom_abline () hinzugefügt werden kann

df <- data.frame( y=rpois(100, 4) )

ggplot(df, aes(sample=y)) +
  stat_qq() +
  geom_abline(intercept=mean(df$y), slope = sd(df$y))
4
Mike Anderson

ggplot2 v.3.0.0 hat jetzt ein qqline stat. Von der Hilfeseite:

df <- data.frame(y = rt(200, df = 5))
p <- ggplot(df, aes(sample = y))
p + stat_qq() + stat_qq_line()

! ggplot2 v3.0.0 Beispielwerte für qqnorm plus abline ] 1

0
Richard Careaga