我知道如何在gnuplot中创建直方图(只是使用“带框”),如果我的.dat文件已经有正确的二进制数据。是否有一种方法可以获取数字列表,并让gnuplot根据用户提供的范围和bin大小提供一个直方图?


是的,它快速而简单,但非常隐蔽:

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

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

查看帮助平滑频率,看看为什么上面做了一个直方图

要处理范围,只需设置xrange变量。


我对Born2Smile非常有用的答案有一些更正/补充:

空箱子导致相邻箱子的箱子不正确地延伸到它的空间;使用set boxwidth binwidth避免这种情况 在Born2Smile的版本中,箱子以下界为中心呈现。严格来说,它们应该从下界延伸到上界。这可以通过修改bin函数来纠正:bin(x,width)=width*floor(x/width) + width/2.0


你想画一个像这样的图吗? 是吗?然后你可以看看我的博客文章:http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html

代码中的关键行:

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

我发现这个讨论非常有用,但我也遇到过一些“四舍五入”的问题。

更准确地说,使用0.05的binwidth,我注意到,使用上面介绍的技术,读取0.1和0.15的数据点落在同一个bin中。这(显然是不想要的行为)很可能是由于“地板”功能。

下面是我的小贡献,试图规避这一点。

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

这个递归方法适用于x >=0;我们可以用更多的条件语句来概括它,从而得到更一般的结果。


我们不需要使用递归方法,它可能会很慢。我的解决方案是使用用户定义的函数rint,而不是内部函数int或floor。

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

该函数将给出rint(0.0003/0.0001)=3,而int(0.0003/0.0001)=floor(0.0003/0.0001)=2。

为什么?请参阅Perl int函数和填充零


要非常小心:本页上的所有答案都隐含地决定了装箱从哪里开始——如果你喜欢的话,从最左边的箱子的左边边缘开始——而不是由用户决定。如果用户将这些函数中的任何一个与他/她自己决定的数据分箱开始位置相结合(就像在上面链接的博客上所做的那样),上述函数都是不正确的。对于bin 'Min'的任意起始点,正确的函数是:

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

You can see why this is correct sequentially (it helps to draw a few bins and a point somewhere in one of them). Subtract Min from your data point to see how far into the binning range it is. Then divide by binwidth so that you're effectively working in units of 'bins'. Then 'floor' the result to go to the left-hand edge of that bin, add 0.5 to go to the middle of the bin, multiply by the width so that you're no longer working in units of bins but in an absolute scale again, then finally add back on the Min offset you subtracted at the start.

考虑这个函数:

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

例如,值1.1确实落在左bin中:

这个函数正确地将它映射到左bin的中心(0.75); Born2Smile的答案,bin(x)=width*floor(x/width),错误地将其映射为1; Mas90的答案,bin(x)=width*floor(x/width) + binwidth/2.0,错误地将其映射为1.5。

Born2Smile的答案只有在bin边界出现在(n+0.5)*binwidth (n经过整数)时才正确。Mas90的答案只有在bin边界出现在n*binwidth时才正确。


我对Born2Smile的解决方案做了一些修改。

我知道这不太合理,但以防万一,你可能需要它。如果您的数据是整数,并且您需要一个浮动容器大小(可能是为了与另一组数据进行比较,或在更细的网格中绘制密度),您将需要在floor内添加一个0到1之间的随机数。否则,由于四舍五入误差会出现尖峰。地板(x/width+0.5)是不行的,因为它会创建与原始数据不相符的模式。

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

As usual, Gnuplot is a fantastic tool for plotting sweet looking graphs and it can be made to perform all sorts of calculations. However, it is intended to plot data rather than to serve as a calculator and it is often easier to use an external programme (e.g. Octave) to do the more "complicated" calculations, save this data in a file, then use Gnuplot to produce the graph. For the above problem, check out the "hist" function is Octave using [freq,bins]=hist(data), then plot this in Gnuplot using

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

关于分箱函数,到目前为止,我没有预料到函数提供的结果。也就是说,如果我的binwidth是0.001,那么这些函数将箱子以0.0005点为中心,而我认为将箱子以0.001点为中心更直观。

换句话说,我想拥有

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

我想到的宾宁函数是

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

这里有一个脚本来比较一些提供的bin函数:

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
}

这是输出

   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

同一数据集上不同数量的箱子可以揭示数据的不同特征。

不幸的是,没有通用的最佳方法可以确定箱子的数量。

其中一个强大的方法是Freedman-Diaconis规则,它根据给定数据集的统计数据自动确定箱子的数量,还有许多其他的替代方法。

因此,下面的代码可以在gnuplot脚本中使用Freedman-Diaconis规则:

假设你有一个文件,它只包含一列样本,samplesFile:

# samples
0.12345
1.23232
...

以下(基于ChrisW的回答)可以嵌入到现有的gnuplot脚本中:

...
## preceeding gnuplot commands
...

#
samples="$samplesFile"
stats samples nooutput
N = floor(STATS_records)
samplesMin = STATS_min
samplesMax = STATS_max
# Freedman–Diaconis formula for bin-width size estimation
    lowQuartile = STATS_lo_quartile
    upQuartile = STATS_up_quartile
    IQR = upQuartile - lowQuartile
    width = 2*IQR/(N**(1.0/3.0))
    bin(x) = width*(floor((x-samplesMin)/width)+0.5) + samplesMin

plot \
    samples u (bin(\$1)):(1.0/(N*width)) t "Output" w l lw 1 smooth freq