Видалення сторонніх точок поблизу центру QQ-ділянки


14

Я намагаюся побудувати QQ-графік з двома наборами даних приблизно 1,2 мільйона пунктів, в R (використовуючи qqplot та вводячи дані в ggplot2). Розрахунок досить простий, але отриманий графік завантажується болісно повільно, оскільки там так багато очок. Я намагався лінійного наближення зменшити кількість точок до 10000 (це все одно, що робить функція qqplot, якщо один із ваших наборів даних більший за інший), але тоді ви втрачаєте багато деталей у хвостах.

Більшість точок даних до центру в основному марні - вони накладаються на стільки, що, ймовірно, приблизно 100 на піксель. Чи є якийсь простий спосіб видалення даних, що знаходяться занадто близько один до одного, не втрачаючи більш рідкісні дані до хвостів?


Я мав би сказати, я фактично порівнюю один набір даних (кліматичні спостереження) з ансамблем порівнянних наборів даних (модель працює). Таким чином, я фактично порівнюю 1,2 млн. Очок, з 87-мільйонними модельними балами, отже, approx()функція вступає в гру qqplot().
naught101

Відповіді:


12

Сюжети QQ неймовірно автокорельовані, за винятком хвостів. Переглядаючи їх, акцентується увага на загальній формі сюжету та на поведінці хвоста. Ерго , ви добряче зробите, грубо підсилюючись в центрах розподілів і включивши достатню кількість хвостів.

Ось код, що ілюструє вибірку в цілому наборі даних, а також як приймати крайні значення.

quant.subsample <- function(y, m=100, e=1) {
  # m: size of a systematic sample
  # e: number of extreme values at either end to use
  x <- sort(y)
  n <- length(x)
  quants <- (1 + sin(1:m / (m+1) * pi - pi/2))/2
  sort(c(x[1:e], quantile(x, probs=quants), x[(n+1-e):n]))
  # Returns m + 2*e sorted values from the EDF of y
}

Для ілюстрації цей модельований набір даних показує структурну різницю між двома наборами даних приблизно в 1,2 мільйона значень, а також дуже невелику кількість «забруднення» в одному з них. Крім того, щоб зробити цей тест суворим, інтервал значень повністю виключається з одного з наборів даних: графік QQ повинен показувати перерву для цих значень.

set.seed(17)
n.x <- 1.21 * 10^6
n.y <- 1.20 * 10^6
k <- floor(0.0001*n.x)
x <- c(rnorm(n.x-k), rnorm(k, mean=2, sd=2))
x <- x[x <= -3 | x >= -2.5]
y <- rbeta(n.y, 10,13)

Ми можемо підпробовувати 0,1% кожного набору даних і включати ще 0,1% їх крайностей, що дає 2420 балів для побудови. Загальний час, що минув менше 0,5 секунд:

m <- .001 * max(n.x, n.y)
e <- floor(0.0005 * max(n.x, n.y))

system.time(
  plot(quant.subsample(x, m, e), 
       quant.subsample(y, m, e), 
       pch=".", cex=4,
       xlab="x", ylab="y", main="QQ Plot")
  )

Немає інформації не втрачається взагалі:

QQ сюжет


Ви не повинні зливати свої відповіді?
Майкл Р. Черник

2
@Michael Так, зазвичай я б відредагував першу відповідь (теперішню). Але кожна відповідь довга, і вони використовують суттєво різні підходи, з різними характеристиками виконання, тому здавалося, що найкраще розмістити другу як окрему відповідь. Насправді, я спокусився видалити перше після другого (адаптивного), який стався зі мною, але його відносна швидкість може подобатися деяким людям, тому було б несправедливо видалити його взагалі.
whuber

Це в основному те, що я хотів, але яка обґрунтування використання sin? Я правий, що нормальний CDF був би кращою функцією, якщо ви припускали, що х нормально розподілений? Ви просто вибрали гріх, тому що це простіше обчислити?
naught101

Це повинні бути ті самі дані, що і ваша інша відповідь? Якщо так, то чому сюжети такі різні? що сталося з усіма даними для x> 6?
naught101

(3-2х)х2

11

Ще в цій темі я запропонував просте, але дещо спеціальне рішення про підгрупування точок. Це швидко, але вимагає певних експериментів, щоб створити чудові сюжети. Рішення, яке планується описати, - на порядок повільніше (займає до 10 секунд за 1,2 мільйона балів), але є адаптивним та автоматичним. Для великих наборів даних це повинно дати хороші результати в перший раз і зробити це досить швидко.

Dн

(х,у)ту

Є кілька деталей, про які слід подбати, особливо, щоб впоратися з наборами даних різної довжини. Я роблю це, замінюючи коротший на квантові, що відповідають довшому: фактично використовується кусочно лінійне наближення ЕРФ більш короткого, а не його фактичні значення даних. ("Коротше" та "довше" можна змінити налаштування use.shortest=TRUE.)

Ось Rреалізація.

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
}

В якості прикладу я використовую дані, змодельовані як у попередній відповіді (із надзвичайно високою формою, яка закинута yв 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)

Давайте побудуємо кілька версій, використовуючи все менші та менші значення порогу. При значенні .0005 і відображенні на моніторі висотою 1000 пікселів, ми б гарантували помилку не більше половини вертикального пікселя скрізь на ділянці. Це показано сірим кольором (лише 522 пункти, з'єднані відрізками рядків); Більш грубі наближення накреслюються поверх нього: спочатку чорним, потім червоним (червоні точки будуть підмножиною чорних та перегрівають їх), потім синім (що знову є підмножиною та перегрівом). Час дії від 6,5 (синій) до 10 секунд (сірий). Зважаючи на те, що вони масштабують так добре, можна також добре використовувати близько половини пікселів як універсальний за замовчуванням поріг ( наприклад , 1/2000 для високого монітора 1000 пікселів) і робити це з ним.

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 сюжет

Редагувати

Я змінив оригінальний код для qqповернення третього стовпця індексів у найдовший (або найкоротший, як зазначено) початкових двох масивів, xі y, відповідно до вибраних точок. Ці показники вказують на "цікаві" значення даних і тому можуть бути корисними для подальшого аналізу.

Я також видалив помилку, що виникає з повторними значеннями x(які стали betaневизначеними).


Як обчислити qqаргументи для заданого вектора? Також, чи можете ви порадити використання qqфункції з ggplot2пакетом? Я думав про використання ggplot2' stat_functionдля цього.
Олександр Блех

10

Видалення деяких точок даних посередині змінило б емпіричний розподіл і, отже, qqplot. Сказавши це, ви можете зробити наступне і безпосередньо побудувати кванти емпіричного розподілу порівняно з квантовами теоретичного розподілу:

x <- rnorm(1200000)
mean.x <- mean(x)
sd.x <- sd(x)
quantiles.x <- quantile(x, probs = seq(0,1,b=0.000001))
quantiles.empirical <- qnorm(seq(0,1,by=0.000001),mean.x,sd.x)
plot(quantiles.x~quantiles.empirical) 

Вам доведеться регулювати послідовність залежно від того, наскільки глибоко ви хочете потрапити в хвости. Якщо ви хочете бути розумними, ви також можете прояснити цю послідовність посередині, щоб пришвидшити сюжет. Наприклад, використовуючи

plogis(seq(-17,17,by=.1))

є можливість.


Вибачте, я не маю на увазі видалення точок із наборів даних, просто з графіків.
naught101

Навіть вилучати їх із сюжету - погана ідея. Але ви спробували зміни прозорості та / або випадкову вибірку з набору даних?
Пітер Флом - Відновити Моніку

2
У чому справа з видаленням зайвої фарби з точок, що перекриваються на ділянці, @Peter?
whuber

1

Можна зробити hexbinсюжет.

x <- rnorm(1200000)
mean.x <- mean(x)
sd.x <- sd(x)
quantiles.x <- quantile(x, probs = seq(0,1,b=0.000001))
quantiles.empirical <- qnorm(seq(0,1,by=0.000001),mean.x,sd.x)

library(hexbin)
bin <- hexbin(quantiles.empirical[-c(1,length(quantiles.empirical))],quantiles.x[-c(1,length(quantiles.x))],xbins=100)
plot(bin)

Я не знаю, чи реально це застосовано до qq-графічних даних (див. Також мій коментар до мого запитання, чому це не спрацює для мого конкретного випадку). Цікавий момент, хоча. Я можу побачити, чи можу я змусити його працювати над окремими моделями проти проти.
naught101

1

Інша альтернатива - паралельний короб; Ви сказали, що у вас є два набори даних, так що:

y <- rnorm(1200000)
x <- rnorm(1200000)
grpx <- cut(y,20)
boxplot(y~grpx)

і ви можете налаштувати різні варіанти, щоб покращити свої дані.


Я ніколи не був великим прихильником дискретизації постійних даних, але це цікава ідея.
naught101
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.