这是可以做到的。它将广泛使用 stats 命令和一个临时文件。在 gnuplot 5 中,可以使用命名数据块在内存中创建临时文件(请参阅 参考资料help datablocks
)。
此外,由于您的绘图命令在很大程度上是重复的,您可以使用绘图作为语法
plot for[in=0:2] 'file' i in u 1:2 w lines t columnheader(1)
这将使用变量 in 的值 0 到 2 重复绘图命令(您提供的命令使用四个数据块,但您提供的数据文件只有 3 个)。
以下脚本将完成您想要的:
stats 'file' u 1:2 nooutput
blocks = STATS_blocks
set print 'tempfile'
first_y = ""
first_x = ""
do for[i=0:blocks-1] {
stats 'file' index i u (first_x=($0==1)?sprintf("%s %f",first_x,$1):first_x,first_y=($0==1)?sprintf("%s %f",first_y,$2):first_y,$1):2 nooutput
print sprintf("%f %f",STATS_pos_max_y,STATS_max_y)
}
print ""
print ""
do for[i=1:blocks] {
print sprintf("%s %s",word(first_x,i),word(first_y,i))
}
set print
plot for[i=0:blocks-1] 'file' i i u 1:2 w lines title columnheader(1),\
for[i=0:1] 'tempfile' i i u 1:2:($0+1) w points pt (i==0?7:9) lc variable not
这会产生(使用您提供的数据文件)

在曲线 0 和 2 的情况下,第一个点和最大值点相同,因此符号被遮挡。
重新绘制此图,但更改规范以将第一个点标记向上移动 0.1,我们可以看到它们出现在应有的位置。

这部分会很长,但我会分解代码并详细解释,尽可能接近逐行,因为这里有一些微妙的东西。
前两行
stats 'file' u 1:2 nooutput
blocks = STATS_blocks
对文件运行 stats 命令。由于命名的列标题,如果我们不指定使用规范,统计功能将失败,所以我们给它u 1:2
规范。该nooutput
选项告诉 stats 命令捕获结果,但不输出它们。这里我们只关心获取块的数量。我们将其存储在变量块中(因为稍后的 stats 命令将覆盖该变量)。我们本可以给定一个命名前缀,但这会保存所有变量,而且没有理由这样做。代替这两个命令,在正好 3 个块的情况下,我们可以将值 3 替换为下面所有出现的块,但是这样块的数量不是硬编码的。
接下来,我们使用set print 'tempfile'
将打印命令重定向到临时文件。我们将建立一个包含最大点和第一个点的新数据文件。
下一段代码
first_y = ""
first_x = ""
do for[i=0:blocks-1] {
stats 'file' index i u (first_x=($0==1)?sprintf("%s %f",first_x,$1):first_x,first_y=($0==1)?sprintf("%s %f",first_y,$2):first_y,$1):2 nooutput
print sprintf("%f %f",STATS_pos_max_y,STATS_max_y)
}
是最困难的,也是最神奇的地方。我们将创建我们的临时文件以包含两个数据块。第一个是最大值,第二个是第一个值。我们将计算内存中的第一个点并在创建第一个数据块后添加它们。x 坐标和 y 坐标将存储在空格分隔的字符串变量中。
我们遍历所有数据块并为其计算统计命令。表达方式
(first_x=($0==1)?sprintf("%s %f",first_x,$1):first_x,first_y=($0==1)?sprintf("%s %f",first_y,$2):first_y,$1)
为读入的每个点重新分配两个字符串变量。为此,它首先检查该点是否是系列中的第一个($0 的值将是 1,因为 0 值对应于标题行)。如果是,它通过将第一列的值添加到它来重建字符串变量(对于 y 坐标也是如此)。否则,它只是将相同的东西重新分配给变量。最后,它返回第一列中的值。像这样将表达式放在括号中并用逗号分隔时,将依次计算每个表达式,并返回最终值。
因此 stats 命令的行为就像是
stats 'file' index i u 1:2 nooutput
但是这个小技巧可以让我们读取第一行的值并在它们进入时存储它们。最后打印出具有最大 y 值的点。这将进入临时文件。
现在我们需要将第一个点作为新数据块添加到临时文件中。所以首先我们打印两个空行,然后我们再次迭代运行的块数
print sprintf("%s %s",word(first_x,i),word(first_y,i))
对于每个块(其中 i 是块的编号)。word 函数将字符串变量视为以空格分隔的单词列表,并提取所请求的单词。此时我们的字符串变量看起来像
0.000000 0.000000 8.000000 # first_x
0.780000 0.876000 2.230500 # first_y
最后,我们发出set print
恢复打印命令以打印到控制台。我们现在已经建立了一个临时文件,看起来像
0.000000 0.780000
16.000000 0.950000
8.000000 2.230500
0.000000 0.780000
0.000000 0.876000
8.000000 2.230500
其中第一个数据块是具有最大 y 值的点,第二个数据块是第一个点。
最后,我们绘制
plot for[i=0:blocks-1] 'file' i i u 1:2 w lines title columnheader(1),\
for[i=0:1] 'tempfile' i i u 1:2:($0+1) w points pt (i==0?7:9) lc variable not
第一部分与之前相同,只是使用了 blocks 变量而不是硬编码块的数量。
接下来,我们使用索引 0 和索引 1 绘制临时文件两次。线条颜色根据行号(在本例中为 0 到 2)而变化。我们加一以强制通常基于 0 的行号为 1 到 3。这将与之前的数据块相对应。我们用点绘制并根据我们正在绘制的数据块选择点类型。在这种情况下,它是一个实心圆(对于最大值)或实心三角形(对于第一个点)。