ฮิสโตแกรมโดยใช้ gnuplot


202

ฉันรู้วิธีสร้างฮิสโตแกรม (เพียงแค่ใช้ "พร้อมกล่อง") ใน gnuplot หากไฟล์. dat ของฉันมีข้อมูลที่ถูกเจาะอย่างถูกต้องอยู่แล้ว มีวิธีในการรับรายการตัวเลขและให้ gnuplot จัดทำฮิสโตแกรมตามช่วงและขนาดถังที่ผู้ใช้ให้หรือไม่


2
หากคุณไม่ได้รับคำตอบมีเครื่องมืออื่น ๆ ที่ตั้งใจจะทำสิ่งนั้น ฉันใช้รูท ( root.cern.ch ) อื่น ๆ อีกมากมายที่นี่ใช้ R และมีตัวเลือกอื่นอย่างน้อย
dmckee --- ผู้ดูแลอดีตลูกแมว

1
Bin คือช่วงของค่าที่รวบรวมเข้าด้วยกันสำหรับแต่ละแท่งในฮิสโตแกรม แต่ละ bin มีขีด จำกัด ล่างและบนและข้อมูลทั้งหมดที่มีค่าในช่วงนั้นจะถูกนับเข้าสู่แถบนั้น Binned หมายถึงไฟล์ข้อมูลของฉันได้รับการจัดระเบียบแล้วโดยมีจุดข้อมูลจำนวนมากอยู่ในแต่ละ bin ดังนั้นจึงพร้อมที่จะลงจุดเป็นฮิสโตแกรม
mary

คำตอบ:


225

ใช่และรวดเร็วและเรียบง่าย แต่ซ่อนอยู่มาก:

binwidth=5
bin(x,width)=width*floor(x/width)

plot 'datafile' using (bin($1,binwidth)):(1.0) smooth freq with boxes

ลองhelp smooth freqดูว่าทำไมข้างต้นทำฮิสโตแกรม

เพื่อจัดการกับช่วงเพียงแค่ตั้งค่าตัวแปร xrange


11
ฉันคิดว่าคำตอบของ @ ChrisW ด้านล่างนำมาซึ่งจุดสำคัญที่จะสังเกตเห็นได้สำหรับทุกคนที่ต้องการสร้างฮิสโตแกรมใน Gnuplot
Abhinav

2
โปรดใช้ความระมัดระวังอย่างยิ่งจะสามารถใช้งานได้หากไม่มีถังขยะ "ที่หายไป" ในชุด ... ฟังก์ชั่นนี้จะแก้ไขค่า y ของถังที่หายไปเป็นค่า y ของถังที่ไม่ขาดหายไปก่อนหน้านี้ นี่อาจทำให้เข้าใจผิดมาก !!!
PinkFloyd

1
ฉันจะเพิ่มset boxwidth binwidthไปด้านบน มันมีประโยชน์จริง ๆ สำหรับฉัน
Jaakko

90

ฉันมีคำตอบที่เป็นประโยชน์อย่างมากของ Born2Smile ในการแก้ไข / เพิ่มเติม

  1. ช่องว่างเปล่าทำให้กล่องสำหรับถังขยะติดกันขยายออกไปในอวกาศอย่างไม่ถูกต้อง หลีกเลี่ยงการใช้สิ่งนี้set boxwidth binwidth
  2. ในเวอร์ชั่นของ Born2Smile จะมีการแสดงถังขยะโดยมีศูนย์กลางที่ขอบล่าง พวกเขาควรขยายจากขอบเขตล่างถึงขอบเขตบนอย่างเคร่งครัด สิ่งนี้สามารถแก้ไขได้โดยการแก้ไขbinฟังก์ชั่น:bin(x,width)=width*floor(x/width) + width/2.0

10
ที่จริงแล้วส่วนที่สองควรเป็นbin(x,width)=width*floor(x/width) + binwidth/2.0(การคำนวณจุดลอย)
bgw

8
bin(x,width)=width*floor(x/width) + width/2.0คุณหมายถึง หากเราผ่านไปwidthเป็นข้อโต้แย้งให้ใช้มัน :-)
Mitar

78

ระวังให้ดี: คำตอบทั้งหมดในหน้านี้เป็นการตัดสินใจโดยปริยายว่าจุดเริ่มต้นของการเริ่มจากจุดใด - ขอบซ้ายของถังขยะซ้ายหากคุณต้องการ - ออกจากมือของผู้ใช้ หากผู้ใช้กำลังรวมฟังก์ชั่นใด ๆ เหล่านี้เข้ากับการตัดสินใจข้อมูลของตัวเองเกี่ยวกับการเริ่มต้นการทำสำเนา (ตามที่ทำในบล็อกที่เชื่อมโยงกับด้านบน) ฟังก์ชั่นด้านบนนั้นไม่ถูกต้องทั้งหมด ด้วยจุดเริ่มต้นโดยพลการสำหรับการ 'ต่ำสุด' ฟังก์ชันที่ถูกต้องคือ:

bin(x) = width*(floor((x-Min)/width)+0.5) + Min

คุณสามารถดูว่าทำไมสิ่งนี้ถึงถูกต้องตามลำดับ (ช่วยในการวาดถังขยะสองสามอันและจุดหนึ่งในหนึ่งในนั้น) ลบขั้นต่ำออกจากจุดข้อมูลของคุณเพื่อดูว่าอยู่ไกลแค่ไหนในขอบเขตของการ binning จากนั้นหารด้วยความเร็วเพื่อให้คุณทำงานได้อย่างมีประสิทธิภาพในหน่วยของ 'ถังขยะ' จากนั้น 'จัดเรียง' ผลลัพธ์เพื่อไปที่ขอบด้านซ้ายของถังขยะนั้นเพิ่ม 0.5 เพื่อไปที่กลางถังขยะคูณด้วยความกว้างเพื่อให้คุณไม่ได้ทำงานในหน่วยถังขยะอีกต่อไป อีกครั้งจากนั้นก็บวกกลับไปที่ค่าชดเชยขั้นต่ำที่คุณหักออกเมื่อเริ่มต้น

ลองใช้ฟังก์ชั่นนี้:

Min = 0.25 # where binning starts
Max = 2.25 # where binning ends
n = 2 # the number of bins
width = (Max-Min)/n # binwidth; evaluates to 1.0
bin(x) = width*(floor((x-Min)/width)+0.5) + Min

เช่นค่า 1.1 อยู่ในถังขยะด้านซ้ายอย่างแท้จริง:

  • ฟังก์ชั่นนี้แมปไปยังกึ่งกลางของถังด้านซ้ายอย่างถูกต้อง (0.75);
  • คำตอบของ Born2Smile, bin (x) = width * floor (x / width), แมปไม่ถูกต้องกับ 1;
  • คำตอบของ mas90, bin (x) = width * floor (x / width) + binwidth / 2.0, แมปไม่ถูกต้องกับ 1.5

คำตอบของ Born2Smile นั้นถูกต้องก็ต่อเมื่อขอบเขตของถังขยะเกิดขึ้นที่ (n + 0.5) * ความกว้าง (ที่ n วิ่งข้ามจำนวนเต็ม) คำตอบของ mas90 นั้นถูกต้องก็ต่อเมื่อมีขอบเขตของ bin เกิดขึ้นที่ n * binwidth


48

คุณต้องการพล็อตกราฟแบบนี้หรือไม่? ป้อนคำอธิบายรูปภาพที่นี่ ใช่? จากนั้นคุณสามารถดูบทความบล็อกของฉัน: http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html

สายสำคัญจากรหัส:

n=100 #number of intervals
max=3. #max value
min=-3. #min value
width=(max-min)/n #interval width
#function used to map a value to the intervals
hist(x,width)=width*floor(x/width)+width/2.0
set boxwidth width*0.9
set style fill solid 0.5 # fill style

#count and plot
plot "data.dat" u (hist($1,width)):(1.0) smooth freq w boxes lc rgb"green" notitle

10

ตามปกติแล้ว Gnuplot เป็นเครื่องมือที่ยอดเยี่ยมสำหรับการพล็อตกราฟที่ดูหวานและสามารถทำการคำนวณได้ทุกประเภท อย่างไรก็ตามมีจุดประสงค์ในการพล็อตข้อมูลแทนที่จะใช้เป็นเครื่องคิดเลขและมักจะใช้โปรแกรมภายนอก (เช่น Octave) เพื่อทำการคำนวณที่ "ซับซ้อน" มากขึ้นบันทึกข้อมูลนี้ในไฟล์แล้วใช้ Gnuplot เพื่อสร้าง กราฟ สำหรับปัญหาข้างต้นให้ตรวจสอบฟังก์ชั่น "hist" ที่ใช้เป็นอ็อกเท[freq,bins]=hist(data)ฟจากนั้นพล็อตเรื่องนี้ใน Gnuplot โดยใช้

set style histogram rowstacked gap 0
set style fill solid 0.5 border lt -1
plot "./data.dat" smooth freq with boxes

7

ฉันพบการสนทนานี้มีประโยชน์อย่างยิ่ง แต่ฉันพบปัญหา "การปัดเศษ" บางอย่าง

แม่นยำยิ่งขึ้นด้วยการใช้ความกว้างของ 0.05 ฉันได้สังเกตเห็นว่าด้วยเทคนิคที่นำเสนอที่นี่ข้างต้นจุดข้อมูลที่อ่าน 0.1 และ 0.15 อยู่ในถังขยะเดียวกัน พฤติกรรมนี้ (พฤติกรรมที่ไม่ต้องการอย่างเห็นได้ชัด) มักเกิดจากฟังก์ชั่น "floor"

ต่อจากนี้ไปฉันมีส่วนร่วมเล็กน้อยในการพยายามหลีกเลี่ยงสิ่งนี้

bin(x,width,n)=x<=n*width? width*(n-1) + 0.5*binwidth:bin(x,width,n+1)
binwidth = 0.05
set boxwidth binwidth
plot "data.dat" u (bin($1,binwidth,1)):(1.0) smooth freq with boxes

วิธีการเรียกซ้ำนี้ใช้สำหรับ x> = 0; เราสามารถพูดคุยเรื่องนี้โดยมีเงื่อนไขเพิ่มเติมเพื่อให้ได้อะไรที่กว้างกว่า


6

เราไม่จำเป็นต้องใช้วิธีการเรียกซ้ำมันอาจจะช้า วิธีการแก้ปัญหาของฉันคือการใช้ฟังก์ชั่นที่ผู้ใช้กำหนดเองอินสแตนซ์ของฟังก์ชั่น instrinsic int หรือพื้น

rint(x)=(x-int(x)>0.9999)?int(x)+1:int(x)

ฟังก์ชั่นนี้จะช่วยให้ในขณะที่rint(0.0003/0.0001)=3int(0.0003/0.0001)=floor(0.0003/0.0001)=2

ทำไม? โปรดดูฟังก์ชั่น Perl int และการเติมเต็มศูนย์


4

ฉันมีการดัดแปลงเล็กน้อยเพื่อแก้ปัญหาของ Born2Smile

ฉันรู้ว่ามันไม่สมเหตุสมผล แต่คุณอาจต้องการในกรณี หากข้อมูลของคุณเป็นจำนวนเต็มและคุณต้องการขนาดถังขยะ (อาจเปรียบเทียบกับชุดข้อมูลอื่นหรือความหนาแน่นของพล็อตในตารางปลีกย่อย) คุณจะต้องเพิ่มจำนวนสุ่มระหว่าง 0 ถึง 1 มิฉะนั้นจะมี spikes เนื่องจากข้อผิดพลาดในการปัดเศษขึ้น floor(x/width+0.5)จะไม่ทำเพราะมันจะสร้างรูปแบบที่ไม่เป็นความจริงกับข้อมูลต้นฉบับ

binwidth=0.3
bin(x,width)=width*floor(x/width+rand(0))

1
คุณไม่ได้พบกับสถานการณ์ดังกล่าว แต่คุณสามารถทำได้ในภายหลัง คุณสามารถทดสอบกับจำนวนเต็มกระจายปกติกับลอย sd และพล็อต histograms กับ bin = 1 และ bin = sd ดูสิ่งที่คุณได้รับด้วยและไม่มีเคล็ดลับแรนด์ (0) ฉันจับข้อผิดพลาดของผู้ทำงานร่วมกันเมื่อตรวจสอบต้นฉบับของเขา ผลลัพธ์ของเขาเปลี่ยนจากเรื่องไร้สาระเป็นรูปสวยงามอย่างที่คาดไว้
path4

ตกลงบางทีคำอธิบายสั้นเกินไปที่เราไม่สามารถเข้าใจได้หากไม่มีกรณีทดสอบที่เป็นรูปธรรมมากขึ้น ฉันจะแก้ไขคำตอบของคุณสั้น ๆ เพื่อให้ฉันสามารถยกเลิก downvote;)
Christoph

พิจารณาจำนวนเต็มของการแจกแจงแบบปกติ เนื่องจากเป็นจำนวนเต็มหลายคนจึงมี x / ความกว้างเท่ากัน สมมุติว่าตัวเลขนั้นคือ 1.3 ด้วย floor (x / width + 0.5) ทั้งหมดจะถูกกำหนดให้กับ bin 1 แต่สิ่งที่ 1.3 จริงๆหมายถึงในแง่ของความหนาแน่นคือ 70% ของพวกเขาควรอยู่ใน bin 1 และ 30% ใน bin 2 rand (0 ) รักษาความหนาแน่นที่เหมาะสม ดังนั้น 0.5 สร้างเดือยและแรนด์ (0) ทำให้เป็นจริง ฉันเดิมพันตัวเลขโดย hsxz จะราบรื่นกว่าโดยใช้ rand (0) แทนที่จะเป็น 0.5 มันไม่ใช่แค่การปัดเศษ แต่เป็นการปัดเศษโดยไม่ก่อกวน
path4

3

ด้วยความเคารพต่อฟังก์ชั่น binning ฉันไม่ได้คาดหวังผลลัพธ์ของฟังก์ชั่นที่เสนอมาจนถึงตอนนี้ กล่าวคือถ้าความกว้างของฉันคือ 0.001 ฟังก์ชั่นเหล่านี้อยู่ตรงกลางของถังขยะที่ 0.0005 จุดในขณะที่ฉันรู้สึกว่ามันง่ายกว่าที่จะให้ถังขยะอยู่ที่ขอบเขต 0.001

กล่าวอีกนัยหนึ่งฉันต้องการที่จะมี

Bin 0.001 contain data from 0.0005 to 0.0014
Bin 0.002 contain data from 0.0015 to 0.0024
...

ฟังก์ชั่น binning ที่ฉันคิดขึ้นมาคือ

my_bin(x,width)     = width*(floor(x/width+0.5))

ต่อไปนี้เป็นสคริปต์เพื่อเปรียบเทียบฟังก์ชั่นถังขยะที่นำเสนอกับฟังก์ชันนี้:

rint(x) = (x-int(x)>0.9999)?int(x)+1:int(x)
bin(x,width)        = width*rint(x/width) + width/2.0
binc(x,width)       = width*(int(x/width)+0.5)
mitar_bin(x,width)  = width*floor(x/width) + width/2.0
my_bin(x,width)     = width*(floor(x/width+0.5))

binwidth = 0.001

data_list = "-0.1386 -0.1383 -0.1375 -0.0015 -0.0005 0.0005 0.0015 0.1375 0.1383 0.1386"

my_line = sprintf("%7s  %7s  %7s  %7s  %7s","data","bin()","binc()","mitar()","my_bin()")
print my_line
do for [i in data_list] {
    iN = i + 0
    my_line = sprintf("%+.4f  %+.4f  %+.4f  %+.4f  %+.4f",iN,bin(iN,binwidth),binc(iN,binwidth),mitar_bin(iN,binwidth),my_bin(iN,binwidth))
    print my_line
}

และนี่คือผลลัพธ์

   data    bin()   binc()  mitar()  my_bin()
-0.1386  -0.1375  -0.1375  -0.1385  -0.1390
-0.1383  -0.1375  -0.1375  -0.1385  -0.1380
-0.1375  -0.1365  -0.1365  -0.1375  -0.1380
-0.0015  -0.0005  -0.0005  -0.0015  -0.0010
-0.0005  +0.0005  +0.0005  -0.0005  +0.0000
+0.0005  +0.0005  +0.0005  +0.0005  +0.0010
+0.0015  +0.0015  +0.0015  +0.0015  +0.0020
+0.1375  +0.1375  +0.1375  +0.1375  +0.1380
+0.1383  +0.1385  +0.1385  +0.1385  +0.1380
+0.1386  +0.1385  +0.1385  +0.1385  +0.1390
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.