An anderer Stelle in diesem Thread habe ich eine einfache, aber etwas spontane Lösung für die Unterabtastung der Punkte vorgeschlagen. Es ist schnell, erfordert jedoch einige Experimente, um großartige Diagramme zu erstellen. Die zu beschreibende Lösung ist um eine Größenordnung langsamer (bis zu 10 Sekunden für 1,2 Millionen Punkte), ist jedoch adaptiv und automatisch. Bei großen Datenmengen sollte dies beim ersten Mal zu guten Ergebnissen führen und dies relativ schnell.
Dn
Ermitteln Sie die maximale vertikale Abweichung zwischen der Verbindungslinie der Extrema von ( x , y)ty
Es sind einige Details zu beachten, insbesondere, um mit Datensätzen unterschiedlicher Länge fertig zu werden. Ich tue dies, indem ich die kürzere durch die Quantile ersetze, die der längeren entsprechen: In der Tat wird anstelle der tatsächlichen Datenwerte eine stückweise lineare Approximation der EDF der kürzeren verwendet. ("Kürzer" und "länger" können durch Einstellen umgekehrt werden use.shortest=TRUE
.)
Hier ist eine R
Implementierung.
qq <- function(x0, y0, t.y=0.0005, use.shortest=FALSE) {
qq.int <- function(x,y, i.min,i.max) {
# x, y are sorted and of equal length
n <-length(y)
if (n==1) return(c(x=x, y=y, i=i.max))
if (n==2) return(cbind(x=x, y=y, i=c(i.min,i.max)))
beta <- ifelse( x[1]==x[n], 0, (y[n] - y[1]) / (x[n] - x[1]))
alpha <- y[1] - beta*x[1]
fit <- alpha + x * beta
i <- median(c(2, n-1, which.max(abs(y-fit))))
if (abs(y[i]-fit[i]) > thresh) {
assemble(qq.int(x[1:i], y[1:i], i.min, i.min+i-1),
qq.int(x[i:n], y[i:n], i.min+i-1, i.max))
} else {
cbind(x=c(x[1],x[n]), y=c(y[1], y[n]), i=c(i.min, i.max))
}
}
assemble <- function(xy1, xy2) {
rbind(xy1, xy2[-1,])
}
#
# Pre-process the input so that sorting is done once
# and the most detail is extracted from the data.
#
is.reversed <- length(y0) < length(x0)
if (use.shortest) is.reversed <- !is.reversed
if (is.reversed) {
y <- sort(x0)
n <- length(y)
x <- quantile(y0, prob=(1:n-1)/(n-1))
} else {
y <- sort(y0)
n <- length(y)
x <- quantile(x0, prob=(1:n-1)/(n-1))
}
#
# Convert the relative threshold t.y into an absolute.
#
thresh <- t.y * diff(range(y))
#
# Recursively obtain points on the QQ plot.
#
xy <- qq.int(x, y, 1, n)
if (is.reversed) cbind(x=xy[,2], y=xy[,1], i=xy[,3]) else xy
}
Als Beispiel verwende ich Daten, die wie in meiner früheren Antwort simuliert wurden (mit einem extrem hohen Ausreißer, der in dieser Zeit stark y
verschmutzt ist x
):
set.seed(17)
n.x <- 1.21 * 10^6
n.y <- 1.20 * 10^6
k <- floor(0.01*n.x)
x <- c(rnorm(n.x-k), rnorm(k, mean=2, sd=2))
x <- x[x <= -3 | x >= -2.5]
y <- c(rbeta(n.y, 10,13), 1)
Zeichnen wir mehrere Versionen mit immer kleineren Schwellenwerten. Bei einem Wert von .0005 und einer Bildschirmdiagonale von 1000 Pixeln würde ein Fehler von höchstens einem halben vertikalen Pixel überall auf dem Plot garantiert . Dies ist grau dargestellt (nur 522 Punkte, verbunden durch Liniensegmente); Darüber werden die gröberen Näherungen aufgetragen: zuerst in Schwarz, dann in Rot (die roten Punkte sind eine Teilmenge der schwarzen und überzeichnen sie), dann in Blau (die wiederum eine Teilmenge und eine Überzeichnung sind). Die Timings reichen von 6,5 (blau) bis 10 Sekunden (grau). Da sie so gut skalieren, kann man genauso gut ungefähr ein halbes Pixel als universellen Standardwert für den Schwellenwert verwenden ( z. B. 1/2000 für einen 1000 Pixel hohen Monitor) und damit fertig werden.
qq.1 <- qq(x,y)
plot(qq.1, type="l", lwd=1, col="Gray",
xlab="x", ylab="y", main="Adaptive QQ Plot")
points(qq.1, pch=".", cex=6, col="Gray")
points(qq(x,y, .01), pch=23, col="Black")
points(qq(x,y, .03), pch=22, col="Red")
points(qq(x,y, .1), pch=19, col="Blue")
Bearbeiten
Ich habe den ursprünglichen Code geändert qq
, um eine dritte Spalte von Indizes in die längste (oder kürzeste, wie angegeben) der ursprünglichen zwei Arrays x
und y
entsprechend den ausgewählten Punkten zurückzugeben. Diese Indizes verweisen auf "interessante" Werte der Daten und könnten daher für die weitere Analyse nützlich sein.
Ich habe auch einen Fehler behoben, der bei wiederholten Werten von x
( beta
undefiniert) auftrat.
approx()
Funktion in der Funktion ins Spielqqplot()
.