绘制与gnuplot最佳拟合的曲线

时间:2017-10-06 17:37:42

标签: unix gnuplot average

假设我有一组点x,y来绘制带有gnuplot的图像。它按预期工作,我得到一个很好的曲线。我想重复实验的大型图像数据集(比如1000) 。在这一点上,你会在一个图上得到1000条曲线,每条曲线对应一幅图像。我如何告诉gnuplot绘制曲线的最佳曲线?

我希望gnuplot能够在csv中给出最佳拟合曲线的x,y点,因为我打算稍后会有一个最佳拟合图。

可以找到数据here

2 个答案:

答案 0 :(得分:2)

如果我理解正确,你想要通过数据绘制平均线,而不是拟合数据的功能。您可以使用plot命令的smooth选项执行此操作。

根据您的需要,您可以通过数据绘制插值函数。例如:

plot \
"libjpeg-2000-bench.png.csv" u 3:5 w p, \
"libjpeg-2000-mural.png.csv" u 3:5 w p, \
"libjpeg-2000-red-room.png.csv" u 3:5 w p, \
"libjpeg-bench.png.csv" u 3:5 w p, \
"libjpeg-mural.png.csv" u 3:5 w p, \
"libjpeg-red-room.png.csv" u 3:5 w p, \
 "< tail -q -n +4  libjpeg*csv" u 3:5 smooth acsplines   w l lw 2

给出

enter image description here

您可能想要尝试各种平滑功能,请参阅help smooth。其中一些功能也采用其他参数。例如,您可以为acsplines插值指定权重:

plot \
"libjpeg-2000-bench.png.csv" u 3:5 w p, \
"libjpeg-2000-mural.png.csv" u 3:5 w p, \
"libjpeg-2000-red-room.png.csv" u 3:5 w p, \
"libjpeg-bench.png.csv" u 3:5 w p, \
"libjpeg-mural.png.csv" u 3:5 w p, \
"libjpeg-red-room.png.csv" u 3:5 w p, \
"< tail -q -n +4  libjpeg*csv" u 3:5:(100) smooth acsplines title "acsplines, weight = 100" w l lw 2,  \
"< tail -q -n +4  libjpeg*csv" u 3:5:(0.1) smooth acsplines title "acsplines, weight = 0.1" w l lw 2

enter image description here

权重的选择需要权衡:如果权重很大,那么曲线将更紧密地跟随数据点,但可能会出现振荡。

或者,您可以在x方向上对数据点进行分级,并对属于同一个bin的数据点进行平均。幸运的是,你可以在gnuplot中完成所有这些:

round(x) = floor(x+0.5)
bin(x,binwidth) = binwidth*round(x/binwidth)
binwidth = 1.
plot \
"libjpeg-2000-bench.png.csv" u 3:5 w p, \
"libjpeg-2000-mural.png.csv" u 3:5 w p, \
"libjpeg-2000-red-room.png.csv" u 3:5 w p, \
"libjpeg-bench.png.csv" u 3:5 w p, \
"libjpeg-mural.png.csv" u 3:5 w p, \
"libjpeg-red-room.png.csv" u 3:5 w p, \
 "< tail -q -n +4  libjpeg*csv"  u (bin($3,binwidth)):5 smooth uniq  w l lw 2

给出

enter image description here

您可以在此根据需要调整binsize binwidth

答案 1 :(得分:1)

我必须承认,我并不完全清楚你想要达到什么目标,但我还有一种感觉,正如@KevinBoone在评论中所提到的那样,你正试图对其进行某种分类统计。数据。如果是这种情况,那么遗憾的是Gnuplot不适合执行此任务。在我看来,将这个处理任务委托给更合适的事情会更加实际。

作为一个例子,让我们说战略确实是:

  1. 加载当前目录中的所有csv文件
  2. 将x范围划分为M个区域并计算落入每个区间的y值的平均值
  3. 绘制此“平均”数据
  4. 为此,可以根据需要准备一个简短的Python脚本(实现上述步骤) scipy工具包提供的binned_statistic函数。所需的bin数作为第一个参数传递,而其余的参数被解释为csv文件进行处理:

    #!/usr/bin/env python
    import sys
    
    import numpy as np
    from scipy.stats import binned_statistic
    
    num_of_bins = int(sys.argv[1])
    
    data = []
    for fname in sys.argv[2:]:    
        with open(fname, 'r') as F:
            for line_id, line in enumerate(F):
                if line_id < 3: continue
    
                cols = line.strip().split(',')
                x, y = map(float, [cols[i] for i in [2, 3]])
                data.append((x, y))
    
    data = np.array(data)
    stat, bin_edges, _ = binned_statistic(data[:, 0], data[:, 1], 'mean', bins = num_of_bins, range = None)
    
    for val, (lb, ub) in zip(stat, zip(bin_edges, bin_edges[1:])):
        print('%E,%E' % ( (lb+ub)/2, val ))
    

    现在,在Gnuplot中,我们可以在外部调用此脚本(假设它作为stat.py存储在当前工作目录中)并将其与各个文件一起绘制:

    set terminal pngcairo enhanced
    set output 'fig.png'
    
    #get all csv files in current directory as a space-delimited string
    files = system("ls *.csv | xargs")
    
    #construct a "pretty" label from the file name
    getLabel(fname)=system(sprintf('echo "%s" | gawk -F"-" "BEGIN{OFS=\"-\"} {NF=NF-2;print}"', fname))
    
    set datafile separator ","
    set key spacing 1.5
    
    LINE_WIDTH = 1.25
    plot \
        for [filename in files] filename u 3:4 w l lw LINE_WIDTH t getLabel(filename), \
        sprintf('<python ./stat.py 20 %s', files) w l lw 3*LINE_WIDTH lc rgb 'red' t 'average'
    

    使用您在评论中提供的一些示例数据,这会产生: enter image description here

    然而,正如@KevinBoone指出的那样,这个“平均”在你的具体环境中是否具有合理的数学意义是另一个问题......