【问题标题】:Draw a curve of best fit with gnuplot用gnuplot画一条最合适的曲线
【发布时间】:2017-10-06 17:37:42
【问题描述】:

假设我有一组点x,y 用 gnuplot 绘制图像。它按预期工作并且我得到了一条漂亮的曲线。我想对大型图像数据集(比如 1000)重复实验。在此时,您将在一个绘图上获得 1000 条曲线,每条曲线用于一张图像。我如何告诉 gnuplot 绘制曲线的最佳拟合?

我希望 gnuplot 为我提供 csv 中最佳拟合曲线的 x,y 点,因为我计划稍后再绘制一个最佳拟合图。

数据可以查到here

【问题讨论】:

  • 拟合的数学模型是什么?如果您让 gnuplot 通过一组点绘制曲线,那么我的回忆是它没有找到点之间的任何数学关系,而只是使用三次样条或其他简单函数来逐点拟合曲线点基础。您可以以某种方式“平均”这些曲线,但在任何严格的数学意义上它都不是“最佳拟合”。为了获得最佳拟合,您必须有一个数学模型,您可以将数据作为衡量标准。您的数据是否以某种数学方式相关?
  • @KevinBoone 和 fit 您可以为您的数据拟合任何函数。
  • @KevinBoone 那么我如何对曲线进行良好的平均。我没有数学函数。
  • 不看数据很难说。一种通用的方法是将 x 范围划分为小块(所需的数量将取决于数据点的数量),然后对落入每个 x 区域的所有数据点的 y 值取平均值。然后使用 x 区域的中心和相应的 y 平均值作为数据点绘制一条线。老实说,我不记得 gnuplot 是否可以自动进行此计算。如果您只是“目测”数据,这种方法会给出与您绘制相同类型的线;但是,它根本没有数学上的有效性。
  • @KevinBoone 数据和图可以在@encode.ru/threads/…找到

标签: unix gnuplot average


【解决方案1】:

如果我对您的理解正确,您希望通过数据绘制一条平均线,而不是拟合函数的数据。您可以使用 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

给予

您可能想尝试各种平滑函数,请参阅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

权重的选择涉及权衡:如果权重较大,则曲线将更接近数据点,但可能会出现波动。

或者,您可以在 x 方向对数据点进行分箱,并对属于同一分箱的数据点进行平均。幸运的是,您可以在 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

给予

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

【讨论】:

  • 你能给我一些关于这种数据类型的 acsplines 使用什么权重的见解
  • 没有“正确”的方法来选择权重,这实际上取决于您的数据以及您希望插值函数的平滑程度。我添加了一个示例来说明更改权重的效果。
  • @user8153....实际上我认为曲线不是更合适的。如果我想要这样的合并图,你有什么建议i.gyazo.com/d0cdc7c7df927d5ef329f26c2f95703a.png
【解决方案2】:

我不得不承认,我并不完全清楚你到底想要达到什么目标,但我也有一种感觉,正如@KevinBoone 在 cmets 中提到的那样,你正试图对数据。如果是这种情况,那么很遗憾 Gnuplot 不是完成这项任务的合适工具。在我看来,将这个处理任务委托给更合适的人会更实际。

例如,假设策略确实是:

  1. 加载当前目录下的所有csv文件
  2. 将 x 范围划分为 M 个 bin 并计算落入每个 bin 的 y 值的平均值
  3. 绘制这个“平均”数据

为此,可以根据 binned_statistic scipy 工具包提供的函数。所需数量的 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'

使用您在 cmets 中提供的一些示例数据,这会产生:

但是,正如@KevinBoone 所指出的那样,这个“平均值”在您的特定设置中是否具有合理的数学含义本身就是另一个问题......

【讨论】:

  • 感谢您的帮助。但是它比这更简单。想象一下你有一个只有 libjpeg 曲线的图。假设我对 1k 图像进行测试,所以我将在该图上获得 1k libjpeg 曲线。我想要的是一条“平均”曲线而不是 1k 曲线
  • @user2650277 然后您可以将“平均脚本”仅应用于这些文件,其余的将保持不变...
猜你喜欢
  • 2023-03-14
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 2011-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多