วิธีการทางสถิติเพื่อพล็อตข้อมูลได้อย่างมีประสิทธิภาพมากขึ้นเมื่อมีจุดหลายล้านจุด?


31

ฉันพบว่า R อาจใช้เวลานานในการสร้างแปลงเมื่อมีคะแนนนับล้าน - ไม่น่าแปลกใจเนื่องจากมีการลงจุดเป็นรายบุคคล นอกจากนี้แปลงดังกล่าวมักจะรกเกินไปและแน่นเกินไปที่จะเป็นประโยชน์ หลายจุดซ้อนทับกันและก่อตัวเป็นมวลดำและใช้เวลามากมายในการพล็อตจุดให้มากขึ้น

มีทางเลือกทางสถิติใด ๆ ในการแทนข้อมูลขนาดใหญ่ใน scatterplot มาตรฐานหรือไม่? ฉันได้พิจารณาโครงเรื่องความหนาแน่นแล้ว แต่มีทางเลือกอื่นอะไรอีกบ้างn


1
สำหรับการแก้ปัญหาบางอย่างกับแปลงเชิงเส้นดูstats.stackexchange.com/questions/35220/...
whuber

คำตอบ:


13

นี่เป็นงานที่หนักหนาสาหัสที่ไม่มีวิธีแก้ปัญหาที่พร้อม (นี่เป็นเพราะการวางแผนความหนาแน่นเป็นทางเลือกที่ดึงดูดกว่าที่ไม่มีใครใส่ใจจริงๆ) ดังนั้นคุณจะทำอย่างไร

หากพวกเขาทับซ้อนกันจริงๆ (เช่นมีพิกัด X & Y ที่เหมือนกันทุกประการ) และคุณไม่ได้ใช้อัลฟ่าความคิดที่ดีที่สุดคือการลดการทับซ้อนโดยใช้unique(ด้วยอัลฟ่าอาจรวมอยู่ในกลุ่มดังกล่าว)

หากไม่เป็นเช่นนั้นคุณสามารถปัดเศษพิกัดด้วยตนเองเป็นพิกเซลที่ใกล้ที่สุดและใช้วิธีการก่อนหน้า (แต่นี่เป็นวิธีที่สกปรก)

ในที่สุดคุณสามารถสร้างพล็อตความหนาแน่นเท่านั้นที่จะใช้เพื่อย่อยตัวอย่างคะแนนในพื้นที่ที่หนาแน่นที่สุด ในทางกลับกันจะไม่ทำพล็อตเดียวกันและอาจแนะนำสิ่งประดิษฐ์หากไม่ได้ปรับอย่างแม่นยำ


5
การลดการทับซ้อนกับuniqueหรือโดยการปัดเศษอาจส่งผลให้เกิดแผนการลำเอียง (หลอกลวง) สิ่งสำคัญคือต้องระบุจำนวนของการทับซ้อนผ่านวิธีกราฟิกบางอย่างเช่นความสว่างหรือด้วยแปลงดอกทานตะวัน
whuber

44

ดูแพ็คเกจhexbinที่ใช้กระดาษ / วิธีโดย Dan Carr บทความ pdf ที่มีรายละเอียดเพิ่มเติมที่ฉันพูดด้านล่าง:

1 ภาพรวม

Hexagon binning เป็นรูปแบบของฮิสโตแกรมแบบ bivariate ที่มีประโยชน์สำหรับการมองเห็นโครงสร้างในชุดข้อมูลที่มีขนาดใหญ่ n แนวคิดพื้นฐานของการหกเหลี่ยม binning ง่ายมาก;

  1. ระนาบ xy เหนือชุด (ช่วง (x), ช่วง (y)) ถูก tessellated โดยตารางปกติของรูปหกเหลี่ยม
  2. จำนวนคะแนนที่ตกลงในแต่ละหกเหลี่ยมจะถูกนับและจัดเก็บในโครงสร้างข้อมูล
  3. รูปหกเหลี่ยมที่มีจำนวน> 0 ถูกพล็อตโดยใช้ทางลาดสีหรือเปลี่ยนรัศมีของรูปหกเหลี่ยมตามสัดส่วนของจำนวน อัลกอริทึมพื้นฐานนั้นเร็วมากและมีประสิทธิภาพสำหรับการแสดงโครงสร้างของชุดข้อมูลด้วยn106

หากขนาดของกริดและการตัดในทางลาดสีถูกเลือกในแบบที่ฉลาดกว่าโครงสร้างที่มีอยู่ในข้อมูลควรจะปรากฎในพล็อตแบบโค้ง ข้อแม้เดียวกันนี้ใช้กับการกัดหกเหลี่ยมที่ใช้กับฮิสโทแกรมและควรใช้ความระมัดระวังในการเลือกพารามิเตอร์การแยก


4
นั่นเป็นสิ่งที่ดี สิ่งที่แพทย์สั่ง
Roman Luštrik

13
(+1) นอกจากนี้ที่น่าสนใจและsmoothScatter {RColorBrewer} densCols {grDevices}ฉันสามารถยืนยันได้ว่ามันทำงานได้ดีโดยมีคะแนนพันถึงล้านจากข้อมูลทางพันธุกรรม
chl

2
เกิดอะไรขึ้นถ้ามีข้อมูล 3D (มากเกินไปสำหรับ scatterplot3d)
skan

เพื่อประหยัดเวลาอื่น ๆ - ฉันพบ smoothScatter ตามที่แนะนำ 2 ความคิดเห็นขึ้นเพื่อให้มีค่าเริ่มต้น / การทำงานที่ดีขึ้นมาก
Charlie

16

ฉันต้องยอมรับว่าฉันไม่เข้าใจย่อหน้าสุดท้ายของคุณอย่างสมบูรณ์:

"ฉันไม่ได้กำลังมองหาพล็อตความหนาแน่น (แม้ว่าสิ่งเหล่านี้มักจะมีประโยชน์) ฉันต้องการผลลัพธ์แบบเดียวกับการเรียกจุดแบบง่าย ๆ แต่จะเร็วกว่ามากเกินล้านถ้าเป็นไปได้"

ยังไม่ชัดเจนว่าคุณกำลังมองหาพล็อต (ฟังก์ชัน) ประเภทใด

ระบุว่าคุณมีตัวแปรเมตริกคุณอาจพบว่าแปลงหกเหลี่ยมที่มีการผสมสองส่วนหรือแปลง sunnflower มีประโยชน์ สำหรับการอ้างอิงเพิ่มเติมดู


6

อีกคำตอบที่ตรงกับคำถามคือแพ็คเกจ rgl ซึ่งสามารถพล็อตจุดนับล้านโดยใช้ OpenGL นอกจากนี้ให้ระบุขนาดพอยต์ (เช่น 3) และซูมออกเพื่อดูจุดศูนย์กลางมวลชนเหล่านี้เป็นบล็อกเสาหินหรือซูมเข้าและดูโครงสร้างของสิ่งที่เคยเป็นเสาหิน - ขนาดของจุดคงที่ แต่ระยะทางระหว่างพวกเขาบนหน้าจอ ขึ้นอยู่กับการซูม นอกจากนี้ยังสามารถใช้ระดับอัลฟ่า


5

bigplotfix.Rนี่คือที่ผมเรียกไฟล์ หากคุณแหล่งมามันจะกำหนดเสื้อคลุมplot.xyที่ "บีบอัด" ข้อมูลพล็อตเมื่อมันมีขนาดใหญ่มาก wrapper ไม่ทำอะไรเลยถ้าอินพุตมีขนาดเล็ก แต่ถ้าอินพุตมีขนาดใหญ่มันจะแบ่งออกเป็นส่วน ๆ และเพียงแปลงค่า x และ y สูงสุดและต่ำสุดสำหรับแต่ละอัน การจัดหาbigplotfix.Rยัง rebinds graphics::plot.xyเพื่อชี้ไปที่ wrapper (การจัดหาหลาย ๆ ครั้งก็โอเค)

โปรดทราบว่า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

บางทีฉันอาจจะไม่ชอบวิธีการของฉันความทรงจำที่ไม่ดีของหนึ่งในผลงานวิจัยของฉันส่งเสียงร้องต่อผู้คนที่ทิ้งข้อมูลที่ดีโดยการแปลพวกเขาเป็นหมวดหมู่ (แน่นอนฉันเห็นด้วยตอนนี้สักวัน) อย่างไรก็ตามหากคุณกำลังพูดถึงแผนการกระจายฉันก็มีปัญหาเดียวกัน ตอนนี้เมื่อฉันมีข้อมูลที่เป็นตัวเลขฉันไม่มีความหมายที่จะจัดหมวดหมู่สำหรับการวิเคราะห์ แต่การมองเห็นเป็นเรื่องที่แตกต่าง สิ่งที่ฉันได้พบว่าสิ่งที่ดีที่สุดสำหรับฉันคือการแบ่งตัวแปรอิสระของคุณออกเป็นกลุ่มโดยใช้ฟังก์ชั่นการตัด คุณสามารถเล่นกับจำนวนกลุ่มและจากนั้น (2) เพียงแค่วางแผน DV กับเวอร์ชั่นที่ตัดของ IV R จะสร้างพล็อตของกล่องแทนพล็อตกระจายที่น่าขยะแขยง ฉันขอแนะนำให้ลบค่าผิดพลาดจากพล็อต (ใช้ตัวเลือกเค้าร่าง = FALSE ในคำสั่งแปลง) อีกครั้งฉันจะไม่เสียข้อมูลตัวเลขที่ดีอย่างสมบูรณ์แบบโดยการจัดหมวดหมู่แล้ววิเคราะห์ มีปัญหาในการทำเช่นนั้นมากเกินไป แม้ว่าฉันจะรู้ว่ามันเป็นหัวข้อถกเถียง แต่การทำเช่นนั้นโดยเฉพาะเพื่อเป้าหมายอย่างน้อยก็ทำให้การมองเห็นบางอย่างออกจากข้อมูลไม่เป็นอันตรายมากเท่าที่ฉันเคยเห็นจากมัน ฉันได้วางแผนข้อมูลที่มีขนาดใหญ่ถึง 10M และยังคงจัดการเพื่อให้เข้าใจได้จากวิธีนี้ หวังว่าจะช่วย! ด้วยความเคารพ! ได้เห็นจากมัน ฉันได้วางแผนข้อมูลที่มีขนาดใหญ่ถึง 10M และยังคงจัดการเพื่อให้เข้าใจได้จากวิธีนี้ หวังว่าจะช่วย! ด้วยความเคารพ! ได้เห็นจากมัน ฉันได้วางแผนข้อมูลที่มีขนาดใหญ่ถึง 10M และยังคงจัดการเพื่อให้เข้าใจได้จากวิธีนี้ หวังว่าจะช่วย! ด้วยความเคารพ!


0

สำหรับซีรีย์เวลาที่มีขนาดใหญ่ฉันได้เติบโตขึ้นเพื่อให้ได้ผลลัพธ์ที่น่าพอใจ (ส่วนหนึ่งของฐาน 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.