Metode statistik untuk memplot data dengan lebih efisien ketika jutaan titik hadir?

31

Saya menemukan R dapat membutuhkan waktu lama untuk menghasilkan plot ketika jutaan titik hadir - tidak mengherankan mengingat bahwa poin diplot secara individual. Selain itu, plot seperti itu seringkali terlalu berantakan dan padat untuk menjadi berguna. Banyak poin yang tumpang tindih dan membentuk massa hitam dan banyak waktu dihabiskan untuk merencanakan lebih banyak poin ke dalam massa tersebut.

Apakah ada alternatif statistik untuk merepresentasikan data besar dalam sebaran standar? Saya telah mempertimbangkan plot kepadatan, tetapi apa alternatif lain yang ada?n

Alex Stoddard
sumber
1
Untuk beberapa solusi dengan plot linear, lihat stats.stackexchange.com/questions/35220/… .
whuber

Jawaban:

13

Ini adalah tugas yang sulit tanpa solusi yang siap (ini tentu saja karena plot kepadatan adalah mundur yang menggoda daripada tidak ada yang benar-benar peduli). Jadi, apa yang bisa kamu lakukan?

Jika mereka benar-benar tumpang tindih (yaitu memiliki koordinat X & Y yang persis sama) dan Anda tidak menggunakan alpha, ide terbaik adalah hanya dengan mengurangi tumpang tindih menggunakan unique(dengan alpha, itu dapat diringkas atas kelompok-kelompok tersebut).

Jika tidak, Anda dapat membulatkan koordinat secara manual ke piksel terdekat dan menggunakan metode sebelumnya (namun ini adalah solusi kotor).

Terakhir, Anda dapat membuat plot kerapatan hanya untuk menggunakannya untuk mensampel titik-titik di area yang paling padat. Ini di sisi lain tidak akan membuat plot yang sama persis dan dapat memperkenalkan artefak jika tidak disetel secara tepat.


sumber
5
Mengurangi tumpang tindih dengan uniqueatau dengan pembulatan dapat menghasilkan plot yang bias (menipu). Sangat penting untuk menunjukkan jumlah tumpang tindih melalui beberapa cara grafis seperti cahaya atau dengan plot bunga matahari.
whuber
44

Lihatlah paket hexbin yang mengimplementasikan kertas / metode oleh Dan Carr. The pdf sketsa memiliki rincian lebih lanjut yang saya kutip di bawah ini:

1 Ikhtisar

Hexagon binning adalah bentuk histogram bivariat yang berguna untuk memvisualisasikan struktur dalam dataset dengan n besar. Konsep yang mendasari hexagon binning sangat sederhana;

  1. bidang xy di atas set (rentang (x), rentang (y)) di-tellell oleh kisi-kisi heksagon biasa.
  2. jumlah titik yang jatuh di setiap segi enam dihitung dan disimpan dalam struktur data
  3. n106

Jika ukuran kisi-kisi dan potongan-potongan di jalur warna dipilih dengan cara yang cerdas daripada struktur yang melekat dalam data harus muncul di plot binned. Peringatan yang sama berlaku untuk heksagon binning sebagaimana diterapkan pada histogram dan perawatan harus dilakukan dalam memilih parameter binning

Dirk Eddelbuettel
sumber
4
Itu bagus. Seperti yang diperintahkan dokter.
Roman Luštrik
13
(+1) Juga menarik, smoothScatter {RColorBrewer}dan densCols {grDevices}. Saya dapat mengkonfirmasi itu bekerja dengan cukup baik dengan ribuan hingga jutaan poin dari data genetik.
chl
2
Bagaimana jika memiliki data 3D? (terlalu banyak untuk scatterplot3d)
skan
Untuk menghemat waktu orang lain - saya menemukan smoothScatter, seperti yang disarankan 2 komentar, memiliki default / berfungsi jauh lebih baik.
Charlie
16

Saya harus mengakui bahwa saya tidak sepenuhnya memahami paragraf terakhir Anda:

"Saya tidak mencari plot densitas (walaupun itu sering berguna), saya ingin output yang sama dengan panggilan plot sederhana tetapi jauh lebih cepat daripada jutaan plot berlebih jika memungkinkan."

Juga tidak jelas apa jenis plot (fungsi) yang Anda cari.

Karena Anda memiliki variabel metrik, Anda mungkin menemukan plot binagon segi enam atau plot sunnflower berguna. Untuk referensi lebih lanjut, lihat

Bernd Weiss
sumber
6

Jawaban langsung lain untuk pertanyaan ini adalah paket rgl, yang dapat memplot jutaan poin menggunakan OpenGL. Juga, tentukan ukuran titik (mis. 3) dan perkecil untuk melihat pusat massa ini sebagai blok monolitik, atau perbesar dan lihat struktur yang dulunya monolitik - ukuran titik konstan tetapi jarak di antara mereka di layar tergantung pada pembesaran. Tingkat alfa juga dapat digunakan.

Robi5
sumber
5

Ini file yang saya panggil bigplotfix.R. Jika Anda sumbernya, itu akan menentukan pembungkus plot.xyyang "kompres" data plot ketika itu sangat besar. Wrapper tidak melakukan apa-apa jika inputnya kecil, tetapi jika inputnya besar maka ia memecahnya menjadi potongan-potongan dan hanya memplot nilai x dan y maksimum dan minimum untuk setiap potongan. Sumberbigplotfix.R juga rebinds graphics::plot.xyuntuk menunjuk ke pembungkus (sumber berkali-kali OK).

Perhatikan bahwa plot.xyfungsi "pekerja keras" untuk metode merencanakan standar seperti plot(),lines() , dan points(). Dengan demikian Anda dapat terus menggunakan fungsi-fungsi ini dalam kode Anda tanpa modifikasi, dan plot besar Anda akan secara otomatis dikompresi.

Ini adalah beberapa contoh keluaran. Ini pada dasarnya plot(runif(1e5)), dengan titik dan garis, dan dengan dan tanpa "kompresi" diimplementasikan di sini. Plot "titik terkompresi" merindukan wilayah tengah karena sifat kompresi, tetapi plot "garis terkompresi" terlihat lebih dekat dengan sumber asli yang tidak terkompresi. Waktu untuk png()perangkat; untuk beberapa alasan poin di pngperangkat jauh lebih cepat daripada di X11perangkat, tetapi kecepatan-up X11sebanding ( X11(type="cairo")lebih lambat dari X11(type="Xlib")pada percobaan saya).

"tes pengujian bigplotfix.R"

Alasan saya menulis ini adalah karena saya lelah menjalankan plot()secara tidak sengaja pada dataset besar (misalnya file WAV). Dalam kasus seperti itu saya harus memilih antara menunggu beberapa menit agar plot selesai, dan mengakhiri sesi R saya dengan sinyal (sehingga kehilangan sejarah perintah dan variabel saya baru-baru ini). Sekarang jika saya ingat untuk memuat file ini sebelum setiap sesi, saya benar-benar bisa mendapatkan plot yang berguna dalam kasus ini. Sebuah pesan peringatan kecil menunjukkan kapan data plot telah "dikompres".

# 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);
Metamorf
sumber
0

Mungkin saya akan dijauhi metode saya, kenangan buruk dari salah satu profesor riset saya berteriak pada orang-orang karena membuang data yang baik dengan menerjemahkannya ke dalam kategori (tentu saja, saya setuju sekarang menjadi hari lol), tidak tahu. Lagi pula, jika Anda berbicara tentang sebar, maka saya sudah memiliki masalah yang sama. Sekarang, ketika saya memiliki data numerik, tidak masuk akal bagi saya untuk mengkategorikannya untuk dianalisis. Tetapi memvisualisasikan adalah cerita yang berbeda. Apa yang saya temukan yang paling berhasil bagi saya adalah pertama-tama (1) memecah variabel independen Anda menjadi grup menggunakan fungsi cut. Anda dapat bermain-main dengan jumlah grup, dan kemudian (2) cukup memplot DV terhadap versi cut IV. R akan menghasilkan plot kotak alih-alih plot sebaran menjijikkan itu. Saya merekomendasikan menghapus outlier dari plot (gunakan opsi outline = FALSE dalam perintah plot). Sekali lagi, saya TIDAK AKAN PERNAH menyia-nyiakan data numerik yang sangat baik dengan mengategorikan dan kemudian menganalisis. Terlalu banyak masalah melakukan itu. Meskipun saya tahu itu adalah topik perdebatan yang sensitif. Tetapi melakukan itu secara khusus untuk tujuan setidaknya membuat beberapa pengertian visual dari data, tidak banyak kerugian yang saya lihat dari itu. Saya telah memplot data sebesar 10M dan masih berhasil memahaminya dari metode ini. Semoga itu bisa membantu! Salam Hormat! Saya sudah melihatnya. Saya telah memplot data sebesar 10M dan masih berhasil memahaminya dari metode ini. Semoga itu bisa membantu! Salam Hormat! Saya sudah melihatnya. Saya telah memplot data sebesar 10M dan masih berhasil memahaminya dari metode ini. Semoga itu bisa membantu! Salam Hormat!

Mgarvey
sumber
0

Untuk seri waktu yang besar, saya telah tumbuh untuk menyukai smoothScatter (bagian dari basis R tidak kurang). Saya sering harus memasukkan beberapa data tambahan, dan menjaga plot API dasar sangat membantu, misalnya:

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)

Yang memberi Anda (jika Anda mengampuni desain):

contoh scatterSmooth

Itu selalu tersedia dan berfungsi dengan baik dengan kumpulan data yang sangat besar, jadi senang setidaknya melihat apa yang Anda miliki.

Josh Rumbut
sumber