存在数百万个点时可以更有效地绘制数据的统计方法?


31

我发现当存在数百万个点时,R可能需要很长时间才能生成图-考虑到点是单独绘制的,这不足为奇。此外,这样的地块常常太杂乱,太密集而无法使用。许多点重叠并形成黑色块,并且花费大量时间在该块上绘制更多点。

在标准散点图中,有没有其他统计方法可以表示大数据?我考虑过密度图,但是还有哪些其他选择?ñ


1
有关线性图的一些解决方案,请参见stats.stackexchange.com/questions/35220/…
ub

Answers:


13

这是一项艰巨的任务,没有现成的解决方案(这当然是因为密度图是如此诱人的后备,而没人真正关心)。所以,你可以做什么?

如果它们确实重叠(即X和Y坐标完全相同)并且您没有使用Alpha,则最好的办法就是使用以下方法减少重叠 unique(使用alpha,可以将它们叠加在这样的组上)。

如果不是,您可以手动将坐标四舍五入到最接近的像素并使用以前的方法(但是这是一个肮脏的解决方案)。

最后,您仅可以使用密度图对最密集区域中的点进行二次采样。另一方面,这将不会产生完全相同的图,并且如果未精确调整,可能会引入伪影。


5
unique通过四舍五入或通过四舍五入减少重叠可能会导致图的偏差(欺骗性)。重要的是要通过某种图形方式(例如亮度或向日葵图)指示重叠的数量。
ub

44

看一下实现Dan Carr的纸张/方法的hexbin软件包。该PDF小品有更多的细节,我引述如下:

1概述

六边形合并是一种双变量直方图,可用于可视化具有大n的数据集中的结构。六边形合并的基本概念非常简单。

  1. 集合(range(x),range(y))上的xy平面由六边形的规则网格细分。
  2. 计算落在每个六边形中的点数并存储在数据结构中
  3. ñ106

如果以巧妙的方式选择网格的大小和色带上的切口,则数据的固有结构应出现在合并图中。六边形合并与直方图同样要注意,在选择合并参数时应格外小心


4
很好 正是医生命令的。
RomanLuštrik2011年

13
(+1)也很有趣,smoothScatter {RColorBrewer}densCols {grDevices}。我可以确认它与遗传数据中的千至百万个点非常有效。
chl

2
如果有3D数据怎么办?(对于
scatterplot3d

为了节省其他时间-我发现smoothScatter(建议2条注释)具有更好的默认设置/功能。
查理

16

我必须承认,我不完全理解你的最后一段:

“我不是在寻找密度图(尽管它们通常是有用的),但我希望获得与简单的图调用相同的输出,但是如果可能的话,它要比数百万的超图快得多。”

还不清楚您要查找哪种类型的绘图(函数)。

假定您具有度量变量,则可能会发现六角形合并图或向日葵图很有用。有关更多参考,请参见


6

这个问题的另一个直接答案是rgl软件包,它可以使用OpenGL绘制数百万个点。另外,指定一个点的大小(例如3),然后缩小以将这些质心看成是整体块,或者放大并查看过去是整体的结构-点的大小是恒定的,但是它们在屏幕上的距离取决于缩放。也可以使用Alpha水平。


5

这是我叫的文件bigplotfix.R。如果您提供它,它将定义一个包装,当包装plot.xy数据很大时,将为其“压缩” 包装数据。如果输入较小,则包装器不执行任何操作,但是如果输入较大,则包装器将其分成多个块,并仅绘制每个块的最大和最小x和y值。采购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将生成箱形图,而不是该令人讨厌的散点图。我确实建议从绘图中删除异常值(使用plot命令中的outline = FALSE选项)。同样,我永远也不会通过分类和分析来浪费完美的数值数据。这样做有太多问题。尽管我知道这是一个棘手的辩论主题。但这是专门为至少从数据中获得视觉效果的目的而进行的,对我而言,危害不大。我已经绘制了高达10M的数据,但仍然可以从这种方法中理解数据。希望有帮助!最好的祝福!我已经看到了。我已经绘制了高达10M的数据,但仍然可以从这种方法中理解数据。希望有帮助!最好的祝福!我已经看到了。我已经绘制了高达10M的数据,但仍然可以从这种方法中理解数据。希望有帮助!最好的祝福!


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)

给您(如果您原谅设计):

平滑示例

它始终可用,并且可以与庞大的数据集一起很好地工作,因此至少可以看看您所拥有的东西真是太好了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.