Histogramme utilisant gnuplot?

202

Je sais comment créer un histogramme (utilisez simplement "avec des boîtes") dans gnuplot si mon fichier .dat contient déjà des données correctement regroupées. Existe-t-il un moyen de prendre une liste de nombres et de demander à gnuplot de fournir un histogramme basé sur les plages et les tailles de bacs fournies par l'utilisateur?

Marie
la source
2
Si vous n'obtenez pas de réponse, il existe d'autres outils destinés à faire de telles choses. J'utilise Root ( root.cern.ch ) beaucoup d'autres ici utilisent R, et il y a au moins quelques autres options.
dmckee --- chaton ex-modérateur
1
Bin est la plage de valeurs collectées ensemble pour chaque barre de l'histogramme. Chaque bac a une limite inférieure et supérieure, et toutes les données avec une valeur dans cette plage sont comptées dans cette barre. Binned signifie que mon fichier de données est déjà organisé par le nombre de points de données qui se trouvent dans chaque bac, il est donc prêt à être tracé sous forme d'histogramme.
mary

Réponses:

225

oui, et c'est rapide et simple mais très caché:

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

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

vérifiez help smooth freqpourquoi ce qui précède fait un histogramme

pour gérer les plages, il suffit de définir la variable xrange.

Born2Smile
la source
11
Je pense que la réponse de @ ChrisW ci-dessous apporte un point important à noter pour quiconque veut faire un histogramme dans Gnuplot.
Abhinav
2
Soyez très prudent, cela ne fonctionne que s'il n'y a pas de bac "manquant" dans l'ensemble ... Cette fonction fixe la valeur y d'un bac manquant à la valeur y du bac non manquant précédent. Cela peut être très trompeur !!!
PinkFloyd
1
J'ajouterais set boxwidth binwidthci-dessus. Cela m'a vraiment aidé.
Jaakko
90

J'ai quelques corrections / ajouts à la réponse très utile de Born2Smile:

  1. Des bacs vides ont provoqué une extension incorrecte de la boîte du bac adjacent dans son espace; éviter cela en utilisantset boxwidth binwidth
  2. Dans la version de Born2Smile, les bacs sont rendus comme centrés sur leur limite inférieure. Strictement, ils devraient s'étendre de la limite inférieure à la limite supérieure. Cela peut être corrigé en modifiant la binfonction:bin(x,width)=width*floor(x/width) + width/2.0
mas90
la source
10
En fait, cette deuxième partie devrait être bin(x,width)=width*floor(x/width) + binwidth/2.0(calculs en virgule flottante)
bgw
8
Tu veux dire bin(x,width)=width*floor(x/width) + width/2.0. Si nous passons widthcomme argument, alors utilisez-le. :-)
Mitar
78

Soyez très prudent: toutes les réponses sur cette page prennent implicitement la décision de commencer le binning - le bord gauche du bac le plus à gauche, si vous le souhaitez - hors des mains de l'utilisateur. Si l'utilisateur combine l'une de ces fonctions pour le binning des données avec sa propre décision sur le début du binning (comme cela se fait sur le blog lié ci-dessus), les fonctions ci-dessus sont toutes incorrectes. Avec un point de départ arbitraire pour le binning 'Min', la fonction correcte est:

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

Vous pouvez voir pourquoi cela est correct séquentiellement (cela aide à dessiner quelques bacs et un point quelque part dans l'un d'eux). Soustrayez Min de votre point de données pour voir dans quelle mesure il se trouve dans la plage de regroupement. Ensuite, divisez par la largeur de bin afin que vous travailliez efficacement en unités de «bacs». Ensuite, `` plancher '' le résultat pour aller au bord gauche de ce bac, ajoutez 0,5 pour aller au milieu du bac, multipliez par la largeur de sorte que vous ne travaillez plus en unités de bacs mais à une échelle absolue encore une fois, puis ajoutez à nouveau le décalage Min que vous avez soustrait au début.

Considérez cette fonction en action:

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

Par exemple, la valeur 1.1 tombe vraiment dans le bac de gauche:

  • cette fonction la mappe correctement au centre du bac gauche (0,75);
  • La réponse de Born2Smile, bin (x) = width * floor (x / width), la mappe incorrectement à 1;
  • La réponse de mas90, bin (x) = width * floor (x / width) + binwidth / 2.0, la mappe incorrectement à 1.5.

La réponse de Born2Smile n'est correcte que si les limites de bin se produisent à (n + 0,5) * binwidth (où n court sur des entiers). La réponse de mas90 n'est correcte que si les limites de bin se produisent à n * binwidth.

ChrisW
la source
48

Voulez-vous tracer un graphique comme celui-ci? entrez la description de l'image ici Oui? Ensuite, vous pouvez consulter mon article de blog: http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html

Lignes clés du 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
hsxz
la source
10

Comme d'habitude, Gnuplot est un outil fantastique pour tracer des graphiques à l'aspect doux et il peut être fait pour effectuer toutes sortes de calculs. Cependant , il est destiné à tracer des données plutôt qu'à servir de calculatrice et il est souvent plus facile d'utiliser un programme externe (par exemple Octave) pour effectuer les calculs les plus "compliqués", enregistrer ces données dans un fichier, puis utiliser Gnuplot pour produire le graphique. Pour le problème ci-dessus, vérifiez que la fonction "hist" utilise Octave [freq,bins]=hist(data), puis tracez ceci dans Gnuplot en utilisant

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

J'ai trouvé cette discussion extrêmement utile, mais j'ai rencontré des problèmes d'arrondi.

Plus précisément, en utilisant une largeur de bin de 0,05, j'ai remarqué qu'avec les techniques présentées ci-dessus, les points de données qui lisent 0,1 et 0,15 tombent dans le même bin. Cela (comportement manifestement indésirable) est très probablement dû à la fonction «plancher».

Ci-après ma petite contribution pour tenter de contourner cela.

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

Cette méthode récursive est pour x> = 0; on pourrait généraliser cela avec des instructions plus conditionnelles pour obtenir quelque chose d'encore plus général.

Alex
la source
6

Nous n'avons pas besoin d'utiliser de méthode récursive, cela peut être lent. Ma solution consiste à utiliser une fonction définie par l'utilisateur au lieu de la fonction instrinsic int ou floor.

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

Cette fonction donnera rint(0.0003/0.0001)=3, tout int(0.0003/0.0001)=floor(0.0003/0.0001)=2.

Pourquoi? Veuillez regarder la fonction int de Perl et les zéros de remplissage

JOE
la source
4

J'ai une petite modification à la solution de Born2Smile.

Je sais que cela n'a pas beaucoup de sens, mais vous pouvez le vouloir juste au cas où. Si vos données sont entières et que vous avez besoin d'une taille de bac flottant (peut-être pour la comparaison avec un autre ensemble de données ou une densité de tracé dans une grille plus fine), vous devrez ajouter un nombre aléatoire entre 0 et 1 à l'intérieur du sol. Sinon, il y aura des pics en raison d'une erreur d'arrondi. floor(x/width+0.5)ne le fera pas car il créera un modèle qui n'est pas fidèle aux données d'origine.

binwidth=0.3
bin(x,width)=width*floor(x/width+rand(0))
chemin4
la source
1
Vous n'avez pas rencontré de telles situations, mais vous pourrez le faire plus tard. Vous pouvez le tester avec des entiers normalement distribués avec un float sd et tracer des histogrammes avec bin = 1, et bin = sd Voyez ce que vous obtenez avec et sans l'astuce rand (0). J'ai remarqué l'erreur d'un collaborateur lors de l'examen de son manuscrit. Ses résultats sont passés d'une absurdité absolue à une belle silhouette comme prévu.
path4
Ok, peut-être que l'explication est si courte, qu'on ne peut pas la comprendre sans un cas de test plus concret. Je ferai une courte modification de votre réponse afin que je puisse annuler le vote négatif;)
Christoph
Considérons des entiers de distribution normale. Puisqu'il s'agit d'entiers, beaucoup d'entre eux auront la même largeur x /. Disons que ce nombre est 1,3. Avec le plancher (x / largeur + 0,5), tous seront affectés au bac 1. Mais ce que 1,3 signifie vraiment en termes de densité, c'est que 70% d'entre eux devraient être dans le bac 1 et 30% dans le bac 2. rand (0 ) conserve la bonne densité. Donc, 0,5 crée des pointes et rand (0) le maintient. Je parie que le chiffre de hsxz sera beaucoup plus fluide en utilisant rand (0) au lieu de 0,5. Ce n'est pas seulement arrondir, c'est arrondir sans perturbation.
path4
3

En ce qui concerne les fonctions de binning, je ne m'attendais pas au résultat des fonctions proposées jusqu'à présent. À savoir, si ma largeur de bin est de 0,001, ces fonctions centraient les cases sur 0,0005 points, alors que je pense qu'il est plus intuitif d'avoir les cases centrées sur les limites de 0,001.

En d'autres termes, j'aimerais avoir

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

La fonction de binning que j'ai trouvée est

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

Voici un script pour comparer certaines des fonctions bin offertes à celle-ci:

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
}

et voici la sortie

   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
Winston Smith
la source