En otra parte de este hilo, he propuesto una simple pero algo ad hoc solución de submuestreo de los puntos. Es rápido, pero requiere un poco de experimentación para producir grandes parcelas. La solución a punto de ser descrito es de un orden de magnitud más lento (de hasta 10 segundos de 1,2 millones de puntos), pero es adaptable y automática. Para grandes conjuntos de datos, se debería dar buenos resultados en el primer tiempo y hacerlo con bastante rapidez.
La idea es que de la de Douglas-Peucker polilínea simplificación del algoritmo, adaptada a las características de un QQ plot. La estadística relevante para esa parcela es la prueba de Kolmogorov-Smirnov estadístico Dn, la máxima desviación vertical de una cocina equipada línea. En consecuencia, el algoritmo es este:
Encontrar el máximo de la desviación vertical entre la línea que une los extremos de la (x,y) pares y sus QQ plot. Si esto está dentro de un aceptable fracción t de la gama completa de y, reemplace la parcela con esta línea. De lo contrario, la partición de los datos en los anteriores el punto de máxima desviación vertical y los de después y de forma recursiva aplicar el algoritmo para las dos piezas.
Hay algunos detalles que cuidar, especialmente para hacer frente con conjuntos de datos de diferente longitud. Puedo hacer esto mediante la sustitución de la menor por los cuantiles correspondientes a más de uno: en efecto, una aproximación lineal por tramos de la FED de la menor se utiliza en lugar de sus valores reales de los datos. ("Corto" y "largo" puede ser revertido por la configuración de la use.shortest=TRUE
.)
Aquí está una R
de ejecución.
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
}
Como ejemplo puedo usar datos simulados como en mi anterior respuesta (con una alta extrema outlier lanzado en y
y un poco más de contaminación en x
este momento):
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)
Vamos parcela varias versiones, utilizando más y más pequeños los valores del umbral. En un valor de .0005 y mostrar en un monitor de 1000 píxeles de alto, estaríamos garantizando un error de no más de la mitad vertical de píxeles en todas partes en la parcela. Esto se muestra en gris (sólo 522 puntos, acompañado por segmentos de línea); la burdas aproximaciones se representan en la parte superior: la primera en negro, luego en color rojo (los puntos rojos será un subconjunto de los negros y overplot ellos), luego en azul (que de nuevo son un subconjunto y overplot). Los horarios rango de 6.5 (azul) a 10 segundos (en color gris). Dado que la escala tan bien, uno podría usar alrededor de la mitad de píxeles como un universal predeterminado para el umbral (por ejemplo, 1/2000 de 1000 píxeles de alto monitor) y hacer con ella.
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")
![QQ plot]()
Editar
He modificado el código original de qq
a devolver una tercera columna de los índices en la más larga (o más corto, según se especifica) de la original de dos matrices, x
y y
, correspondiente a los puntos que están seleccionados. Estos índices señalan a "interesantes" los valores de los datos y por lo tanto podrían ser útiles para su posterior análisis.
También he eliminado un bug que ocurre con la repetición de los valores de x
(lo que causó beta
a ser indefinido).