Gnuplot kullanarak histogram?


205

Eğer .dat dosya zaten düzgün bir şekilde depolanmış veri varsa gnuplot bir histogram (sadece "kutuları" kullanın) oluşturmak biliyorum. Bir sayı listesi almanın ve gnuplot'un kullanıcının sağladığı aralıklara ve çöp kutusu boyutlarına dayalı bir histogram sağlamasının bir yolu var mı?


2
Bir cevap alamazsanız, böyle şeyler yapmak için başka araçlar da vardır. Kök ( root.cern.ch ) kullanıyorum, buradaki pek çok kişi R kullanıyor ve en az birkaç seçenek daha var.
dmckee --- eski moderatör yavru kedi

1
Bölme, histogramdaki her çubuk için birlikte toplanan değer aralığıdır. Her bölmenin alt ve üst sınırı vardır ve bu aralıktaki bir değere sahip tüm veriler bu çubuğa sayılır. Binned, veri dosyamın zaten her bir bölmeye kaç veri noktasının düştüğüne göre düzenlendiği anlamına gelir, bu nedenle bir histogram olarak çizilmeye hazırdır.
mary

Yanıtlar:


225

evet, ve çok gizli olsa da hızlı ve basit:

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

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

kontrol help smooth freqyukarıda bir histogram yapar neden görmek

aralıkları ele almak için sadece xrange değişkenini ayarlayın.


11
@ ChrisW'nin aşağıdaki cevabının Gnuplot'ta Histogram yapmak isteyen herkes için önemli bir nokta getirdiğini düşünüyorum.
Abhinav

2
Çok dikkatli olun, bu yalnızca kümede "eksik" bölme yoksa çalışır ... Bu işlev, eksik bir bölmenin y değerini önceki eksik olmayan bölmenin y değerine sabitler. Bu çok yanıltıcı olabilir !!!
PinkFloyd

1
Ben eklersiniz set boxwidth binwidthyukarıda. Benim için gerçekten çok yardımcı oldu.
Jaakko

90

Born2Smile'ın çok yararlı cevabına birkaç düzeltme / ekleme yapıyorum:

  1. Boş kutular, bitişik bölmenin kutusunun yanlış şekilde boşluğuna uzanmasına neden oldu; bundan kaçınınset boxwidth binwidth
  2. Born2Smile'in versiyonunda, kutular alt sınırlarına ortalanmış olarak işlenir. Kesinlikle alt sınırdan üst sınıra uzanmalıdırlar. Bu, binişlev değiştirilerek düzeltilebilir :bin(x,width)=width*floor(x/width) + width/2.0

10
Aslında bu ikinci bölüm olmalıdır bin(x,width)=width*floor(x/width) + binwidth/2.0(kayan nokta hesaplamaları)
bgw

8
Yani bin(x,width)=width*floor(x/width) + width/2.0. Eğer widthbir argüman olarak geçiyorsak, onu kullan. :-)
Mitar

78

Çok dikkatli olun: bu sayfadaki tüm cevaplar, binning'in nerede başladığına - isterseniz en soldaki bölmenin sol kenarına - kullanıcının ellerinden çıkma kararını dolaylı olarak alıyor. Kullanıcı, verileri bölmek için bu işlevlerden herhangi birini, binning'in nerede başladığı hakkındaki kendi kararı ile birleştiriyorsa (yukarıdaki bağlantıya bağlı olan blogda olduğu gibi) yukarıdaki işlevlerin tümü yanlıştır. 'Min' için binicilik keyfi bir başlangıç ​​noktasıyla, doğru fonksiyon:

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

Bunun neden ardışık olarak doğru olduğunu görebilirsiniz (bunlardan birinde bir yer ve birkaç kutu çizmeye yardımcı olur). Binning aralığına ne kadar uzak olduğunu görmek için Min'i veri noktanızdan çıkarın. Ardından bin kutularına bölün, böylece 'kutu' birimlerinde etkili bir şekilde çalışabilirsiniz. Ardından, o bölmenin sol kenarına gitmek için sonucu 'katlayın', bölmenin ortasına gitmek için 0,5 ekleyin, genişlikle çarpın, böylece artık kutularda değil mutlak bir ölçekte çalışacaksınız sonra, başlangıçta çıkardığınız Min ofsetine tekrar ekleyin.

Bu işlevi çalışırken düşünün:

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

Örneğin, 1.1 değeri gerçekten sol bölmeye düşer:

  • bu işlev onu sol selenin merkezine doğru bir şekilde eşler (0.75);
  • Born2Smile yanıtı, bin (x) = width * floor (x / width), yanlış 1 olarak eşleştirir;
  • mas90'ın yanıtı, bin (x) = genişlik * kat (x / genişlik) + binwidth / 2.0, yanlış 1,5 ile eşler.

Born2Smile'ın yanıtı, yalnızca bin sınırları (n + 0,5) * bin genişliğinde (n'nin tamsayılar üzerinde çalıştığı yerlerde) gerçekleşmesi durumunda doğrudur. mas90'ın yanıtı, yalnızca kutu sınırları n * bin genişliğinde gerçekleşirse doğrudur.


48

Bunun gibi bir grafik çizmek ister misiniz? resim açıklamasını buraya girin Evet? O zaman blog makaleme göz atabilirsiniz: http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html

Koddaki anahtar satırlar:

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

Her zamanki gibi, Gnuplot tatlı görünümlü grafikler çizmek için harika bir araçtır ve her türlü hesaplamayı yapmak için yapılabilir. Bununla birlikte , bir hesap makinesi olarak hizmet etmek yerine verileri çizmeyi amaçlamaktadır ve daha "karmaşık" hesaplamaları yapmak için harici bir program (örneğin Octave) kullanmak, bu verileri bir dosyaya kaydetmek, daha sonra üretmek için Gnuplot kullanmak daha kolaydır. grafik. Yukarıdaki sorun için, "hist" işlevini kullanarak Octave olduğunu kontrol edin [freq,bins]=hist(data), sonra bunu kullanarak Gnuplot'ta çizin

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

7

Bu tartışmayı son derece yararlı buldum, ancak bazı "yuvarlama" sorunları yaşadım.

Daha kesin olarak, 0.05'lik bir bin genişlik kullanarak, yukarıda sunulan tekniklerle, 0.1 ve 0.15'i okuyan veri noktalarının aynı kutuda düştüğünü fark ettim. Bu (açıkça istenmeyen davranış) büyük olasılıkla "zemin" işlevinden kaynaklanmaktadır.

Ahiret bundan kaçınmaya çalışmak için benim küçük katkım olacak.

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

Bu özyinelemeli yöntem x> = 0 içindir; daha genel bir şey elde etmek için bunu daha koşullu ifadelerle genelleştirebiliriz.


6

Özyinelemeli yöntem kullanmamız gerekmez, yavaş olabilir. Benim çözüm iç ya da zemine içten işlevli kullanıcı tanımlı işlev rint instesd kullanmaktır.

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

Bu işlev rint(0.0003/0.0001)=3 süre verir int(0.0003/0.0001)=floor(0.0003/0.0001)=2.

Neden? Lütfen Perl int işlevine ve dolgu sıfırlarına bakın


4

Born2Smile'ın çözümünde küçük bir değişiklik yaptım.

Bunun pek mantıklı olmadığını biliyorum, ama her ihtimale karşı isteyebilirsiniz. Verileriniz tamsayı ise ve bir kayan kutu boyutuna ihtiyacınız varsa (belki başka bir veri kümesiyle karşılaştırmak veya daha ince ızgaradaki grafik yoğunluğu için), zemine 0 ile 1 arasında rastgele bir sayı eklemeniz gerekir. Aksi takdirde, yuvarlama hatası nedeniyle ani artışlar olacaktır. floor(x/width+0.5)yapmaz çünkü orijinal veriler için doğru olmayan bir desen oluşturur.

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

1
Bu tür durumlarla karşılaşmadınız, ancak daha sonra da karşılaşabilirsiniz. Normalde dağıtılmış tamsayılarla bir float sd ile test edebilir ve histogramları bin = 1 ve bin = sd ile çizebilirsiniz. Rand (0) hilesi ile ve olmadan ne elde ettiğinizi görün. Makalesini incelerken bir ortak çalışmanın hatasını yakaladım. Sonuçları beklendiği gibi kesinlikle saçmalıktan güzel bir rakama dönüştü.
path4

Tamam, belki açıklama o kadar kısadır ki, daha somut bir test senaryosu olmadan bunu anlayamazsınız. Cevabınızı kısa bir düzenleme yapacağım, böylece aşağı oyu geri alabilirim;)
Christoph

Normal dağılımın tam sayılarını düşünün. Tamsayı oldukları için birçoğu aynı x / genişliğe sahip olacaktır. Diyelim ki bu sayı 1.3. Zemin (x / genişlik + 0.5) ile, hepsi 1. bölmeye atanacaktır. Ancak 1.3'ün yoğunluk açısından gerçekten anlamı, bunların% 70'inin 1. bölmede ve% 30'un 2. bölmede olması gerektiğidir. Rand (0 ) uygun yoğunluğu korur. Böylece, 0,5 ani artışlar yaratır ve rand (0) bunu gerçek tutar. Ben hsxz rakam 0.5 yerine rand (0) kullanarak çok daha düzgün olacağını bahis. Sadece yuvarlanmakla kalmıyor, pertürbasyon olmadan yuvarlanıyor.
path4

3

Binning fonksiyonları ile ilgili olarak, şimdiye kadar sunulan fonksiyonların sonucunu beklemiyordum. Yani, bin genişliğim 0.001 ise, bu işlevler bölmeleri 0.0005 noktaya ortalıyken, kutuların 0.001 sınırlarına odaklanmasının daha sezgisel olduğunu hissediyorum.

Başka bir deyişle, sahip olmak istiyorum

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

Geldiğim binning fonksiyonu

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

Sunulan bazı bin işlevlerini bununla karşılaştırmak için bir komut dosyası:

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
}

ve işte çıktı

   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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.