Em outras partes deste tópico, propus uma solução simples, mas um tanto ad hoc , de subamostragem dos pontos. É rápido, mas requer algumas experiências para produzir grandes parcelas. A solução a ser descrita é uma ordem de magnitude mais lenta (levando até 10 segundos para 1,2 milhão de pontos), mas é adaptável e automática. Para conjuntos de dados grandes, deve fornecer bons resultados na primeira vez e fazê-lo razoavelmente rapidamente.
Dn
( x , y)ty
Há alguns detalhes a serem resolvidos, especialmente para lidar com conjuntos de dados de diferentes comprimentos. Eu faço isso substituindo o menor pelos quantis correspondentes ao maior: com efeito, uma aproximação linear por partes do EDF do menor é usada em vez de seus valores reais de dados. ("Mais curto" e "mais longo" podem ser revertidos pela configuração use.shortest=TRUE.)
Aqui está uma Rimplementação.
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 exemplo, eu uso dados simulados, como na minha resposta anterior (com um outlier extremamente alto jogado ye um pouco mais de contaminação xnesse período):
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 plotar várias versões, usando valores cada vez menores do limite. Com um valor de .0005 e exibindo em um monitor com 1000 pixels de altura, estaríamos garantindo um erro não superior a metade de um pixel vertical em todo o gráfico. Isso é mostrado em cinza (apenas 522 pontos, unidos por segmentos de linha); as aproximações mais grosseiras são plotadas sobre ela: primeiro em preto, depois em vermelho (os pontos vermelhos serão um subconjunto dos pretos e os plotam em excesso), depois em azul (que novamente é um subconjunto e overplot). Os intervalos variam de 6,5 (azul) a 10 segundos (cinza). Dado que eles têm uma escala tão boa, pode-se usar da mesma maneira meio pixel como padrão universal para o limite ( por exemplo , 1/2000 para um monitor com 1000 pixels de altura) e terminar com ele.
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")

Editar
Modifiquei o código original para qqretornar uma terceira coluna de índices na mais longa (ou mais curta, conforme especificado) das duas matrizes originais xe y, correspondendo aos pontos selecionados. Esses índices apontam para valores "interessantes" dos dados e, portanto, podem ser úteis para análises adicionais.
Também removi um erro que ocorria com valores repetidos de x(que betaeram indefinidos).
approx()função entra em jogo naqqplot()função.