Histograma usando gnuplot?

202

Sé cómo crear un histograma (solo 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 basado en rangos y tamaños de bin que proporciona el usuario?

María
fuente
2
Si no obtiene una respuesta, hay otras herramientas que están destinadas a hacer tales cosas. Yo uso Root ( root.cern.ch ) muchos otros por aquí usan R, y hay al menos algunas otras opciones.
dmckee --- ex-gatito moderador
1
Bin es el rango de valores recopilados juntos para cada barra en el histograma. Cada bin tiene un límite inferior y superior, y todos los datos con un valor en ese rango se cuentan para esa barra. Binned significa que mi archivo de datos ya está organizado por la cantidad de puntos de datos dentro de cada bin, por lo que está listo para trazarse como un histograma.
María

Respuestas:

225

sí, y es rápido y simple aunque muy oculto:

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

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

echa un vistazo help smooth freqpara ver por qué lo anterior hace un histograma

para lidiar con rangos solo configure la variable xrange.

Born2Smile
fuente
11
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
2
Tenga mucho cuidado, esto solo funciona si no hay un contenedor "perdido" en el conjunto ... Esta función fija el valor y de un contenedor perdido al valor y del contenedor anterior no perdido. ¡Esto puede ser muy engañoso!
PinkFloyd
1
Yo agregaría set boxwidth binwidtha lo anterior. Fue realmente útil para mí.
Jaakko
90

Tengo un par de correcciones / adiciones a la muy útil respuesta de Born2Smile:

  1. Los contenedores vacíos hicieron que la caja del contenedor adyacente se extendiera incorrectamente a su espacio; evitar esto usandoset boxwidth binwidth
  2. En la versión de Born2Smile, los contenedores se representan como centrados en su límite inferior. Estrictamente, deberían extenderse desde el límite inferior hasta el límite superior. Esto se puede corregir modificando la binfunción:bin(x,width)=width*floor(x/width) + width/2.0
mas90
fuente
10
En realidad esa segunda parte debería ser bin(x,width)=width*floor(x/width) + binwidth/2.0(flotante cálculos de punto)
bgw
8
¿Quiere decir bin(x,width)=width*floor(x/width) + width/2.0. Si lo estamos pasando widthcomo argumento, úselo. :-)
Mitar
78

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

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

Puede ver por qué esto es correcto secuencialmente (ayuda a dibujar algunos contenedores y un punto en algún lugar de uno de ellos). Resta Min de tu punto de datos para ver qué tan lejos está en el rango de binning. Luego divida por ancho de bandeja para que esté trabajando efectivamente en unidades de 'contenedores'. Luego 'piso' el resultado para ir al borde izquierdo de ese contenedor, agregue 0.5 para ir al centro del contenedor, multiplique por el ancho para que ya no trabaje en unidades de contenedores sino en una escala absoluta nuevamente, luego finalmente agregue nuevamente el desplazamiento Mín que resta al principio.

Considere 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 realmente cae en el contenedor izquierdo:

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

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

ChrisW
fuente
48

¿Quieres trazar un gráfico como este? ingrese la descripción de la imagen aquí ¿si? Entonces puedes echar un vistazo al artículo de mi 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
hsxz
fuente
10

Como de costumbre, Gnuplot es una herramienta fantástica para trazar gráficos atractivos y se puede hacer para realizar todo tipo de cálculos. Sin embargo , está destinado a 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 los cálculos más "complicados", guardar estos datos en un archivo y luego usar Gnuplot para producir la gráfica. Para el problema anterior, verifique que la función "hist" esté usando Octave [freq,bins]=hist(data), luego trace esto 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
Dai
fuente
7

Esta discusión me ha resultado extremadamente útil, pero he experimentado algunos problemas de "redondeo".

Más precisamente, usando un ancho de bin 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 el mismo bin. Esto (comportamiento obviamente no deseado) probablemente se deba a la función "piso".

De aquí 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 declaraciones más condicionales para obtener algo aún más general.

Alex
fuente
6

No necesitamos usar un método recursivo, puede ser lento. Mi solución está utilizando una función definida por el usuario rint en lugar de la función intrínseca int o floor.

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

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

¿Por qué? Mire la función Perl int y los ceros de relleno

JOE
fuente
4

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

Sé que eso 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), deberá 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 sea fiel a los datos originales.

binwidth=0.3
bin(x,width)=width*floor(x/width+rand(0))
camino4
fuente
1
No ha encontrado tales situaciones, pero puede que más tarde. Puede probarlo con números enteros distribuidos normalmente con un sd flotante y trazar histogramas con bin = 1 y bin = sd Vea lo que obtiene con y sin el truco rand (0). Capté el error de un colaborador al revisar su manuscrito. Sus resultados cambiaron de absolutamente absurdos a una hermosa figura como se esperaba.
ruta4
Ok, tal vez la explicación es tan corta que no se puede entender sin un caso de prueba más concreto. Haré una pequeña edición de su respuesta para poder deshacer el voto negativo;)
Christoph
Considere enteros de distribución normal. Como son enteros, muchos de ellos tendrán el mismo x / ancho. Digamos que el número es 1.3. Con el piso (x / ancho + 0.5), todos serán asignados al bin 1. Pero lo que 1.3 realmente significa en términos de densidad es que el 70% de ellos debería estar en el bin 1 y el 30% en el bin 2. rand (0 ) mantiene la densidad adecuada. Entonces, 0.5 crea picos y rand (0) lo mantiene verdadero. Apuesto a que la cifra de hsxz será mucho más uniforme usando rand (0) en lugar de 0.5. No es solo redondear, sino redondear sin perturbación.
ruta4
3

Con respecto a las funciones de binning, no esperaba el resultado de las funciones ofrecidas hasta ahora. Es decir, si mi ancho de bandeja es 0.001, estas funciones centran las bandejas en 0.0005 puntos, mientras que siento que es más intuitivo tener las bandejas centradas 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 binning que se me ocurrió es

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

Aquí hay un script para comparar algunas de las funciones bin ofrecidas con esta:

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
Winston Smith
fuente