Статистические методы для более эффективного построения данных, когда присутствуют миллионы точек?

31

Я считаю, что R может занять много времени для создания графиков, когда присутствуют миллионы точек - неудивительно, учитывая, что точки строятся индивидуально. Кроме того, такие графики часто слишком загромождены и плотны, чтобы быть полезными. Многие из точек перекрываются и образуют черную массу, и много времени тратится на построение большего количества точек в этой массе.

Существуют ли статистические альтернативы представлению больших данных на стандартной диаграмме рассеяния? Я рассмотрел график плотности, но какие еще есть альтернативы?n

Алекс Стоддард
источник
1
Для некоторых решений с линейными графиками см. Stats.stackexchange.com/questions/35220/… .
whuber

Ответы:

13

Это трудная задача без готовых решений (это, конечно, потому что график плотности настолько заманчивый запасной вариант, чем никто на самом деле не заботится). Так что ты можешь сделать?

Если они действительно перекрываются (то есть имеют абсолютно одинаковые координаты X и Y), и вы не используете альфа, то лучшей идеей будет просто уменьшить использование перекрытия unique(с альфой оно может суммироваться по таким группам).

Если нет, вы можете вручную округлить координаты до ближайших пикселей и использовать предыдущий метод (но это грязное решение).

Наконец, вы можете сделать график плотности только для того, чтобы использовать его для отбора точек в наиболее плотных областях. Это, с другой стороны, не создаст точно такой же сюжет и может привести к появлению артефактов, если не будет точно настроено.


источник
5
Уменьшение перекрытия с uniqueили округлением может привести к смещенным (обманчивым) графикам. Важно как-то указать степень перекрытия с помощью некоторых графических средств, таких как легкость или графики подсолнечника.
whuber
44

Посмотрите на пакет hexbin , в котором реализована бумага / метод Дэна Карра. PDF виньетка имеет больше деталей , которые я привожу ниже:

1 Обзор

Шестигранный биннинг - это форма двумерной гистограммы, полезная для визуализации структуры в наборах данных с большим n. Основная концепция объединения шестиугольников чрезвычайно проста;

  1. плоскость xy над множеством (range (x), range (y)) тесселяется регулярной сеткой шестиугольников.
  2. количество точек, попадающих в каждый шестиугольник, подсчитывается и сохраняется в структуре данных
  3. n106

Если размер сетки и срезы в цветовой шкале выбираются хитроумно, то структура, присущая данным, должна появиться на бин-графиках. К шестиугольному бинингу применяются те же предостережения, что и к гистограммам, поэтому следует соблюдать осторожность при выборе параметров биннинга.

Дирк Эддельбюттель
источник
4
Это хорошо. Только то, что доктор прописал.
Роман Луштрик
13
(+1) Также интерес, smoothScatter {RColorBrewer}и densCols {grDevices}. Я могу подтвердить, что это работает довольно хорошо с тысячами до миллионов точек из генетических данных.
хл
2
Что делать, если есть 3D-данные? (слишком много для
scatterplot3d
Чтобы сэкономить время другим - я обнаружил, что smoothScatter, как было предложено в 2 комментариях, имеет гораздо лучшие значения по умолчанию / функционирование.
Чарли
16

Я должен признать, что я не полностью понимаю ваш последний абзац:

«Я не ищу график плотности (хотя он часто полезен), я хотел бы получить тот же результат, что и простой вызов графика, но гораздо быстрее, чем миллионы оверплотов, если это возможно».

Также неясно, какой тип сюжета (функции) вы ищете.

Учитывая, что у вас есть метрические переменные, вы можете найти графики с шестигранной или подсолнечника полезными. Для дальнейших ссылок см.

Бернд Вайс
источник
6

Другой прямой ответ на этот вопрос - пакет rgl, который может построить миллионы точек, используя OpenGL. Кроме того, укажите размер точки (например, 3) и уменьшите масштаб, чтобы увидеть эти центры масс как монолитные блоки, или увеличьте масштаб и посмотрите структуру того, что раньше было монолитным - размеры точек постоянны, но расстояния между ними на экране зависит от масштабирования. Альфа-уровни также могут быть использованы.

Robi5
источник
5

Вот файл, который я называю bigplotfix.R. Если вы его создадите, он определит оболочку, для plot.xyкоторой «сжимает» данные графика, когда они очень велики. Оболочка ничего не делает, если ввод маленький, но если он большой, он разбивает его на куски и просто отображает максимальное и минимальное значения x и y для каждого чанка. Sourcing bigplotfix.Rтакже повторяет привязку, graphics::plot.xyчтобы указать на оболочку (можно использовать несколько раз подряд).

Обратите внимание , что plot.xyфункция «рабочей лошадки» для стандартных методов ПРОКЛАДКИ , как plot(), lines()и points(). Таким образом, вы можете продолжать использовать эти функции в своем коде без изменений, и ваши большие графики будут автоматически сжаты.

Это пример выходных данных. По сути plot(runif(1e5)), с точками и линиями, с и без "сжатия", реализованного здесь. График «сжатые точки» не попадает в среднюю область из-за характера сжатия, но график «сжатые линии» выглядит намного ближе к несжатому оригиналу. Время для png()устройства; По некоторым причинам точки в устройстве гораздо быстрее png, чем в X11устройстве, но ускорения в нем X11сопоставимы ( X11(type="cairo")было медленнее, чем X11(type="Xlib")в моих экспериментах).

Тестовый вывод "bigplotfix.R"

Я написал это потому, что устал от plot()случайного запуска большого набора данных (например, файла WAV). В таких случаях мне пришлось бы выбирать между ожиданием нескольких минут до завершения графика и завершением сеанса R с сигналом (таким образом, теряя мою недавнюю историю команд и переменные). Теперь, если я не забуду загружать этот файл перед каждой сессией, я действительно смогу получить полезный график в этих случаях. Небольшое предупреждающее сообщение указывает, когда данные графика были «сжаты».

# bigplotfix.R
# 28 Nov 2016

# This file defines a wrapper for plot.xy which checks if the input
# data is longer than a certain maximum limit. If it is, it is
# downsampled before plotting. For 3 million input points, I got
# speed-ups of 10-100x. Note that if you want the output to look the
# same as the "uncompressed" version, you should be drawing lines,
# because the compression involves taking maximum and minimum values
# of blocks of points (try running test_bigplotfix() for a visual
# explanation). Also, no sorting is done on the input points, so
# things could get weird if they are out of order.
test_bigplotfix = function() {
  oldpar=par();
  par(mfrow=c(2,2))
  n=1e5;
  r=runif(n)
  bigplotfix_verbose<<-T
  mytitle=function(t,m) { title(main=sprintf("%s; elapsed=%0.4f s",m,t["elapsed"])) }
  mytime=function(m,e) { t=system.time(e); mytitle(t,m); }

  oldbigplotfix_maxlen = bigplotfix_maxlen
  bigplotfix_maxlen <<- 1e3;

  mytime("Compressed, points",plot(r));
  mytime("Compressed, lines",plot(r,type="l"));
  bigplotfix_maxlen <<- n
  mytime("Uncompressed, points",plot(r));
  mytime("Uncompressed, lines",plot(r,type="l"));
  par(oldpar);
  bigplotfix_maxlen <<- oldbigplotfix_maxlen
  bigplotfix_verbose <<- F
}

bigplotfix_verbose=F

downsample_xy = function(xy, n, xlog=F) {
  msg=if(bigplotfix_verbose) { message } else { function(...) { NULL } }
  msg("Finding range");
  r=range(xy$x);
  msg("Finding breaks");
  if(xlog) {
    breaks=exp(seq(from=log(r[1]),to=log(r[2]),length.out=n))
  } else {
    breaks=seq(from=r[1],to=r[2],length.out=n)
  }
  msg("Calling findInterval");
  ## cuts=cut(xy$x,breaks);
  # findInterval is much faster than cuts!
  cuts = findInterval(xy$x,breaks);
  if(0) {
    msg("In aggregate 1");
    dmax = aggregate(list(x=xy$x, y=xy$y), by=list(cuts=cuts), max)
    dmax$cuts = NULL;
    msg("In aggregate 2");
    dmin = aggregate(list(x=xy$x, y=xy$y), by=list(cuts=cuts), min)
    dmin$cuts = NULL;
  } else { # use data.table for MUCH faster aggregates
    # (see http://stackoverflow.com/questions/7722493/how-does-one-aggregate-and-summarize-data-quickly)
    suppressMessages(library(data.table))
    msg("In data.table");
    dt = data.table(x=xy$x,y=xy$y,cuts=cuts)
    msg("In data.table aggregate 1");
    dmax = dt[,list(x=max(x),y=max(y)),keyby="cuts"]
    dmax$cuts=NULL;
    msg("In data.table aggregate 2");
    dmin = dt[,list(x=min(x),y=min(y)),keyby="cuts"]
    dmin$cuts=NULL;
    #  ans = data_t[,list(A = sum(count), B = mean(count)), by = 'PID,Time,Site']
  }
  msg("In rep, rbind");
  # interleave rows (copied from a SO answer)
  s <- rep(1:n, each = 2) + (0:1) * n
  xy = rbind(dmin,dmax)[s,];
  xy
}

library(graphics);
# make sure we don't create infinite recursion if someone sources
# this file twice
if(!exists("old_plot.xy")) {
  old_plot.xy = graphics::plot.xy
}

bigplotfix_maxlen = 1e4

# formals copied from graphics::plot.xy
my_plot.xy = function(xy, type, pch = par("pch"), lty = par("lty"),
  col = par("col"), bg = NA, cex = 1, lwd = par("lwd"),
  ...) {

  if(bigplotfix_verbose) {
    message("In bigplotfix's plot.xy\n");
  }

  mycall=match.call();
  len=length(xy$x)
  if(len>bigplotfix_maxlen) {
    warning("bigplotfix.R (plot.xy): too many points (",len,"), compressing to ",bigplotfix_maxlen,"\n");
    xy = downsample_xy(xy, bigplotfix_maxlen, xlog=par("xlog"));
    mycall$xy=xy
  }
  mycall[[1]]=as.symbol("old_plot.xy");

  eval(mycall,envir=parent.frame());
}

# new binding solution adapted from Henrik Bengtsson
# https://stat.ethz.ch/pipermail/r-help/2008-August/171217.html
rebindPackageVar = function(pkg, name, new) {
  # assignInNamespace() no longer works here, thanks nannies
  ns=asNamespace(pkg)
  unlockBinding(name,ns)
  assign(name,new,envir=asNamespace(pkg),inherits=F)
  assign(name,new,envir=globalenv())
  lockBinding(name,ns)
}
rebindPackageVar("graphics", "plot.xy", my_plot.xy);
Метаморфические
источник
0

Возможно, меня откажут от моего метода, плохие воспоминания о том, как один из моих научных сотрудников кричал на людей за то, что они выбрасывают хорошие данные, переводя их в категории (конечно, сейчас я согласен, смеется), не знаю. Во всяком случае, если вы говорите о графике рассеяния, то у меня были те же проблемы. Теперь, когда у меня есть числовые данные, нет особого смысла классифицировать их для анализа. Но визуализация - это другая история. Что я обнаружил, что лучше всего работает для меня, это сначала (1) разбить вашу независимую переменную на группы, используя функцию cut. Вы можете поиграть с количеством групп, а затем (2) просто построить график DV против урезанной версии IV. R будет генерировать коробочные графики вместо этого отвратительного рассеивающего графика. Я рекомендую удалить выбросы из графика (используйте параметр outline = FALSE в команде plot). Опять же, я НИКОГДА не буду тратить совершенно хорошие числовые данные, классифицируя и затем анализируя. Слишком много проблем делает это. Хотя я знаю, что это тема для обсуждения. Но делаю это специально для того, чтобы хотя бы получить какой-то визуальный смысл из данных, не так много вреда, который я видел из этого. Я построил данные размером до 10M и все же сумел извлечь из этого смысл. Надеюсь, это поможет! С уважением! видел из этого. Я построил данные размером до 10M и все же сумел извлечь из этого смысл. Надеюсь, это поможет! С уважением! видел из этого. Я построил данные размером до 10M и все же сумел извлечь из этого смысл. Надеюсь, это поможет! С уважением!

Mgarvey
источник
0

Для больших временных рядов я полюбил smoothScatter (часть базы R не меньше). Мне часто приходится включать некоторые дополнительные данные, и сохранение базового API графика действительно полезно, например:

set.seed(1)
ra <- rnorm(n = 100000, sd = 1, mean = 0)
smoothScatter(ra)
abline(v=25000, col=2)
text(25000, 0, "Event 1", col=2)

Что дает вам (если вы простите дизайн):

пример scatterSmooth

Он всегда доступен и хорошо работает с огромными наборами данных, поэтому приятно хотя бы взглянуть на то, что у вас есть.

Джош Рамбут
источник