Histogramm mit Gnuplot?


202

Ich weiß, wie man in gnuplot ein Histogramm erstellt (verwenden Sie einfach "mit Kästchen"), wenn meine .dat-Datei bereits ordnungsgemäß gruppierte Daten enthält. Gibt es eine Möglichkeit, eine Liste mit Zahlen zu erstellen und Gnuplot ein Histogramm basierend auf den vom Benutzer angegebenen Bereichen und Behältergrößen erstellen zu lassen?


2
Wenn Sie keine Antwort erhalten, gibt es andere Tools, die solche Dinge tun sollen. Ich benutze Root ( root.cern.ch ), viele andere hier verwenden R, und es gibt mindestens ein paar andere Optionen.
dmckee --- Ex-Moderator Kätzchen

1
Bin ist der Wertebereich, der für jeden Balken im Histogramm zusammengetragen wird. Jeder Behälter hat eine Unter- und Obergrenze, und alle Daten mit einem Wert in diesem Bereich werden auf diesen Balken angerechnet. Binned bedeutet, dass meine Datendatei bereits nach der Anzahl der Datenpunkte in jedem Bin organisiert ist, sodass sie als Histogramm dargestellt werden kann.
Mary

Antworten:


225

Ja, und es ist schnell und einfach, aber sehr versteckt:

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

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

Überprüfen Sie, help smooth freqwarum oben ein Histogramm erstellt wird

Um mit Bereichen umzugehen, setzen Sie einfach die Variable xrange.


11
Ich denke, die Antwort von @ ChrisW unten bringt einen wichtigen Punkt für jeden, der ein Histogramm in Gnuplot erstellen möchte.
Abhinav

2
Seien Sie sehr vorsichtig, dies funktioniert nur, wenn kein "fehlender" Behälter im Satz vorhanden ist ... Diese Funktion legt den y-Wert eines fehlenden Fachs auf den y-Wert des vorherigen nicht fehlenden Fachs fest. Das kann sehr irreführend sein !!!
PinkFloyd

1
Ich würde oben hinzufügen set boxwidth binwidth. Es war sehr hilfreich für mich.
Jaakko

90

Ich habe ein paar Korrekturen / Ergänzungen zu Born2Smiles sehr nützlicher Antwort:

  1. Leere Behälter führten dazu, dass die Box für den angrenzenden Behälter falsch in ihren Raum hineinragte. Vermeiden Sie dies mitset boxwidth binwidth
  2. In der Version von Born2Smile werden Bins so zentriert, dass sie auf ihrer Untergrenze zentriert sind. Streng genommen sollten sie sich von der Untergrenze zur Obergrenze erstrecken. Dies kann durch Ändern der binFunktion korrigiert werden :bin(x,width)=width*floor(x/width) + width/2.0

10
Eigentlich sollte dieser zweite Teil sein bin(x,width)=width*floor(x/width) + binwidth/2.0(Gleitkommaberechnungen)
bgw

8
Du meinst bin(x,width)=width*floor(x/width) + width/2.0. Wenn wir widthals Argument übergeben, dann verwenden Sie es. :-)
Mitar

78

Seien Sie sehr vorsichtig: Alle Antworten auf dieser Seite treffen implizit die Entscheidung, wo das Binning beginnt - der linke Rand des am weitesten links liegenden Fachs, wenn Sie möchten - aus den Händen des Benutzers. Wenn der Benutzer eine dieser Funktionen zum Binning von Daten mit seiner eigenen Entscheidung darüber kombiniert, wo das Binning beginnt (wie in dem Blog, auf das oben verwiesen wird), sind die oben genannten Funktionen alle falsch. Mit einem beliebigen Startpunkt für das Binning von 'Min' lautet die richtige Funktion:

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

Sie können sehen, warum dies nacheinander korrekt ist (es hilft, ein paar Fächer und einen Punkt irgendwo in einem von ihnen zu zeichnen). Subtrahieren Sie Min von Ihrem Datenpunkt, um zu sehen, wie weit es im Binning-Bereich liegt. Teilen Sie dann durch die Binbreite, damit Sie effektiv in Einheiten von "Bins" arbeiten. Dann 'boden' das Ergebnis, um zum linken Rand dieses Behälters zu gelangen, addieren Sie 0,5, um zur Mitte des Behälters zu gelangen, multiplizieren Sie mit der Breite, damit Sie nicht mehr in Einheiten von Behältern, sondern in einer absoluten Skala arbeiten Fügen Sie dann wieder den Min-Offset hinzu, den Sie zu Beginn subtrahiert haben.

Betrachten Sie diese Funktion in Aktion:

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

zB fällt der Wert 1.1 wirklich in den linken Bereich:

  • Diese Funktion ordnet es korrekt der Mitte des linken Fachs zu (0,75).
  • Die Antwort von Born2Smile, bin (x) = width * floor (x / width), ordnet sie fälschlicherweise 1 zu.
  • Die Antwort von mas90, bin (x) = width * floor (x / width) + binwidth / 2.0, ordnet sie fälschlicherweise 1,5 zu.

Die Antwort von Born2Smile ist nur dann richtig, wenn die Bin-Grenzen bei (n + 0,5) * Binwidth auftreten (wobei n über ganze Zahlen läuft). Die Antwort von mas90 ist nur dann richtig, wenn die Bin-Grenzen bei n * binwidth auftreten.


48

Möchten Sie ein Diagramm wie dieses zeichnen? Geben Sie hier die Bildbeschreibung ein Ja? Dann können Sie sich meinen Blog-Artikel ansehen: http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html

Schlüsselzeilen aus dem Code:

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

Wie üblich ist Gnuplot ein fantastisches Werkzeug zum Zeichnen süß aussehender Grafiken und kann für alle Arten von Berechnungen verwendet werden. Jedoch ist es zu Plotdaten gedacht und nicht als Rechner zu dienen , und es ist oft einfacher , ein externes Programm (zB Octave) zu verwenden , die mehr „kompliziert“ Berechnungen zu tun, speichern diese Daten in einer Datei, dann verwenden Gnuplot zu produzieren der Graph. Überprüfen Sie für das oben genannte Problem, ob die Funktion "hist" Octave verwendet [freq,bins]=hist(data), und zeichnen Sie diese dann in Gnuplot mit

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

7

Ich fand diese Diskussion äußerst nützlich, aber ich habe einige "Abrundungs" -Probleme festgestellt.

Genauer gesagt habe ich bei einer Binbreite von 0,05 festgestellt, dass mit den hier oben vorgestellten Techniken Datenpunkte mit 0,1 und 0,15 in denselben Bin fallen. Dies (offensichtlich unerwünschtes Verhalten) ist höchstwahrscheinlich auf die "Boden" -Funktion zurückzuführen.

Im Folgenden ist mein kleiner Beitrag, um dies zu umgehen.

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

Diese rekursive Methode ist für x> = 0; man könnte dies mit bedingteren Aussagen verallgemeinern, um etwas noch allgemeineres zu erhalten.


6

Wir müssen keine rekursive Methode verwenden, sie kann langsam sein. Meine Lösung besteht darin, eine benutzerdefinierte Funktion zu verwenden, die die Instrumentenfunktion int oder floor enthält.

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

Diese Funktion wird geben rint(0.0003/0.0001)=3, während int(0.0003/0.0001)=floor(0.0003/0.0001)=2.

Warum? Bitte schauen Sie sich die Perl int-Funktion an und füllen Sie Nullen auf


4

Ich habe eine kleine Änderung an der Lösung von Born2Smile vorgenommen.

Ich weiß, dass das nicht viel Sinn macht, aber Sie können es für alle Fälle wollen. Wenn Ihre Daten eine Ganzzahl sind und Sie eine Float-Bin-Größe benötigen (möglicherweise zum Vergleich mit einem anderen Datensatz oder zur Darstellung der Dichte in einem feineren Raster), müssen Sie eine Zufallszahl zwischen 0 und 1 innerhalb des Stockwerks hinzufügen. Andernfalls treten aufgrund eines Aufrundungsfehlers Spitzen auf. floor(x/width+0.5)funktioniert nicht, da dadurch ein Muster erstellt wird, das den Originaldaten nicht entspricht.

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

1
Sie haben solche Situationen noch nicht erlebt, können es aber später tun. Sie können es mit normalverteilten Ganzzahlen mit einem Float-SD testen und Histogramme mit bin = 1 und bin = sd zeichnen. Sehen Sie, was Sie mit und ohne den Rand (0) -Trick erhalten. Ich habe den Fehler eines Mitarbeiters beim Überprüfen seines Manuskripts festgestellt. Seine Ergebnisse änderten sich erwartungsgemäß von absolutem Unsinn zu einer schönen Figur.
Path4

Ok, vielleicht ist die Erklärung so kurz, dass man sie ohne einen konkreteren Testfall nicht verstehen kann. Ich werde Ihre Antwort kurz bearbeiten, damit ich die Ablehnung rückgängig machen kann;)
Christoph

Betrachten Sie Ganzzahlen der Normalverteilung. Da es sich um Ganzzahlen handelt, haben viele von ihnen die gleiche x / width. Nehmen wir an, diese Zahl ist 1.3. Mit Boden (x / Breite + 0,5) werden alle von ihnen Bin 1 zugewiesen. Was 1,3 jedoch in Bezug auf die Dichte wirklich bedeutet, ist, dass 70% von ihnen in Bin 1 und 30% in Bin 2 sein sollten. Rand (0) ) behält die richtige Dichte. 0,5 erzeugt also Spitzen und Rand (0) hält es wahr. Ich wette, die Zahl von hsxz wird mit rand (0) anstelle von 0,5 viel glatter. Es wird nicht nur aufgerundet, es wird ohne Störung aufgerundet.
Path4

3

In Bezug auf Binning-Funktionen habe ich das Ergebnis der bisher angebotenen Funktionen nicht erwartet. Wenn meine Binbreite 0,001 beträgt, haben diese Funktionen die Bins auf 0,0005 Punkte zentriert, während es meiner Meinung nach intuitiver ist, die Bins auf 0,001 Grenzen zu zentrieren.

Mit anderen Worten, ich hätte gerne

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

Die Binning-Funktion, die ich mir ausgedacht habe, ist

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

Hier ist ein Skript, um einige der angebotenen Bin-Funktionen mit dieser zu vergleichen:

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
}

und hier ist die Ausgabe

   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
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.