数百万のポイントが存在する場合にデータをより効率的にプロットする統計的方法?


31

Rは、数百万のポイントが存在する場合にプロットを生成するのに時間がかかることがあります-ポイントが個別にプロットされることを考えると、当然です。さらに、そのようなプロットは散らかりすぎて有用ではないことが多い。多くのポイントが重なり合って黒いマスを形成し、そのマスに多くのポイントをプロットするのに多くの時間が費やされます。

標準の散布図で大きなデータを表す統計的な代替手段はありますか?密度プロットを検討しましたが、他の選択肢はありますか?n


1
線形プロットを使用した一部のソリューションについては、stats.stackexchange.com / questions / 35220 /…を参照してください。
whuber

回答:


13

これは、準備ができていない難しい作業です(もちろん、密度プロットは誰も気にしないより魅力的なフォールバックであるためです)。だから、あなたは何ができますか?

それらが本当に重なり合っている場合(つまり、XとY座標がまったく同じである場合)、アルファを使用していない場合、最良のアイデアは、 unique(アルファすると、そのようなグループで合計される可能性があります)です。

そうでない場合は、手動で座標を最も近いピクセルに丸めて、前の方法を使用できます(ただし、これはダーティソリューションです)。

最後に、最も密な領域のポイントをサブサンプリングするためにのみ使用する密度プロットを作成できます。一方、これは正確に同じプロットを作成せず、正確に調整しないとアーティファクトが発生する可能性があります。


5
オーバーラップをunique丸めることにより、または丸めることにより、偏った(虚偽の)プロットになる可能性があります。明るさやヒマワリのプロットなどのグラフィカルな手段を通じて、何らかの方法でオーバーラップの量を示すことが重要です。
whuber

44

Dan Carrによる論文/方法を実装するhexbinパッケージを見てください。PDFのビネットは、私は以下の引用の詳細があります。

1。概要

六角形のビニングは、nが大きいデータセットの構造を視覚化するのに役立つ2変量ヒストグラムの形式です。六角形ビニングの基本概念は非常に単純です。

  1. セット(range(x)、range(y))のxy平面は、六角形の規則的なグリッドによってテッセレーションされます。
  2. 各六角形に含まれるポイントの数がカウントされ、データ構造に格納されます
  3. n106

グリッドのサイズとカラーランプのカットが巧妙に選択されている場合、データに固有の構造がビンプロットに現れるはずです。ヒストグラムに適用するのと同じ注意が六角形のビニングに適用され、ビニングパラメーターの選択には注意が必要です。


4
それはいいものです。医者が注文したもの。
ローマンルシュトリック

13
(+1)また、興味がsmoothScatter {RColorBrewer}ありdensCols {grDevices}ます。遺伝データから数千から数百万のポイントでうまく機能することを確認できます。
chl

2
3Dデータがある場合はどうなりますか?(scatterplot3dには多すぎます)
skan

他の人の時間をいくらか節約するために-2つのコメントが提案されているように、smoothScatterが見つかりました。
チャーリー

16

私はあなたの最後の段落を完全に理解していないことを認めなければなりません:

「密度プロットを探しているわけではありませんが(多くの場合有用ですが)、単純なプロット呼び出しと同じ出力を望みますが、可能であれば数百万のオーバープロットよりもはるかに高速です。」

また、どのタイプのプロット(関数)を探しているのかも不明です。

メトリック変数がある場合、六角ビンプロットまたはひまわりプロットが役立つ場合があります。詳細については、を参照してください


6

質問に対する別の直接的な答えは、OpenGLを使用して数百万のポイントをプロットできるrglパッケージです。また、ポイントサイズ(3など)を指定してズームアウトしてこれらの重心をモノリシックブロックとして表示するか、ズームインしてモノリシックであったものの構造を確認します。ポイントサイズは一定ですが、画面上の距離は一定です。ズームに依存します。アルファレベルも使用できます。


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人が良いデータをカテゴリに翻訳することで人々に叫んでいるという悪い思い出です(もちろん、今では同意します笑)。とにかく、散布図について話しているなら、私は同じ問題を抱えています。今、数値データがある場合、分析のためにそれを分類することはあまり意味がありません。しかし、視覚化は別の話です。私にとって最も効果的であることがわかったのは、最初に(1)カット関数を使用して独立変数をグループに分割することです。グループの数をいろいろ試して、(2)IVのカットバージョンに対してDVをプロットするだけです。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)

(デザインをご容赦ください):

scatterSmoothの例

それは常に利用可能であり、膨大なデータセットでうまく機能するので、少なくともあなたが持っているものを見てみるのは良いことです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.