Statistische Methoden zur effizienteren Darstellung von Daten, wenn Millionen von Punkten vorhanden sind?


31

Ich finde, R kann lange dauern, um Diagramme zu erstellen, wenn Millionen von Punkten vorhanden sind - nicht überraschend, da die Punkte einzeln dargestellt werden. Darüber hinaus sind solche Diagramme häufig zu unübersichtlich und dicht, um nützlich zu sein. Viele der Punkte überlappen sich und bilden eine schwarze Masse, und es wird viel Zeit darauf verwendet, mehr Punkte in diese Masse zu zeichnen.

Gibt es statistische Alternativen zur Darstellung von großen Daten in einem Standardstreudiagramm? Ich habe über eine Dichtekurve nachgedacht, aber welche anderen Alternativen gibt es?n


1
Einige Lösungen mit linearen Diagrammen finden Sie unter stats.stackexchange.com/questions/35220/… .
Whuber

Antworten:


13

Dies ist eine schwierige Aufgabe ohne fertige Lösungen (dies ist natürlich deshalb so verlockend, weil die Dichtekurve so verlockend ist, als es niemanden wirklich interessiert). Also was kannst du tun?

Wenn sie sich wirklich überlappen (dh genau die gleichen X- und Y-Koordinaten haben) und Sie kein Alpha verwenden, ist es die beste Idee, die Überlappung mit zu verringern unique(bei Alpha kann sie über solche Gruppen summiert werden).

Andernfalls können Sie die Koordinaten manuell auf die nächsten Pixel runden und die vorherige Methode verwenden (dies ist jedoch eine fehlerhafte Lösung).

Schließlich können Sie ein Dichtediagramm erstellen, um nur die Punkte in den dichtesten Bereichen zu untertasten. Dies führt andererseits nicht zu genau derselben Darstellung und kann zu Artefakten führen, wenn sie nicht genau abgestimmt sind.


5
Das Reduzieren der Überlappung mit uniqueoder durch Runden kann zu verzerrten (trügerischen) Darstellungen führen. Es ist wichtig, das Ausmaß der Überlappung mithilfe von grafischen Mitteln wie Helligkeit oder Sonnenblumenparzellen anzuzeigen.
whuber

44

Schauen Sie sich das Hexbin- Paket an, in dem paper / method von Dan Carr implementiert ist. Die pdf-Vignette enthält weitere Details, die ich unten zitiere:

1. Übersicht

Hexagon-Binning ist eine Form des bivariaten Histogramms, mit dem die Struktur in Datensätzen mit großem n dargestellt werden kann. Das zugrunde liegende Konzept des Binning von Sechsecken ist äußerst einfach.

  1. Die xy-Ebene über der Menge (Bereich (x), Bereich (y)) ist durch ein regelmäßiges Gitter von Sechsecken tesselliert.
  2. Die Anzahl der in jedes Sechseck fallenden Punkte wird gezählt und in einer Datenstruktur gespeichert
  3. Die Sechsecke mit einer Zählung> 0 werden mithilfe einer Farbrampe oder durch Variieren des Radius des Sechsecks im Verhältnis zu den Zählungen gezeichnet. Der zugrunde liegende Algorithmus ist extrem schnell und effektiv, um die Struktur von Datensätzen mit anzuzeigenn106

Wenn die Größe des Rasters und die Schnitte in der Farbrampe auf geschickte Weise ausgewählt werden, sollte die den Daten inhärente Struktur in den gruppierten Diagrammen auftreten. Für das Binning von Sechsecken gelten die gleichen Vorsichtsmaßnahmen wie für Histogramme. Bei der Auswahl der Binning-Parameter ist Vorsicht geboten


4
Das ist schön. Genau das, was der Arzt bestellt hat.
Roman Luštrik

13
(+1) Auch von Interesse, smoothScatter {RColorBrewer}und densCols {grDevices}. Ich kann bestätigen, dass es mit Tausenden bis Millionen von Punkten aus genetischen Daten ziemlich gut funktioniert.
chl

2
Was ist, wenn 3D-Daten vorliegen? (zu viele für scatterplot3d)
skan

Um anderen Zeit zu sparen, habe ich smoothScatter, wie in 2 Kommentaren vorgeschlagen, mit viel besseren Standardeinstellungen / Funktionen gefunden.
Charlie

16

Ich muss zugeben, dass ich Ihren letzten Absatz nicht ganz verstehe:

"Ich bin nicht auf der Suche nach einem Dichtediagramm (obwohl diese häufig nützlich sind). Ich möchte die gleiche Ausgabe wie ein einfacher Diagrammaufruf, aber wenn möglich viel schneller als Millionen von Überdrucken."

Es ist auch unklar, nach welcher Art von Grundstück (Funktion) Sie suchen.

Angesichts der Tatsache, dass Sie metrische Variablen haben, könnten Sie Sechseck-Diagramme oder Sonnenblumen-Diagramme nützlich finden. Weitere Referenzen finden Sie unter


6

Eine weitere direkte Antwort auf diese Frage ist das rgl-Paket, das mit OpenGL Millionen von Punkten darstellen kann. Geben Sie außerdem eine Punktgröße an (z. B. 3) und zoomen Sie heraus, um diese Massenschwerpunkte als monolithische Blöcke anzuzeigen, oder zoomen Sie hinein und sehen Sie die Struktur der früheren monolithischen Blöcke - die Punktgrößen sind konstant, aber die Abstände zwischen ihnen auf dem Bildschirm hängen vom zoomen ab. Alpha-Level können ebenfalls verwendet werden.


5

Hier ist eine Datei, die ich anrufe bigplotfix.R. Wenn Sie es als Quelle angeben, wird ein Wrapper definiert, für den plot.xydie Plotdaten "komprimiert" werden, wenn sie sehr groß sind. Der Wrapper macht nichts, wenn die Eingabe klein ist, aber wenn die Eingabe groß ist, dann zerlegt er sie in Blöcke und zeichnet nur den maximalen und minimalen x- und y-Wert für jeden Block. Die Beschaffung wird bigplotfix.Rauch erneut gebunden graphics::plot.xy, um auf den Wrapper zu verweisen (die mehrfache Beschaffung ist in Ordnung).

Beachten Sie, dass plot.xydie „Arbeitspferd“ -Funktion für die Standard - Plotten Methoden wie ist plot(), lines()und points(). So können Sie diese Funktionen in Ihrem Code unverändert weiterverwenden, und Ihre großen Zeichnungen werden automatisch komprimiert.

Dies ist eine Beispielausgabe. Es ist im Wesentlichen plot(runif(1e5))mit Punkten und Linien und mit und ohne die hier implementierte "Komprimierung". In der Darstellung "Komprimierte Punkte" fehlt der mittlere Bereich aufgrund der Art der Komprimierung, aber die Darstellung "Komprimierte Linien" ähnelt dem unkomprimierten Original viel eher. Die Zeiten gelten für das png()Gerät; Aus irgendeinem Grund sind die Punkte im pngGerät viel schneller als im X11Gerät, aber die Beschleunigungen X11sind vergleichbar ( X11(type="cairo")waren langsamer als X11(type="Xlib")in meinen Experimenten).

"bigplotfix.R" Testausgabe

Der Grund, warum ich das geschrieben habe, ist, dass ich es leid war, plot()versehentlich auf einem großen Datensatz (z. B. einer WAV-Datei) zu rennen . In solchen Fällen müsste ich wählen, ob ich mehrere Minuten warten möchte, bis der Plot beendet ist, oder ob ich meine R-Sitzung mit einem Signal beenden möchte (wodurch meine aktuelle Befehlsgeschichte und Variablen verloren gehen). Wenn ich jetzt daran denke, diese Datei vor jeder Sitzung zu laden, kann ich in diesen Fällen tatsächlich einen nützlichen Plot erhalten. Eine kleine Warnmeldung zeigt an, wann die Plotdaten "komprimiert" wurden.

# 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

Vielleicht werde ich für meine Methode gemieden, schlechte Erinnerungen an einen meiner Forschungsprofis, die Leute anschreien, weil sie gute Daten weggeworfen haben, indem sie sie in Kategorien übersetzt haben (natürlich stimme ich jetzt ein paar Tage zu lol), wissen es nicht. Egal, wenn Sie von einem Streudiagramm sprechen, dann hatte ich die gleichen Probleme. Wenn ich numerische Daten habe, ist es für mich nicht sinnvoll, sie für die Analyse zu kategorisieren. Visualisieren ist eine andere Geschichte. Ich habe festgestellt, dass es für mich am besten ist, zuerst (1) Ihre unabhängige Variable mithilfe der Ausschneidefunktion in Gruppen aufzuteilen. Sie können mit der Anzahl der Gruppen herumspielen und dann (2) einfach den DV gegen die Schnittversion des IV plotten. R erzeugt Boxplots anstelle dieses widerlichen Scatterplots. Ich empfehle, die Ausreißer aus dem Plot zu entfernen (verwenden Sie die Option outline = FALSE im Befehl plot). Auch hier würde ich NIEMALS perfekte numerische Daten durch Kategorisieren und anschließendes Analysieren verschwenden. Zu viele Probleme dabei. Obwohl ich weiß, dass dies ein heikles Thema ist. Aber dies speziell für das Ziel, zumindest einen visuellen Sinn aus den Daten zu ziehen, nicht viel Schaden, den ich daraus gesehen habe. Ich habe Daten bis zu einer Größe von 10 MB aufgezeichnet und es dennoch geschafft, mit dieser Methode einen Sinn daraus zu machen. Hoffentlich hilft das! Freundliche Grüße! habe es gesehen. Ich habe Daten bis zu einer Größe von 10 MB aufgezeichnet und es dennoch geschafft, mit dieser Methode einen Sinn daraus zu machen. Hoffentlich hilft das! Freundliche Grüße! habe es gesehen. Ich habe Daten bis zu einer Größe von 10 MB aufgezeichnet und es dennoch geschafft, mit dieser Methode einen Sinn daraus zu machen. Hoffentlich hilft das! Freundliche Grüße!


0

Für große Zeitreihen habe ich mich für smoothScatter (nicht weniger Teil der Basis R) entschieden. Ich muss oft einige zusätzliche Daten angeben, und das Beibehalten der grundlegenden Plot-API ist sehr hilfreich, zum Beispiel:

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)

Was gibt Ihnen (wenn Sie das Design verzeihen):

scatterSmooth Beispiel

Es ist immer verfügbar und funktioniert gut mit riesigen Datenmengen. Es ist also nett, zumindest einen Blick auf das zu werfen, was Sie haben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.