2010-03-18 29 views
187

Sé cómo crear un histograma (simplemente use "con cuadros") en gnuplot si mi archivo .dat ya tiene datos correctamente agrupados. ¿Hay alguna manera de tomar una lista de números y hacer que gnuplot proporcione un histograma en función de los rangos y tamaños de espacio que el usuario proporciona?¿Histograma usando gnuplot?

+2

Si no obtiene una respuesta, existen otras herramientas destinadas a hacer tales cosas. Yo uso Root (http://root.cern.ch/) muchos otros por aquí usan R, y hay al menos algunas otras opciones. – dmckee

+0

¿qué es bin y qué es binned? –

Respuesta

208

sí, y su rápida y sencilla aunque muy ocultos:

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

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

la salida help smooth freq para ver por qué el anterior hace un histograma

para hacer frente a los rangos acaba de establecer la variable xrange.

+8

Creo que la respuesta de @ ChrisW a continuación trae un punto importante a tener en cuenta para cualquiera que quiera hacer un Histograma en Gnuplot. – Abhinav

+1

Tenga mucho cuidado, esto solo funciona si no hay un contenedor "perdido" en el conjunto ... Esta función corrige el valor y de un contenedor perdido al valor y del contenedor anterior no perdido. ¡Esto puede ser muy engañoso! – PinkFloyd

77

I tienen un par de correcciones/adiciones a respuesta muy útil del Born2Smile:

  1. contenedores vacíos causados ​​la caja para el bin adyacente a extender incorrectamente en su espacio; evite esto usando set boxwidth binwidth
  2. En la versión de Born2Smile, los contenedores se muestran centrados en su límite inferior. Estrictamente, deberían extenderse desde el límite inferior al límite superior. Esto se puede corregir mediante la modificación de la función bin: bin(x,width)=width*floor(x/width) + binwidth/2.0
+10

En realidad, esa segunda parte debe ser 'bin (x, ancho) = ancho * piso (x/ancho) + binwidth/2.0' (cálculos de punto flotante) – bgw

+7

Quiere decir' bin (x, ancho) = ancho * piso (x/ancho) + ancho/2.0'. Si estamos pasando 'ancho' como un argumento, entonces úselo. :-) – Mitar

+2

@mgilson, creo que la respuesta de ChrisW aporta una importante corrección a esta respuesta. – Abhinav

40

¿Quieres trazar un gráfico como éste? enter image description here ¿sí? A continuación, puede echar un vistazo a mi artículo de blog: http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html

líneas clave del código:

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 
5

he encontrado esta discusión muy útil, pero he experimentado algunos "redondeo" problemas.

Más precisamente, usando un ancho de contenedor de 0.05, he notado que, con las técnicas presentadas aquí arriba, los puntos de datos que leen 0.1 y 0.15 caen en la misma ubicación. Esto (comportamiento obviamente no deseado) se debe probablemente a la función "piso".

De ahora en adelante es mi pequeña contribución para tratar de eludir esto.

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 

Este método recursivo es para x> = 0; uno podría generalizar esto con más declaraciones condicionales para obtener algo aún más general.

4

No es necesario utilizar el método recursivo, puede ser lento. Mi solución está utilizando una función definida por el usuario rint instesd de la función intrínseca int o floor.

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

Esta función permite obtener rint(0.0003/0.0001)=3, mientras int(0.0003/0.0001)=floor(0.0003/0.0001)=2.

¿Por qué? Consulte Perl int function and padding zeros

67

Tenga mucho cuidado: todas las respuestas en esta página toman implícitamente la decisión de dónde comienza el binning, el borde izquierdo del contenedor más a la izquierda, si lo desea, fuera del usuario. manos. Si el usuario combina cualquiera de estas funciones para agrupar datos con su propia decisión sobre dónde comienza el binning (como se hace en el blog al que se ha vinculado anteriormente), las funciones anteriores son todas incorrectas.Con un punto de partida arbitrario para hurgar en la basura 'Min', la función correcta es:

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

Usted puede ver por qué esto es correcto secuencialmente (que ayuda a sacar algunas cajas y un punto en alguna parte de uno de ellos). Reste Min de su punto de datos para ver qué tan lejos está dentro del rango de binning. Luego divide por binwidth para que estés trabajando efectivamente en unidades de 'bins'. Luego, 'fondo' el resultado para ir al borde izquierdo de ese contenedor, agregue 0.5 para ir al medio del contenedor, multiplique por el ancho para que ya no trabaje en unidades de contenedores, sino en una escala absoluta nuevamente, luego, vuelva a agregar el desplazamiento Min que resta al inicio.

considerar esta función en acció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 

por ejemplo el valor 1.1 cae realmente en el contenedor izquierdo:

  • esta función lo ubica correctamente en el centro del contenedor izquierdo (0.75);
  • La respuesta de Born2Smile, bin (x) = ancho * piso (x/ancho), lo mapea incorrectamente en 1;
  • respuesta mas90, bin (x) = ancho * piso (x/ancho) + binwidth/2.0, lo mapea incorrectamente en 1.5.

La respuesta de Born2Smile es correcta solo si los límites del contenedor se producen en (n + 0.5) * binwidth (donde n se ejecuta sobre enteros). La respuesta de mas90 solo es correcta si los límites del contenedor se producen en n * binwidth.

+0

+1 para prestar atención a los detalles ... gracias – Abhinav

2

Tengo una pequeña modificación en la solución de Born2Smile.

Sé que no tiene mucho sentido, pero es posible que lo desee por si acaso. Si sus datos son enteros y necesita un tamaño de contenedor flotante (tal vez para comparar con otro conjunto de datos, o densidad de trazado en una cuadrícula más fina), necesitará agregar un número aleatorio entre 0 y 1 dentro del piso. De lo contrario, habrá picos debido al error de redondeo. floor(x/width+0.5) no funcionará porque creará un patrón que no es verdadero para los datos originales.

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

¡Eso no tiene ningún sentido! – Christoph

+1

No ha encontrado tales situaciones, pero puede hacerlo más tarde. Puede probarlo con enteros normalmente distribuidos con una sd flotante y trazar histogramas con bin = 1, y bin = s.d. Vea lo que obtiene con y sin el truco rand (0). Capté un error de un colaborador al revisar su manuscrito. Sus resultados cambiaron de absolutamente tonterías a una bella figura como se esperaba. – path4

+0

Ok, tal vez la explicación es tan breve, que uno no puede entenderlo sin un caso de prueba más concreto. Haré una pequeña edición de tu respuesta para poder deshacer el voto a la baja;) – Christoph

6

Como de costumbre, Gnuplot es una herramienta fantástica para trazar gráficos de mirada dulce y puede ser hecho para realizar todo tipo de cálculos. Sin embargo, está diseñado para trazar datos en lugar de servir como una calculadora y a menudo es más fácil usar un programa externo (por ejemplo, Octave) para hacer cálculos más "complicados", guardar estos datos en un archivo y luego usar Gnuplot para producir el gráfico. Para el problema anterior, echa un vistazo a la función "hist" es Octave usando [freq,bins]=hist(data), a continuación, una de sus parcelas en Gnuplot usando

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

Con respecto a las funciones se van a agrupar, yo no esperaba que el resultado de las funciones ofrecidas hasta ahora. A saber, si mi ancho de banda es 0.001, estas funciones estaban centrando los contenedores en 0.0005 puntos, mientras que creo que es más intuitivo tener los contenedores centrados en los límites de 0.001.

En otras palabras, me gustaría tener

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

La función de agrupación que se me ocurrió es

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

Aquí es un script para comparar algunas de las funciones de basura que se ofrecen a éste :

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 
} 

y aquí está la salida

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